blob: 49e75d28d24500f708f40e96bccb39c951289d98 [file] [log] [blame] [edit]
// src/components/CreatePost.jsx
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'
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 [files, setFiles] = useState([])
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: '我染上了拼豆' },
]
// 编辑模式:拉取原帖数据填入
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)
// TODO: 真正上传到服务器后替换为服务端 URL
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 {
if (isEdit) {
await updatePost(postId, {
title: title.trim(),
content: content.trim(),
topic_id: topicId || undefined,
media_urls: mediaUrls,
status
})
alert('更新成功!')
} else {
await createPost({
user_id: 1,
topic_id: topicId || undefined,
title: title.trim(),
content: content.trim(),
media_urls: mediaUrls,
status
})
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>
)
}