| // src/components/CreatePost.jsx |
| |
| import React, { useState } from 'react' |
| import { useNavigate } from 'react-router-dom' |
| import UploadPage from './UploadPage' |
| import { createPost } from '../api/posts' |
| import '../style/CreatePost.css' |
| |
| export default function CreatePost() { |
| const navigate = useNavigate() |
| |
| const [step, setStep] = useState('upload') // 'upload' | 'detail' |
| const [files, setFiles] = useState([]) // 本地 File 对象列表 |
| const [mediaUrls, setMediaUrls] = useState([]) // 上传后得到的 URL 列表 |
| |
| // 详情表单字段 |
| const [title, setTitle] = useState('') |
| const [content, setContent] = useState('') |
| const [topicId, setTopicId] = useState('') |
| const [status, setStatus] = useState('published') |
| |
| const [error, setError] = useState(null) |
| |
| // 静态话题数据 |
| const TOPICS = [ |
| { id: 1, name: '世俱杯环球评大会' }, |
| { id: 2, name: '我的REDmentor' }, |
| { id: 3, name: '我染上了拼豆' }, |
| // …更多静态话题… |
| ] |
| |
| // 上传页面回调 —— 上传完成后切换到“填写详情”步骤 |
| const handleUploadComplete = async uploadedFiles => { |
| setFiles(uploadedFiles) |
| |
| // TODO: 改成真实上传逻辑,拿到真正的 media_urls |
| const urls = await Promise.all( |
| uploadedFiles.map(f => URL.createObjectURL(f)) |
| ) |
| setMediaUrls(urls) |
| |
| setStep('detail') |
| } |
| |
| // 发布按钮 |
| const handleSubmit = async () => { |
| if (!title.trim() || !content.trim()) { |
| setError('标题和正文必填') |
| return |
| } |
| setError(null) |
| try { |
| await createPost({ |
| user_id: 1, |
| topic_id: topicId || undefined, |
| title: title.trim(), |
| content: content.trim(), |
| media_urls: mediaUrls, |
| status |
| }) |
| // 发布成功后跳转回首页 |
| navigate('/home', { replace: true }) |
| } catch (e) { |
| setError(e.message) |
| } |
| } |
| |
| // 渲染上传页 |
| if (step === 'upload') { |
| return <UploadPage onComplete={handleUploadComplete} /> |
| } |
| |
| // 渲染详情页 |
| return ( |
| <div className="create-post"> |
| <h2>填写帖子内容</h2> |
| {error && <div className="error">{error}</div>} |
| |
| {/* 已上传媒体预览 */} |
| <div className="preview-media"> |
| {mediaUrls.map((url, i) => ( |
| <div key={i} className="preview-item"> |
| {files[i].type.startsWith('image/') ? ( |
| <img src={url} alt={`预览 ${i}`} /> |
| ) : ( |
| <video src={url} controls /> |
| )} |
| </div> |
| ))} |
| </div> |
| |
| {/* 标题 */} |
| <label className="form-label"> |
| 标题(最多20字) |
| <input |
| type="text" |
| maxLength={20} |
| value={title} |
| onChange={e => setTitle(e.target.value)} |
| placeholder="填写标题会有更多赞哦~" |
| /> |
| <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)} |
| placeholder="输入正文描述,真诚有价值的分享予人温暖" |
| /> |
| <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}> |
| 发布 |
| </button> |
| <button className="btn btn-secondary" onClick={() => setStep('upload')}> |
| 上一步 |
| </button> |
| </div> |
| </div> |
| ) |
| } |