blob: 2b365db15fa3b429ba5ea5009eabc86d9010ac83 [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])
TRM-codingf55d2372025-06-20 16:22:37 +080054
55 // 上传回调 - 保存原始文件对象
TRM-codingd1cbf672025-06-18 15:15:08 +080056 const handleUploadComplete = async uploadedFiles => {
TRM-codingf55d2372025-06-20 16:22:37 +080057 setFiles(uploadedFiles)
58 // 为预览创建临时URLs
59 const urls = uploadedFiles.map(f => URL.createObjectURL(f))
TRM-codingd1cbf672025-06-18 15:15:08 +080060 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)
TRM-codingf55d2372025-06-20 16:22:37 +080075
TRM-codingd1cbf672025-06-18 15:15:08 +080076 try {
TRM-codingf55d2372025-06-20 16:22:37 +080077 // 创建FormData对象
78 const formData = new FormData()
79
80 // 添加文本字段
81 formData.append('user_id', currentUserId)
82 formData.append('title', title.trim())
83 formData.append('content', content.trim())
84 formData.append('status', status)
85 if (topicId) {
86 formData.append('topic_id', topicId)
87 }
88
wu90da17b2025-06-19 12:45:29 +080089 if (isEdit) {
TRM-codingf55d2372025-06-20 16:22:37 +080090 // 编辑模式:如果有新文件,添加新文件;否则保留现有URLs
91 if (files.length > 0) {
92 files.forEach((file, index) => {
93 formData.append(`media_${index}`, file)
94 })
95 formData.append('media_count', files.length)
96 } else {
97 // 保留现有的media_urls
98 formData.append('existing_media_urls', JSON.stringify(mediaUrls))
99 }
100
101 await updatePost(postId, formData)
wu90da17b2025-06-19 12:45:29 +0800102 alert('更新成功!')
103 } else {
TRM-codingf55d2372025-06-20 16:22:37 +0800104 // 创建模式:添加文件
105 files.forEach((file, index) => {
106 formData.append(`media_${index}`, file)
wu90da17b2025-06-19 12:45:29 +0800107 })
TRM-codingf55d2372025-06-20 16:22:37 +0800108 formData.append('media_count', files.length)
109
110 await createPost(formData)
wu90da17b2025-06-19 12:45:29 +0800111 alert('发布成功!')
112 }
113 navigate('/notebooks', { replace: true })
TRM-codingd1cbf672025-06-18 15:15:08 +0800114 } catch (e) {
115 setError(e.message)
116 }
117 }
118
wu90da17b2025-06-19 12:45:29 +0800119 if (loading) return <p>加载中…</p>
120 if (step === 'upload' && !isEdit) {
TRM-codingd1cbf672025-06-18 15:15:08 +0800121 return <UploadPage onComplete={handleUploadComplete} />
122 }
123
TRM-codingd1cbf672025-06-18 15:15:08 +0800124 return (
125 <div className="create-post">
wu90da17b2025-06-19 12:45:29 +0800126 <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2>
TRM-codingd1cbf672025-06-18 15:15:08 +0800127 {error && <div className="error">{error}</div>}
128
wu90da17b2025-06-19 12:45:29 +0800129 {/* 媒体预览 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800130 <div className="preview-media">
131 {mediaUrls.map((url, i) => (
132 <div key={i} className="preview-item">
wu90da17b2025-06-19 12:45:29 +0800133 {url.match(/\.(mp4|mov|avi)$/) ? (
TRM-codingd1cbf672025-06-18 15:15:08 +0800134 <video src={url} controls />
wu90da17b2025-06-19 12:45:29 +0800135 ) : (
136 <img src={url} alt={`预览 ${i}`} />
TRM-codingd1cbf672025-06-18 15:15:08 +0800137 )}
138 </div>
139 ))}
140 </div>
141
142 {/* 标题 */}
143 <label className="form-label">
144 标题(最多20字)
145 <input
146 type="text"
147 maxLength={20}
148 value={title}
149 onChange={e => setTitle(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800150 />
151 <span className="char-count">{title.length}/20</span>
152 </label>
153
154 {/* 正文 */}
155 <label className="form-label">
156 正文(最多1000字)
157 <textarea
158 maxLength={1000}
159 value={content}
160 onChange={e => setContent(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800161 />
162 <span className="char-count">{content.length}/1000</span>
163 </label>
164
165 {/* 话题选择 */}
166 <label className="form-label">
167 选择话题(可选)
168 <select
169 value={topicId}
170 onChange={e => setTopicId(e.target.value)}
171 >
172 <option value="">不添加话题</option>
173 {TOPICS.map(t => (
174 <option key={t.id} value={t.id}>
175 #{t.name}
176 </option>
177 ))}
178 </select>
179 </label>
180
181 {/* 发布状态 */}
182 <div className="status-group">
183 <label>
184 <input
185 type="radio"
186 name="status"
187 value="published"
188 checked={status === 'published'}
189 onChange={() => setStatus('published')}
190 />
191 立即发布
192 </label>
193 <label>
194 <input
195 type="radio"
196 name="status"
197 value="draft"
198 checked={status === 'draft'}
199 onChange={() => setStatus('draft')}
200 />
201 存为草稿
202 </label>
203 </div>
204
wu90da17b2025-06-19 12:45:29 +0800205 {/* 按钮 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800206 <div className="btn-group">
207 <button className="btn btn-primary" onClick={handleSubmit}>
wu90da17b2025-06-19 12:45:29 +0800208 {isEdit ? '更新' : '发布'}
TRM-codingd1cbf672025-06-18 15:15:08 +0800209 </button>
wu90da17b2025-06-19 12:45:29 +0800210 {!isEdit && (
211 <button className="btn btn-secondary" onClick={() => setStep('upload')}>
212 上一步
213 </button>
214 )}
TRM-codingd1cbf672025-06-18 15:15:08 +0800215 </div>
216 </div>
217 )
218}