| import React, { useState } from 'react' |
| import { Image, Video, Send } from 'lucide-react' |
| import { searchAPI } from '../api/search_jwlll' |
| import '../style/UploadPage.css' |
| |
| const categories = [ |
| '穿搭','美食','彩妆','影视', |
| '职场','情感','家居','游戏','旅行','健身' |
| ] |
| |
| export default function UploadPageJWLLL({ onComplete }) { |
| const [activeTab, setActiveTab] = useState('image') |
| const [isDragOver, setIsDragOver] = useState(false) |
| const [isUploading, setIsUploading] = useState(false) |
| const [uploadedFiles, setUploadedFiles] = useState([]) |
| const [uploadProgress, setUploadProgress] = useState(0) |
| |
| // 新增表单字段 |
| const [title, setTitle] = useState('') |
| const [content, setContent] = useState('') |
| const [tags, setTags] = useState('') |
| const [category, setCategory] = useState(categories[0]) |
| const [isPublishing, setIsPublishing] = useState(false) |
| |
| const DEFAULT_USER_ID = '3' // 默认用户ID |
| |
| const validateFiles = files => { |
| const imgTypes = ['image/jpeg','image/jpg','image/png','image/webp'] |
| const vidTypes = ['video/mp4','video/mov','video/avi'] |
| const types = activeTab==='video'? vidTypes : imgTypes |
| const max = activeTab==='video'? 2*1024*1024*1024 : 32*1024*1024 |
| |
| const invalid = files.filter(f => !types.includes(f.type) || f.size > max) |
| if (invalid.length) { |
| alert(`发现 ${invalid.length} 个无效文件,请检查文件格式和大小`) |
| return false |
| } |
| return true |
| } |
| |
| const simulateUpload = files => { |
| setIsUploading(true) |
| setUploadProgress(0) |
| setUploadedFiles(files) |
| const iv = setInterval(() => { |
| setUploadProgress(p => { |
| if (p >= 100) { |
| clearInterval(iv) |
| setIsUploading(false) |
| if (typeof onComplete === 'function') { |
| onComplete(files) |
| } |
| return 100 |
| } |
| return p + 10 |
| }) |
| }, 200) |
| } |
| |
| const handleFileUpload = () => { |
| if (isUploading) return |
| const input = document.createElement('input') |
| input.type = 'file' |
| input.accept = activeTab==='video'? 'video/*' : 'image/*' |
| input.multiple = activeTab==='image' |
| input.onchange = e => { |
| const files = Array.from(e.target.files) |
| if (files.length > 0 && validateFiles(files)) simulateUpload(files) |
| } |
| input.click() |
| } |
| |
| const handleDragOver = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(true) } |
| const handleDragLeave = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false) } |
| const handleDrop = e => { |
| e.preventDefault(); e.stopPropagation(); setIsDragOver(false) |
| if (isUploading) return |
| const files = Array.from(e.dataTransfer.files) |
| if (files.length > 0 && validateFiles(files)) simulateUpload(files) |
| } |
| |
| const clearFiles = () => setUploadedFiles([]) |
| const removeFile = idx => setUploadedFiles(f => f.filter((_,i) => i!==idx)) |
| |
| // 发布帖子 |
| const handlePublish = async () => { |
| if (!title.trim()) { |
| alert('请输入标题') |
| return |
| } |
| if (!content.trim()) { |
| alert('请输入内容') |
| return |
| } |
| |
| setIsPublishing(true) |
| try { |
| const postData = { |
| user_id: DEFAULT_USER_ID, |
| title: title.trim(), |
| content: content.trim(), |
| tags: tags.split(',').map(t => t.trim()).filter(t => t), |
| category: category, |
| type: activeTab === 'video' ? 'video' : 'image', |
| media_files: uploadedFiles.map(f => f.name) // 实际项目中应该是上传后的URL |
| } |
| |
| await searchAPI.uploadPost(postData) |
| alert('发布成功!') |
| |
| // 清空表单 |
| setTitle('') |
| setContent('') |
| setTags('') |
| setUploadedFiles([]) |
| setActiveTab('image') |
| |
| } catch (error) { |
| console.error('发布失败:', error) |
| alert('发布失败,请重试') |
| } finally { |
| setIsPublishing(false) |
| } |
| } |
| |
| return ( |
| <div className="upload-page-jwlll"> |
| <div className="upload-tabs"> |
| <button |
| className={`upload-tab${activeTab==='video'?' active':''}`} |
| onClick={() => setActiveTab('video')} |
| >上传视频</button> |
| <button |
| className={`upload-tab${activeTab==='image'?' active':''}`} |
| onClick={() => setActiveTab('image')} |
| >上传图文</button> |
| </div> |
| |
| {/* 内容表单 */} |
| <div className="content-form"> |
| <div className="form-group"> |
| <label htmlFor="title">标题</label> |
| <input |
| id="title" |
| type="text" |
| value={title} |
| onChange={(e) => setTitle(e.target.value)} |
| placeholder="请输入标题..." |
| className="form-input" |
| maxLength={100} |
| /> |
| </div> |
| |
| <div className="form-group"> |
| <label htmlFor="content">内容</label> |
| <textarea |
| id="content" |
| value={content} |
| onChange={(e) => setContent(e.target.value)} |
| placeholder="请输入内容..." |
| className="form-textarea" |
| rows={4} |
| maxLength={1000} |
| /> |
| </div> |
| |
| <div className="form-row"> |
| <div className="form-group"> |
| <label htmlFor="category">分类</label> |
| <select |
| id="category" |
| value={category} |
| onChange={(e) => setCategory(e.target.value)} |
| className="form-select" |
| > |
| {categories.map(cat => ( |
| <option key={cat} value={cat}>{cat}</option> |
| ))} |
| </select> |
| </div> |
| |
| <div className="form-group"> |
| <label htmlFor="tags">标签</label> |
| <input |
| id="tags" |
| type="text" |
| value={tags} |
| onChange={(e) => setTags(e.target.value)} |
| placeholder="用逗号分隔多个标签..." |
| className="form-input" |
| /> |
| </div> |
| </div> |
| </div> |
| |
| {/* 文件上传区域 */} |
| <div |
| className={`upload-area${isDragOver?' drag-over':''}`} |
| onDragOver={handleDragOver} |
| onDragLeave={handleDragLeave} |
| onDrop={handleDrop} |
| > |
| <div className="upload-icon"> |
| {activeTab==='video'? <Video/> : <Image/>} |
| </div> |
| <h2 className="upload-title"> |
| {activeTab==='video' |
| ? '拖拽视频到此处或点击上传' |
| : '拖拽图片到此处或点击上传' |
| } |
| </h2> |
| <p className="upload-subtitle">(需支持上传格式)</p> |
| <button |
| className={`upload-btn${isUploading?' uploading':''}`} |
| onClick={handleFileUpload} |
| disabled={isUploading} |
| > |
| {isUploading |
| ? `上传中... ${uploadProgress}%` |
| : activeTab==='video' |
| ? '上传视频' |
| : '上传图片' |
| } |
| </button> |
| |
| {isUploading && ( |
| <div className="progress-container"> |
| <div className="progress-bar"> |
| <div |
| className="progress-fill" |
| style={{ width: `${uploadProgress}%` }} |
| /> |
| </div> |
| <div className="progress-text">{uploadProgress}%</div> |
| </div> |
| )} |
| </div> |
| |
| {uploadedFiles.length > 0 && ( |
| <div className="file-preview-area"> |
| <div className="preview-header"> |
| <h3 className="preview-title">已上传文件 ({uploadedFiles.length})</h3> |
| <button className="clear-files-btn" onClick={clearFiles}> |
| 清除所有 |
| </button> |
| </div> |
| <div className="file-grid"> |
| {uploadedFiles.map((file, i) => ( |
| <div key={i} className="file-item"> |
| <button |
| className="remove-file-btn" |
| onClick={() => removeFile(i)} |
| title="删除文件" |
| >×</button> |
| {file.type.startsWith('image/') ? ( |
| <div className="file-thumbnail"> |
| <img src={URL.createObjectURL(file)} alt={file.name} /> |
| </div> |
| ) : ( |
| <div className="file-thumbnail video-thumbnail"> |
| <Video size={24} /> |
| </div> |
| )} |
| <div className="file-info"> |
| <div className="file-name" title={file.name}> |
| {file.name.length > 20 |
| ? file.name.slice(0,17) + '...' |
| : file.name |
| } |
| </div> |
| <div className="file-size"> |
| {(file.size/1024/1024).toFixed(2)} MB |
| </div> |
| </div> |
| </div> |
| ))} |
| </div> |
| </div> |
| )} |
| |
| {/* 发布按钮 */} |
| <div className="publish-section"> |
| <button |
| className={`publish-btn${isPublishing?' publishing':''}`} |
| onClick={handlePublish} |
| disabled={isPublishing || !title.trim() || !content.trim()} |
| > |
| <Send size={20} /> |
| {isPublishing ? '发布中...' : '发布'} |
| </button> |
| </div> |
| |
| <div className="upload-info fade-in"> |
| {activeTab==='image' ? ( |
| <> |
| <div className="info-item"> |
| <h3 className="info-title">图片大小</h3> |
| <p className="info-desc">最大32MB</p> |
| </div> |
| <div className="info-item"> |
| <h3 className="info-title">图片格式</h3> |
| <p className="info-desc">png/jpg/jpeg/webp</p> |
| </div> |
| <div className="info-item"> |
| <h3 className="info-title">分辨率</h3> |
| <p className="info-desc">建议720×960及以上</p> |
| </div> |
| </> |
| ) : ( |
| <> |
| <div className="info-item"> |
| <h3 className="info-title">视频大小</h3> |
| <p className="info-desc">最大2GB,时长≤5分钟</p> |
| </div> |
| <div className="info-item"> |
| <h3 className="info-title">视频格式</h3> |
| <p className="info-desc">mp4/mov</p> |
| </div> |
| <div className="info-item"> |
| <h3 className="info-title">分辨率</h3> |
| <p className="info-desc">建议720P及以上</p> |
| </div> |
| </> |
| )} |
| </div> |
| </div> |
| ) |
| } |