blob: c11e247ee403ea838dddb3d0a88ef7268b350f44 [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)
wu90da17b2025-06-19 12:45:29 +080016 // 步骤:新帖先上传,编辑则直接到 detail
17 const [step, setStep] = useState(isEdit ? 'detail' : 'upload')
wu90da17b2025-06-19 12:45:29 +080018 const [mediaUrls, setMediaUrls] = useState([])
TRM-codingd1cbf672025-06-18 15:15:08 +080019
wu90da17b2025-06-19 12:45:29 +080020 // 表单字段
TRM-codingd1cbf672025-06-18 15:15:08 +080021 const [title, setTitle] = useState('')
22 const [content, setContent] = useState('')
23 const [topicId, setTopicId] = useState('')
24 const [status, setStatus] = useState('published')
25
26 const [error, setError] = useState(null)
wu90da17b2025-06-19 12:45:29 +080027 const [loading, setLoading] = useState(isEdit)
TRM-codingd1cbf672025-06-18 15:15:08 +080028
wu90da17b2025-06-19 12:45:29 +080029 // 静态话题
TRM-codingd1cbf672025-06-18 15:15:08 +080030 const TOPICS = [
31 { id: 1, name: '世俱杯环球评大会' },
32 { id: 2, name: '我的REDmentor' },
33 { id: 3, name: '我染上了拼豆' },
TRM-codingd1cbf672025-06-18 15:15:08 +080034 ]
35
wu2f28f672025-06-19 14:29:30 +080036 // 获取当前登录用户id
37 const user = getUserInfo()
38 const currentUserId = user?.id
39
wu90da17b2025-06-19 12:45:29 +080040 // 编辑模式:拉取原帖数据填入
41 useEffect(() => {
42 if (!isEdit) return
43 fetchPostDetail(postId)
44 .then(data => {
45 setTitle(data.title)
46 setContent(data.content)
47 setTopicId(data.topic_id || '')
48 setStatus(data.status)
49 setMediaUrls(data.media_urls || [])
50 })
51 .catch(err => setError(err.message))
52 .finally(() => setLoading(false))
53 }, [isEdit, postId])
wu90da17b2025-06-19 12:45:29 +080054 // 上传回调
TRM-codingd1cbf672025-06-18 15:15:08 +080055 const handleUploadComplete = async uploadedFiles => {
wu90da17b2025-06-19 12:45:29 +080056 // TODO: 真正上传到服务器后替换为服务端 URL
TRM-codingd1cbf672025-06-18 15:15:08 +080057 const urls = await Promise.all(
58 uploadedFiles.map(f => URL.createObjectURL(f))
59 )
60 setMediaUrls(urls)
TRM-codingd1cbf672025-06-18 15:15:08 +080061 setStep('detail')
62 }
63
wu90da17b2025-06-19 12:45:29 +080064 // 提交(创建/更新)
TRM-codingd1cbf672025-06-18 15:15:08 +080065 const handleSubmit = async () => {
66 if (!title.trim() || !content.trim()) {
67 setError('标题和正文必填')
68 return
69 }
wu2f28f672025-06-19 14:29:30 +080070 if (!currentUserId) {
71 setError('未获取到用户ID,请重新登录')
72 return
73 }
TRM-codingd1cbf672025-06-18 15:15:08 +080074 setError(null)
75 try {
wu90da17b2025-06-19 12:45:29 +080076 if (isEdit) {
77 await updatePost(postId, {
78 title: title.trim(),
79 content: content.trim(),
80 topic_id: topicId || undefined,
81 media_urls: mediaUrls,
82 status
83 })
84 alert('更新成功!')
85 } else {
86 await createPost({
wu2f28f672025-06-19 14:29:30 +080087 user_id: currentUserId,
wu90da17b2025-06-19 12:45:29 +080088 topic_id: topicId || undefined,
89 title: title.trim(),
90 content: content.trim(),
91 media_urls: mediaUrls,
92 status
93 })
94 alert('发布成功!')
95 }
96 navigate('/notebooks', { replace: true })
TRM-codingd1cbf672025-06-18 15:15:08 +080097 } catch (e) {
98 setError(e.message)
99 }
100 }
101
wu90da17b2025-06-19 12:45:29 +0800102 if (loading) return <p>加载中…</p>
103 if (step === 'upload' && !isEdit) {
TRM-codingd1cbf672025-06-18 15:15:08 +0800104 return <UploadPage onComplete={handleUploadComplete} />
105 }
106
TRM-codingd1cbf672025-06-18 15:15:08 +0800107 return (
108 <div className="create-post">
wu90da17b2025-06-19 12:45:29 +0800109 <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2>
TRM-codingd1cbf672025-06-18 15:15:08 +0800110 {error && <div className="error">{error}</div>}
111
wu90da17b2025-06-19 12:45:29 +0800112 {/* 媒体预览 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800113 <div className="preview-media">
114 {mediaUrls.map((url, i) => (
115 <div key={i} className="preview-item">
wu90da17b2025-06-19 12:45:29 +0800116 {url.match(/\.(mp4|mov|avi)$/) ? (
TRM-codingd1cbf672025-06-18 15:15:08 +0800117 <video src={url} controls />
wu90da17b2025-06-19 12:45:29 +0800118 ) : (
119 <img src={url} alt={`预览 ${i}`} />
TRM-codingd1cbf672025-06-18 15:15:08 +0800120 )}
121 </div>
122 ))}
123 </div>
124
125 {/* 标题 */}
126 <label className="form-label">
127 标题(最多20字)
128 <input
129 type="text"
130 maxLength={20}
131 value={title}
132 onChange={e => setTitle(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800133 />
134 <span className="char-count">{title.length}/20</span>
135 </label>
136
137 {/* 正文 */}
138 <label className="form-label">
139 正文(最多1000字)
140 <textarea
141 maxLength={1000}
142 value={content}
143 onChange={e => setContent(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800144 />
145 <span className="char-count">{content.length}/1000</span>
146 </label>
147
148 {/* 话题选择 */}
149 <label className="form-label">
150 选择话题(可选)
151 <select
152 value={topicId}
153 onChange={e => setTopicId(e.target.value)}
154 >
155 <option value="">不添加话题</option>
156 {TOPICS.map(t => (
157 <option key={t.id} value={t.id}>
158 #{t.name}
159 </option>
160 ))}
161 </select>
162 </label>
163
164 {/* 发布状态 */}
165 <div className="status-group">
166 <label>
167 <input
168 type="radio"
169 name="status"
170 value="published"
171 checked={status === 'published'}
172 onChange={() => setStatus('published')}
173 />
174 立即发布
175 </label>
176 <label>
177 <input
178 type="radio"
179 name="status"
180 value="draft"
181 checked={status === 'draft'}
182 onChange={() => setStatus('draft')}
183 />
184 存为草稿
185 </label>
186 </div>
187
wu90da17b2025-06-19 12:45:29 +0800188 {/* 按钮 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800189 <div className="btn-group">
190 <button className="btn btn-primary" onClick={handleSubmit}>
wu90da17b2025-06-19 12:45:29 +0800191 {isEdit ? '更新' : '发布'}
TRM-codingd1cbf672025-06-18 15:15:08 +0800192 </button>
wu90da17b2025-06-19 12:45:29 +0800193 {!isEdit && (
194 <button className="btn btn-secondary" onClick={() => setStep('upload')}>
195 上一步
196 </button>
197 )}
TRM-codingd1cbf672025-06-18 15:15:08 +0800198 </div>
199 </div>
200 )
201}