| import React, { useState, useEffect } from 'react' |
| import { useNavigate, useParams } from 'react-router-dom' |
| import UploadPage from './UploadPage' |
| import { |
| createPost, |
| updatePost, |
| fetchPost as fetchPostDetail |
| } from '../api/posts_wzy' |
| import { getUserInfo } from '../utils/auth' |
| import '../style/CreatePost.css' |
| |
| export default function CreatePost() { |
| const navigate = useNavigate() |
| const { postId } = useParams() |
| const isEdit = Boolean(postId) |
| // 步骤:新帖先上传,编辑则直接到 detail |
| const [step, setStep] = useState(isEdit ? 'detail' : 'upload') |
| 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: '我染上了拼豆' }, |
| ] |
| |
| // 获取当前登录用户id |
| const user = getUserInfo() |
| const currentUserId = user?.id |
| |
| // 编辑模式:拉取原帖数据填入 |
| 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) |
| // 为预览创建临时URLs |
| const urls = uploadedFiles.map(f => URL.createObjectURL(f)) |
| setMediaUrls(urls) |
| setStep('detail') |
| } |
| |
| // 提交(创建/更新) |
| const handleSubmit = async () => { |
| if (!title.trim() || !content.trim()) { |
| setError('标题和正文必填') |
| return |
| } |
| if (!currentUserId) { |
| setError('未获取到用户ID,请重新登录') |
| return |
| } |
| setError(null) |
| |
| try { |
| // 创建FormData对象 |
| const formData = new FormData() |
| |
| // 添加文本字段 |
| formData.append('user_id', currentUserId) |
| formData.append('title', title.trim()) |
| formData.append('content', content.trim()) |
| formData.append('status', status) |
| if (topicId) { |
| formData.append('topic_id', topicId) |
| } |
| |
| if (isEdit) { |
| // 编辑模式:如果有新文件,添加新文件;否则保留现有URLs |
| if (files.length > 0) { |
| files.forEach((file, index) => { |
| formData.append(`media_${index}`, file) |
| }) |
| formData.append('media_count', files.length) |
| } else { |
| // 保留现有的media_urls |
| formData.append('existing_media_urls', JSON.stringify(mediaUrls)) |
| } |
| |
| await updatePost(postId, formData) |
| alert('更新成功!') |
| } else { |
| // 创建模式:添加文件 |
| files.forEach((file, index) => { |
| formData.append(`media_${index}`, file) |
| }) |
| formData.append('media_count', files.length) |
| |
| await createPost(formData) |
| alert('发布成功!') |
| } |
| navigate('/notebooks', { replace: true }) |
| } catch (e) { |
| setError(e.message) |
| } |
| } |
| |
| if (loading) return <p>加载中…</p> |
| if (step === 'upload' && !isEdit) { |
| return <UploadPage onComplete={handleUploadComplete} /> |
| } |
| |
| return ( |
| <div className="create-post"> |
| <h2>{isEdit ? '编辑帖子' : '填写帖子内容'}</h2> |
| {error && <div className="error">{error}</div>} |
| |
| {/* 媒体预览 */} |
| <div className="preview-media"> |
| {mediaUrls.map((url, i) => ( |
| <div key={i} className="preview-item"> |
| {url.match(/\.(mp4|mov|avi)$/) ? ( |
| <video src={url} controls /> |
| ) : ( |
| <img src={url} alt={`预览 ${i}`} /> |
| )} |
| </div> |
| ))} |
| </div> |
| |
| {/* 标题 */} |
| <label className="form-label"> |
| 标题(最多20字) |
| <input |
| type="text" |
| maxLength={20} |
| value={title} |
| onChange={e => setTitle(e.target.value)} |
| /> |
| <span className="char-count">{title.length}/20</span> |
| </label> |
| |
| {/* 正文 */} |
| <label className="form-label"> |
| 正文(最多1000字) |
| <textarea |
| maxLength={1000} |
| value={content} |
| onChange={e => setContent(e.target.value)} |
| /> |
| <span className="char-count">{content.length}/1000</span> |
| </label> |
| |
| {/* 话题选择 */} |
| <label className="form-label"> |
| 选择话题(可选) |
| <select |
| value={topicId} |
| onChange={e => setTopicId(e.target.value)} |
| > |
| <option value="">不添加话题</option> |
| {TOPICS.map(t => ( |
| <option key={t.id} value={t.id}> |
| #{t.name} |
| </option> |
| ))} |
| </select> |
| </label> |
| |
| {/* 发布状态 */} |
| <div className="status-group"> |
| <label> |
| <input |
| type="radio" |
| name="status" |
| value="published" |
| checked={status === 'published'} |
| onChange={() => setStatus('published')} |
| /> |
| 立即发布 |
| </label> |
| <label> |
| <input |
| type="radio" |
| name="status" |
| value="draft" |
| checked={status === 'draft'} |
| onChange={() => setStatus('draft')} |
| /> |
| 存为草稿 |
| </label> |
| </div> |
| |
| {/* 按钮 */} |
| <div className="btn-group"> |
| <button className="btn btn-primary" onClick={handleSubmit}> |
| {isEdit ? '更新' : '发布'} |
| </button> |
| {!isEdit && ( |
| <button className="btn btn-secondary" onClick={() => setStep('upload')}> |
| 上一步 |
| </button> |
| )} |
| </div> |
| </div> |
| ) |
| } |