blob: 1d2f306984ffa7142d9eee472b68b2a9d16132f2 [file] [log] [blame]
wu90da17b2025-06-19 12:45:29 +08001import React, { useState, useEffect } from 'react'
2import { useNavigate, useParams } from 'react-router-dom'
TRM-codingd1cbf672025-06-18 15:15:08 +08003import UploadPage from './UploadPage'
wu90da17b2025-06-19 12:45:29 +08004import {
5 createPost,
6 updatePost,
7 fetchPost as fetchPostDetail
8} from '../api/posts_wzy'
wu2f28f672025-06-19 14:29:30 +08009import { getUserInfo } from '../utils/auth'
TRM-codingd1cbf672025-06-18 15:15:08 +080010import '../style/CreatePost.css'
11
12export default function CreatePost() {
13 const navigate = useNavigate()
wu90da17b2025-06-19 12:45:29 +080014 const { postId } = useParams()
15 const isEdit = Boolean(postId)
TRM-codingd1cbf672025-06-18 15:15:08 +080016
wu90da17b2025-06-19 12:45:29 +080017 // 步骤:新帖先上传,编辑则直接到 detail
18 const [step, setStep] = useState(isEdit ? 'detail' : 'upload')
19 const [files, setFiles] = useState([])
20 const [mediaUrls, setMediaUrls] = useState([])
TRM-codingd1cbf672025-06-18 15:15:08 +080021
wu90da17b2025-06-19 12:45:29 +080022 // 表单字段
TRM-codingd1cbf672025-06-18 15:15:08 +080023 const [title, setTitle] = useState('')
24 const [content, setContent] = useState('')
25 const [topicId, setTopicId] = useState('')
26 const [status, setStatus] = useState('published')
27
28 const [error, setError] = useState(null)
wu90da17b2025-06-19 12:45:29 +080029 const [loading, setLoading] = useState(isEdit)
TRM-codingd1cbf672025-06-18 15:15:08 +080030
wu90da17b2025-06-19 12:45:29 +080031 // 静态话题
TRM-codingd1cbf672025-06-18 15:15:08 +080032 const TOPICS = [
33 { id: 1, name: '世俱杯环球评大会' },
34 { id: 2, name: '我的REDmentor' },
35 { id: 3, name: '我染上了拼豆' },
TRM-codingd1cbf672025-06-18 15:15:08 +080036 ]
37
wu2f28f672025-06-19 14:29:30 +080038 // 获取当前登录用户id
39 const user = getUserInfo()
40 const currentUserId = user?.id
41
wu90da17b2025-06-19 12:45:29 +080042 // 编辑模式:拉取原帖数据填入
43 useEffect(() => {
44 if (!isEdit) return
45 fetchPostDetail(postId)
46 .then(data => {
47 setTitle(data.title)
48 setContent(data.content)
49 setTopicId(data.topic_id || '')
50 setStatus(data.status)
51 setMediaUrls(data.media_urls || [])
52 })
53 .catch(err => setError(err.message))
54 .finally(() => setLoading(false))
55 }, [isEdit, postId])
56
57 // 上传回调
TRM-codingd1cbf672025-06-18 15:15:08 +080058 const handleUploadComplete = async uploadedFiles => {
59 setFiles(uploadedFiles)
wu90da17b2025-06-19 12:45:29 +080060 // TODO: 真正上传到服务器后替换为服务端 URL
TRM-codingd1cbf672025-06-18 15:15:08 +080061 const urls = await Promise.all(
62 uploadedFiles.map(f => URL.createObjectURL(f))
63 )
64 setMediaUrls(urls)
TRM-codingd1cbf672025-06-18 15:15:08 +080065 setStep('detail')
66 }
67
wu90da17b2025-06-19 12:45:29 +080068 // 提交(创建/更新)
TRM-codingd1cbf672025-06-18 15:15:08 +080069 const handleSubmit = async () => {
70 if (!title.trim() || !content.trim()) {
71 setError('标题和正文必填')
72 return
73 }
wu2f28f672025-06-19 14:29:30 +080074 if (!currentUserId) {
75 setError('未获取到用户ID,请重新登录')
76 return
77 }
TRM-codingd1cbf672025-06-18 15:15:08 +080078 setError(null)
79 try {
wu90da17b2025-06-19 12:45:29 +080080 if (isEdit) {
81 await updatePost(postId, {
82 title: title.trim(),
83 content: content.trim(),
84 topic_id: topicId || undefined,
85 media_urls: mediaUrls,
86 status
87 })
88 alert('更新成功!')
89 } else {
90 await createPost({
wu2f28f672025-06-19 14:29:30 +080091 user_id: currentUserId,
wu90da17b2025-06-19 12:45:29 +080092 topic_id: topicId || undefined,
93 title: title.trim(),
94 content: content.trim(),
95 media_urls: mediaUrls,
96 status
97 })
98 alert('发布成功!')
99 }
100 navigate('/notebooks', { replace: true })
TRM-codingd1cbf672025-06-18 15:15:08 +0800101 } catch (e) {
102 setError(e.message)
103 }
104 }
105
wu90da17b2025-06-19 12:45:29 +0800106 if (loading) return <p>加载中…</p>
107 if (step === 'upload' && !isEdit) {
TRM-codingd1cbf672025-06-18 15:15:08 +0800108 return <UploadPage onComplete={handleUploadComplete} />
109 }
110
TRM-codingd1cbf672025-06-18 15:15:08 +0800111 return (
112 <div className="create-post">
wu90da17b2025-06-19 12:45:29 +0800113 <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2>
TRM-codingd1cbf672025-06-18 15:15:08 +0800114 {error && <div className="error">{error}</div>}
115
wu90da17b2025-06-19 12:45:29 +0800116 {/* 媒体预览 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800117 <div className="preview-media">
118 {mediaUrls.map((url, i) => (
119 <div key={i} className="preview-item">
wu90da17b2025-06-19 12:45:29 +0800120 {url.match(/\.(mp4|mov|avi)$/) ? (
TRM-codingd1cbf672025-06-18 15:15:08 +0800121 <video src={url} controls />
wu90da17b2025-06-19 12:45:29 +0800122 ) : (
123 <img src={url} alt={`预览 ${i}`} />
TRM-codingd1cbf672025-06-18 15:15:08 +0800124 )}
125 </div>
126 ))}
127 </div>
128
129 {/* 标题 */}
130 <label className="form-label">
131 标题(最多20字)
132 <input
133 type="text"
134 maxLength={20}
135 value={title}
136 onChange={e => setTitle(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800137 />
138 <span className="char-count">{title.length}/20</span>
139 </label>
140
141 {/* 正文 */}
142 <label className="form-label">
143 正文(最多1000字)
144 <textarea
145 maxLength={1000}
146 value={content}
147 onChange={e => setContent(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800148 />
149 <span className="char-count">{content.length}/1000</span>
150 </label>
151
152 {/* 话题选择 */}
153 <label className="form-label">
154 选择话题(可选)
155 <select
156 value={topicId}
157 onChange={e => setTopicId(e.target.value)}
158 >
159 <option value="">不添加话题</option>
160 {TOPICS.map(t => (
161 <option key={t.id} value={t.id}>
162 #{t.name}
163 </option>
164 ))}
165 </select>
166 </label>
167
168 {/* 发布状态 */}
169 <div className="status-group">
170 <label>
171 <input
172 type="radio"
173 name="status"
174 value="published"
175 checked={status === 'published'}
176 onChange={() => setStatus('published')}
177 />
178 立即发布
179 </label>
180 <label>
181 <input
182 type="radio"
183 name="status"
184 value="draft"
185 checked={status === 'draft'}
186 onChange={() => setStatus('draft')}
187 />
188 存为草稿
189 </label>
190 </div>
191
wu90da17b2025-06-19 12:45:29 +0800192 {/* 按钮 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800193 <div className="btn-group">
194 <button className="btn btn-primary" onClick={handleSubmit}>
wu90da17b2025-06-19 12:45:29 +0800195 {isEdit ? '更新' : '发布'}
TRM-codingd1cbf672025-06-18 15:15:08 +0800196 </button>
wu90da17b2025-06-19 12:45:29 +0800197 {!isEdit && (
198 <button className="btn btn-secondary" onClick={() => setStep('upload')}>
199 上一步
200 </button>
201 )}
TRM-codingd1cbf672025-06-18 15:15:08 +0800202 </div>
203 </div>
204 )
205}