新增管理帖子的组件
Change-Id: I7299ad6f735064dd112f92bab7c87daa952e6dc2
diff --git a/Merge/front/src/components/CreatePost.jsx b/Merge/front/src/components/CreatePost.jsx
index 7519d5b..9817ac0 100644
--- a/Merge/front/src/components/CreatePost.jsx
+++ b/Merge/front/src/components/CreatePost.jsx
@@ -1,48 +1,68 @@
// src/components/CreatePost.jsx
-import React, { useState } from 'react'
-import { useNavigate } from 'react-router-dom'
+import React, { useState, useEffect } from 'react'
+import { useNavigate, useParams } from 'react-router-dom'
import UploadPage from './UploadPage'
-import { createPost } from '../api/posts_wzy'
+import {
+ createPost,
+ updatePost,
+ fetchPost as fetchPostDetail
+} from '../api/posts_wzy'
import '../style/CreatePost.css'
export default function CreatePost() {
const navigate = useNavigate()
+ const { postId } = useParams()
+ const isEdit = Boolean(postId)
- const [step, setStep] = useState('upload') // 'upload' | 'detail'
- const [files, setFiles] = useState([]) // 本地 File 对象列表
- const [mediaUrls, setMediaUrls] = useState([]) // 上传后得到的 URL 列表
+ // 步骤:新帖先上传,编辑则直接到 detail
+ const [step, setStep] = useState(isEdit ? 'detail' : 'upload')
+ const [files, setFiles] = useState([])
+ const [mediaUrls, setMediaUrls] = useState([])
- // 详情表单字段
+ // 表单字段
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const [topicId, setTopicId] = useState('')
const [status, setStatus] = useState('published')
const [error, setError] = useState(null)
+ const [loading, setLoading] = useState(isEdit)
- // 静态话题数据
+ // 静态话题
const TOPICS = [
{ id: 1, name: '世俱杯环球评大会' },
{ id: 2, name: '我的REDmentor' },
{ id: 3, name: '我染上了拼豆' },
- // …更多静态话题…
]
- // 上传页面回调 —— 上传完成后切换到“填写详情”步骤
+ // 编辑模式:拉取原帖数据填入
+ useEffect(() => {
+ if (!isEdit) return
+ fetchPostDetail(postId)
+ .then(data => {
+ setTitle(data.title)
+ setContent(data.content)
+ setTopicId(data.topic_id || '')
+ setStatus(data.status)
+ setMediaUrls(data.media_urls || [])
+ })
+ .catch(err => setError(err.message))
+ .finally(() => setLoading(false))
+ }, [isEdit, postId])
+
+ // 上传回调
const handleUploadComplete = async uploadedFiles => {
setFiles(uploadedFiles)
-
- // TODO: 改成真实上传逻辑,拿到真正的 media_urls
+ // TODO: 真正上传到服务器后替换为服务端 URL
const urls = await Promise.all(
uploadedFiles.map(f => URL.createObjectURL(f))
)
setMediaUrls(urls)
-
setStep('detail')
}
- // 发布按钮
+ // 提交(创建/更新)
const handleSubmit = async () => {
if (!title.trim() || !content.trim()) {
setError('标题和正文必填')
@@ -50,40 +70,50 @@
}
setError(null)
try {
- await createPost({
- user_id: 1,
- topic_id: topicId || undefined,
- title: title.trim(),
- content: content.trim(),
- media_urls: mediaUrls,
- status
- })
- // 发布成功后跳转回首页
- navigate('/home', { replace: true })
+ if (isEdit) {
+ await updatePost(postId, {
+ title: title.trim(),
+ content: content.trim(),
+ topic_id: topicId || undefined,
+ media_urls: mediaUrls,
+ status
+ })
+ alert('更新成功!')
+ } else {
+ await createPost({
+ user_id: 1,
+ topic_id: topicId || undefined,
+ title: title.trim(),
+ content: content.trim(),
+ media_urls: mediaUrls,
+ status
+ })
+ alert('发布成功!')
+ }
+ navigate('/notebooks', { replace: true })
} catch (e) {
setError(e.message)
}
}
- // 渲染上传页
- if (step === 'upload') {
+ if (loading) return <p>加载中…</p>
+ if (step === 'upload' && !isEdit) {
return <UploadPage onComplete={handleUploadComplete} />
}
- // 渲染详情页
return (
<div className="create-post">
- <h2>填写帖子内容</h2>
+ <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2>
{error && <div className="error">{error}</div>}
- {/* 已上传媒体预览 */}
+ {/* 媒体预览 */}
<div className="preview-media">
{mediaUrls.map((url, i) => (
<div key={i} className="preview-item">
- {files[i].type.startsWith('image/') ? (
- <img src={url} alt={`预览 ${i}`} />
- ) : (
+ {url.match(/\.(mp4|mov|avi)$/) ? (
<video src={url} controls />
+ ) : (
+ <img src={url} alt={`预览 ${i}`} />
)}
</div>
))}
@@ -97,7 +127,6 @@
maxLength={20}
value={title}
onChange={e => setTitle(e.target.value)}
- placeholder="填写标题会有更多赞哦~"
/>
<span className="char-count">{title.length}/20</span>
</label>
@@ -109,7 +138,6 @@
maxLength={1000}
value={content}
onChange={e => setContent(e.target.value)}
- placeholder="输入正文描述,真诚有价值的分享予人温暖"
/>
<span className="char-count">{content.length}/1000</span>
</label>
@@ -154,14 +182,16 @@
</label>
</div>
- {/* 操作按钮 */}
+ {/* 按钮 */}
<div className="btn-group">
<button className="btn btn-primary" onClick={handleSubmit}>
- 发布
+ {isEdit ? '更新' : '发布'}
</button>
- <button className="btn btn-secondary" onClick={() => setStep('upload')}>
- 上一步
- </button>
+ {!isEdit && (
+ <button className="btn btn-secondary" onClick={() => setStep('upload')}>
+ 上一步
+ </button>
+ )}
</div>
</div>
)
diff --git a/Merge/front/src/components/NotebookPage.jsx b/Merge/front/src/components/NotebookPage.jsx
new file mode 100644
index 0000000..25264ec
--- /dev/null
+++ b/Merge/front/src/components/NotebookPage.jsx
@@ -0,0 +1,85 @@
+// src/components/NotebookPage.jsx
+
+import React, { useState, useEffect } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { fetchPosts, deletePost } from '../api/posts_wzy'
+import '../style/NotebookPage.css'
+
+export default function NotebookPage() {
+ const navigate = useNavigate()
+ const [posts, setPosts] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
+
+ // TODO: 替换成真实用户 ID
+ const currentUserId = 2
+
+ useEffect(() => {
+ async function load() {
+ try {
+ // GET /posts?user_id=1
+ const list = await fetchPosts(currentUserId)
+ setPosts(list)
+ } catch (e) {
+ setError(e.message)
+ } finally {
+ setLoading(false)
+ }
+ }
+ load()
+ }, [])
+
+ async function handleDelete(id) {
+ if (!window.confirm('确定要删除该帖子吗?')) return
+ try {
+ await deletePost(id)
+ setPosts(posts.filter(p => p.id !== id))
+ alert('删除成功')
+ } catch (e) {
+ alert('删除失败:' + e.message)
+ }
+ }
+
+ function handleEdit(id) {
+ // 假设你在路由里挂载了 /posts/edit/:postId
+ navigate(`/posts/edit/${id}`)
+ }
+
+ if (loading) return <p>加载中…</p>
+ if (error) return <p className="error">加载失败:{error}</p>
+
+ return (
+ <div className="notebook-page">
+ <h2>我的帖子管理</h2>
+ {posts.length === 0 ? (
+ <p>暂无帖子</p>
+ ) : (
+ <table className="post-table">
+ <thead>
+ <tr>
+ <th>ID</th>
+ <th>标题</th>
+ <th>状态</th>
+ <th>创建时间</th>
+ <th>操作</th>
+ </tr>
+ </thead>
+ <tbody>
+ {posts.map(p => (
+ <tr key={p.id}>
+ <td>{p.id}</td>
+ <td>{p.title}</td>
+ <td>{p.status}</td>
+ <td>{new Date(p.created_at).toLocaleString()}</td>
+ <td>
+ <button onClick={() => handleEdit(p.id)}>编辑</button>
+ <button onClick={() => handleDelete(p.id)}>删除</button>
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ )}
+ </div>
+ )
+}