blob: 84f3fecfd31ccfe0751981ab581086d782d6953c [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')
trm36915ed2025-06-20 09:48:51 +000018 const [files, setFiles] = useState([])
wu90da17b2025-06-19 12:45:29 +080019 const [mediaUrls, setMediaUrls] = useState([])
TRM-codingd1cbf672025-06-18 15:15:08 +080020
wu90da17b2025-06-19 12:45:29 +080021 // 表单字段
TRM-codingd1cbf672025-06-18 15:15:08 +080022 const [title, setTitle] = useState('')
23 const [content, setContent] = useState('')
24 const [topicId, setTopicId] = useState('')
25 const [status, setStatus] = useState('published')
26
27 const [error, setError] = useState(null)
wu90da17b2025-06-19 12:45:29 +080028 const [loading, setLoading] = useState(isEdit)
TRM-codingd1cbf672025-06-18 15:15:08 +080029
wu90da17b2025-06-19 12:45:29 +080030 // 静态话题
TRM-codingd1cbf672025-06-18 15:15:08 +080031 const TOPICS = [
32 { id: 1, name: '世俱杯环球评大会' },
33 { id: 2, name: '我的REDmentor' },
34 { id: 3, name: '我染上了拼豆' },
TRM-codingd1cbf672025-06-18 15:15:08 +080035 ]
36
wu2f28f672025-06-19 14:29:30 +080037 // 获取当前登录用户id
38 const user = getUserInfo()
39 const currentUserId = user?.id
40
wu90da17b2025-06-19 12:45:29 +080041 // 编辑模式:拉取原帖数据填入
42 useEffect(() => {
43 if (!isEdit) return
44 fetchPostDetail(postId)
45 .then(data => {
46 setTitle(data.title)
47 setContent(data.content)
48 setTopicId(data.topic_id || '')
49 setStatus(data.status)
50 setMediaUrls(data.media_urls || [])
51 })
52 .catch(err => setError(err.message))
53 .finally(() => setLoading(false))
54 }, [isEdit, postId])
TRM-codingf55d2372025-06-20 16:22:37 +080055
56 // 上传回调 - 保存原始文件对象
TRM-codingd1cbf672025-06-18 15:15:08 +080057 const handleUploadComplete = async uploadedFiles => {
TRM-codingf55d2372025-06-20 16:22:37 +080058 setFiles(uploadedFiles)
59 // 为预览创建临时URLs
60 const urls = uploadedFiles.map(f => URL.createObjectURL(f))
TRM-codingd1cbf672025-06-18 15:15:08 +080061 setMediaUrls(urls)
TRM-codingd1cbf672025-06-18 15:15:08 +080062 setStep('detail')
63 }
64
wu90da17b2025-06-19 12:45:29 +080065 // 提交(创建/更新)
TRM-codingd1cbf672025-06-18 15:15:08 +080066 const handleSubmit = async () => {
67 if (!title.trim() || !content.trim()) {
68 setError('标题和正文必填')
69 return
70 }
wu2f28f672025-06-19 14:29:30 +080071 if (!currentUserId) {
72 setError('未获取到用户ID,请重新登录')
73 return
74 }
TRM-codingd1cbf672025-06-18 15:15:08 +080075 setError(null)
TRM-codingf55d2372025-06-20 16:22:37 +080076
TRM-codingd1cbf672025-06-18 15:15:08 +080077 try {
TRM-codingf55d2372025-06-20 16:22:37 +080078 // 创建FormData对象
79 const formData = new FormData()
80
81 // 添加文本字段
82 formData.append('user_id', currentUserId)
83 formData.append('title', title.trim())
84 formData.append('content', content.trim())
85 formData.append('status', status)
86 if (topicId) {
87 formData.append('topic_id', topicId)
88 }
89
wu90da17b2025-06-19 12:45:29 +080090 if (isEdit) {
TRM-codingf55d2372025-06-20 16:22:37 +080091 // 编辑模式:如果有新文件,添加新文件;否则保留现有URLs
92 if (files.length > 0) {
93 files.forEach((file, index) => {
94 formData.append(`media_${index}`, file)
95 })
96 formData.append('media_count', files.length)
97 } else {
98 // 保留现有的media_urls
99 formData.append('existing_media_urls', JSON.stringify(mediaUrls))
100 }
101
102 await updatePost(postId, formData)
wu90da17b2025-06-19 12:45:29 +0800103 alert('更新成功!')
104 } else {
TRM-codingf55d2372025-06-20 16:22:37 +0800105 // 创建模式:添加文件
106 files.forEach((file, index) => {
107 formData.append(`media_${index}`, file)
wu90da17b2025-06-19 12:45:29 +0800108 })
TRM-codingf55d2372025-06-20 16:22:37 +0800109 formData.append('media_count', files.length)
110
111 await createPost(formData)
wu90da17b2025-06-19 12:45:29 +0800112 alert('发布成功!')
113 }
114 navigate('/notebooks', { replace: true })
TRM-codingd1cbf672025-06-18 15:15:08 +0800115 } catch (e) {
116 setError(e.message)
117 }
118 }
119
wu90da17b2025-06-19 12:45:29 +0800120 if (loading) return <p>加载中…</p>
121 if (step === 'upload' && !isEdit) {
TRM-codingd1cbf672025-06-18 15:15:08 +0800122 return <UploadPage onComplete={handleUploadComplete} />
123 }
124
TRM-codingd1cbf672025-06-18 15:15:08 +0800125 return (
126 <div className="create-post">
wu90da17b2025-06-19 12:45:29 +0800127 <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2>
TRM-codingd1cbf672025-06-18 15:15:08 +0800128 {error && <div className="error">{error}</div>}
129
wu90da17b2025-06-19 12:45:29 +0800130 {/* 媒体预览 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800131 <div className="preview-media">
132 {mediaUrls.map((url, i) => (
133 <div key={i} className="preview-item">
wu90da17b2025-06-19 12:45:29 +0800134 {url.match(/\.(mp4|mov|avi)$/) ? (
TRM-codingd1cbf672025-06-18 15:15:08 +0800135 <video src={url} controls />
wu90da17b2025-06-19 12:45:29 +0800136 ) : (
137 <img src={url} alt={`预览 ${i}`} />
TRM-codingd1cbf672025-06-18 15:15:08 +0800138 )}
139 </div>
140 ))}
141 </div>
142
143 {/* 标题 */}
144 <label className="form-label">
145 标题(最多20字)
146 <input
147 type="text"
148 maxLength={20}
149 value={title}
150 onChange={e => setTitle(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800151 />
152 <span className="char-count">{title.length}/20</span>
153 </label>
154
155 {/* 正文 */}
156 <label className="form-label">
157 正文(最多1000字)
158 <textarea
159 maxLength={1000}
160 value={content}
161 onChange={e => setContent(e.target.value)}
TRM-codingd1cbf672025-06-18 15:15:08 +0800162 />
163 <span className="char-count">{content.length}/1000</span>
164 </label>
165
166 {/* 话题选择 */}
167 <label className="form-label">
168 选择话题(可选)
169 <select
170 value={topicId}
171 onChange={e => setTopicId(e.target.value)}
172 >
173 <option value="">不添加话题</option>
174 {TOPICS.map(t => (
175 <option key={t.id} value={t.id}>
176 #{t.name}
177 </option>
178 ))}
179 </select>
180 </label>
181
182 {/* 发布状态 */}
183 <div className="status-group">
184 <label>
185 <input
186 type="radio"
187 name="status"
188 value="published"
189 checked={status === 'published'}
190 onChange={() => setStatus('published')}
191 />
192 立即发布
193 </label>
194 <label>
195 <input
196 type="radio"
197 name="status"
198 value="draft"
199 checked={status === 'draft'}
200 onChange={() => setStatus('draft')}
201 />
202 存为草稿
203 </label>
204 </div>
205
wu90da17b2025-06-19 12:45:29 +0800206 {/* 按钮 */}
TRM-codingd1cbf672025-06-18 15:15:08 +0800207 <div className="btn-group">
208 <button className="btn btn-primary" onClick={handleSubmit}>
wu90da17b2025-06-19 12:45:29 +0800209 {isEdit ? '更新' : '发布'}
TRM-codingd1cbf672025-06-18 15:15:08 +0800210 </button>
wu90da17b2025-06-19 12:45:29 +0800211 {!isEdit && (
212 <button className="btn btn-secondary" onClick={() => setStep('upload')}>
213 上一步
214 </button>
215 )}
TRM-codingd1cbf672025-06-18 15:15:08 +0800216 </div>
217 </div>
218 )
219}