blob: 2d9ee7dee17f567b9b0a6ecb2b7a61f5bb96b3b6 [file] [log] [blame]
95630366980c1f272025-06-20 14:08:54 +08001import React, { useState } from 'react'
2import { Image, Video, Send } from 'lucide-react'
3import { searchAPI } from '../api/search_jwlll'
4import '../style/UploadPage.css'
5
6const categories = [
7 '穿搭','美食','彩妆','影视',
8 '职场','情感','家居','游戏','旅行','健身'
9]
10
11export default function UploadPageJWLLL({ onComplete }) {
12 const [activeTab, setActiveTab] = useState('image')
13 const [isDragOver, setIsDragOver] = useState(false)
14 const [isUploading, setIsUploading] = useState(false)
15 const [uploadedFiles, setUploadedFiles] = useState([])
16 const [uploadProgress, setUploadProgress] = useState(0)
17
18 // 新增表单字段
19 const [title, setTitle] = useState('')
20 const [content, setContent] = useState('')
21 const [tags, setTags] = useState('')
22 const [category, setCategory] = useState(categories[0])
23 const [isPublishing, setIsPublishing] = useState(false)
24
25 const DEFAULT_USER_ID = '3' // 默认用户ID
26
27 const validateFiles = files => {
28 const imgTypes = ['image/jpeg','image/jpg','image/png','image/webp']
29 const vidTypes = ['video/mp4','video/mov','video/avi']
30 const types = activeTab==='video'? vidTypes : imgTypes
31 const max = activeTab==='video'? 2*1024*1024*1024 : 32*1024*1024
32
33 const invalid = files.filter(f => !types.includes(f.type) || f.size > max)
34 if (invalid.length) {
35 alert(`发现 ${invalid.length} 个无效文件,请检查文件格式和大小`)
36 return false
37 }
38 return true
39 }
40
41 const simulateUpload = files => {
42 setIsUploading(true)
43 setUploadProgress(0)
44 setUploadedFiles(files)
45 const iv = setInterval(() => {
46 setUploadProgress(p => {
47 if (p >= 100) {
48 clearInterval(iv)
49 setIsUploading(false)
50 if (typeof onComplete === 'function') {
51 onComplete(files)
52 }
53 return 100
54 }
55 return p + 10
56 })
57 }, 200)
58 }
59
60 const handleFileUpload = () => {
61 if (isUploading) return
62 const input = document.createElement('input')
63 input.type = 'file'
64 input.accept = activeTab==='video'? 'video/*' : 'image/*'
65 input.multiple = activeTab==='image'
66 input.onchange = e => {
67 const files = Array.from(e.target.files)
68 if (files.length > 0 && validateFiles(files)) simulateUpload(files)
69 }
70 input.click()
71 }
72
73 const handleDragOver = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(true) }
74 const handleDragLeave = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false) }
75 const handleDrop = e => {
76 e.preventDefault(); e.stopPropagation(); setIsDragOver(false)
77 if (isUploading) return
78 const files = Array.from(e.dataTransfer.files)
79 if (files.length > 0 && validateFiles(files)) simulateUpload(files)
80 }
81
82 const clearFiles = () => setUploadedFiles([])
83 const removeFile = idx => setUploadedFiles(f => f.filter((_,i) => i!==idx))
84
85 // 发布帖子
86 const handlePublish = async () => {
87 if (!title.trim()) {
88 alert('请输入标题')
89 return
90 }
91 if (!content.trim()) {
92 alert('请输入内容')
93 return
94 }
95
96 setIsPublishing(true)
97 try {
98 const postData = {
99 user_id: DEFAULT_USER_ID,
100 title: title.trim(),
101 content: content.trim(),
102 tags: tags.split(',').map(t => t.trim()).filter(t => t),
103 category: category,
104 type: activeTab === 'video' ? 'video' : 'image',
105 media_files: uploadedFiles.map(f => f.name) // 实际项目中应该是上传后的URL
106 }
107
108 await searchAPI.uploadPost(postData)
109 alert('发布成功!')
110
111 // 清空表单
112 setTitle('')
113 setContent('')
114 setTags('')
115 setUploadedFiles([])
116 setActiveTab('image')
117
118 } catch (error) {
119 console.error('发布失败:', error)
120 alert('发布失败,请重试')
121 } finally {
122 setIsPublishing(false)
123 }
124 }
125
126 return (
127 <div className="upload-page-jwlll">
128 <div className="upload-tabs">
129 <button
130 className={`upload-tab${activeTab==='video'?' active':''}`}
131 onClick={() => setActiveTab('video')}
132 >上传视频</button>
133 <button
134 className={`upload-tab${activeTab==='image'?' active':''}`}
135 onClick={() => setActiveTab('image')}
136 >上传图文</button>
137 </div>
138
139 {/* 内容表单 */}
140 <div className="content-form">
141 <div className="form-group">
142 <label htmlFor="title">标题</label>
143 <input
144 id="title"
145 type="text"
146 value={title}
147 onChange={(e) => setTitle(e.target.value)}
148 placeholder="请输入标题..."
149 className="form-input"
150 maxLength={100}
151 />
152 </div>
153
154 <div className="form-group">
155 <label htmlFor="content">内容</label>
156 <textarea
157 id="content"
158 value={content}
159 onChange={(e) => setContent(e.target.value)}
160 placeholder="请输入内容..."
161 className="form-textarea"
162 rows={4}
163 maxLength={1000}
164 />
165 </div>
166
167 <div className="form-row">
168 <div className="form-group">
169 <label htmlFor="category">分类</label>
170 <select
171 id="category"
172 value={category}
173 onChange={(e) => setCategory(e.target.value)}
174 className="form-select"
175 >
176 {categories.map(cat => (
177 <option key={cat} value={cat}>{cat}</option>
178 ))}
179 </select>
180 </div>
181
182 <div className="form-group">
183 <label htmlFor="tags">标签</label>
184 <input
185 id="tags"
186 type="text"
187 value={tags}
188 onChange={(e) => setTags(e.target.value)}
189 placeholder="用逗号分隔多个标签..."
190 className="form-input"
191 />
192 </div>
193 </div>
194 </div>
195
196 {/* 文件上传区域 */}
197 <div
198 className={`upload-area${isDragOver?' drag-over':''}`}
199 onDragOver={handleDragOver}
200 onDragLeave={handleDragLeave}
201 onDrop={handleDrop}
202 >
203 <div className="upload-icon">
204 {activeTab==='video'? <Video/> : <Image/>}
205 </div>
206 <h2 className="upload-title">
207 {activeTab==='video'
208 ? '拖拽视频到此处或点击上传'
209 : '拖拽图片到此处或点击上传'
210 }
211 </h2>
212 <p className="upload-subtitle">(需支持上传格式)</p>
213 <button
214 className={`upload-btn${isUploading?' uploading':''}`}
215 onClick={handleFileUpload}
216 disabled={isUploading}
217 >
218 {isUploading
219 ? `上传中... ${uploadProgress}%`
220 : activeTab==='video'
221 ? '上传视频'
222 : '上传图片'
223 }
224 </button>
225
226 {isUploading && (
227 <div className="progress-container">
228 <div className="progress-bar">
229 <div
230 className="progress-fill"
231 style={{ width: `${uploadProgress}%` }}
232 />
233 </div>
234 <div className="progress-text">{uploadProgress}%</div>
235 </div>
236 )}
237 </div>
238
239 {uploadedFiles.length > 0 && (
240 <div className="file-preview-area">
241 <div className="preview-header">
242 <h3 className="preview-title">已上传文件 ({uploadedFiles.length})</h3>
243 <button className="clear-files-btn" onClick={clearFiles}>
244 清除所有
245 </button>
246 </div>
247 <div className="file-grid">
248 {uploadedFiles.map((file, i) => (
249 <div key={i} className="file-item">
250 <button
251 className="remove-file-btn"
252 onClick={() => removeFile(i)}
253 title="删除文件"
254 >×</button>
255 {file.type.startsWith('image/') ? (
256 <div className="file-thumbnail">
257 <img src={URL.createObjectURL(file)} alt={file.name} />
258 </div>
259 ) : (
260 <div className="file-thumbnail video-thumbnail">
261 <Video size={24} />
262 </div>
263 )}
264 <div className="file-info">
265 <div className="file-name" title={file.name}>
266 {file.name.length > 20
267 ? file.name.slice(0,17) + '...'
268 : file.name
269 }
270 </div>
271 <div className="file-size">
272 {(file.size/1024/1024).toFixed(2)} MB
273 </div>
274 </div>
275 </div>
276 ))}
277 </div>
278 </div>
279 )}
280
281 {/* 发布按钮 */}
282 <div className="publish-section">
283 <button
284 className={`publish-btn${isPublishing?' publishing':''}`}
285 onClick={handlePublish}
286 disabled={isPublishing || !title.trim() || !content.trim()}
287 >
288 <Send size={20} />
289 {isPublishing ? '发布中...' : '发布'}
290 </button>
291 </div>
292
293 <div className="upload-info fade-in">
294 {activeTab==='image' ? (
295 <>
296 <div className="info-item">
297 <h3 className="info-title">图片大小</h3>
298 <p className="info-desc">最大32MB</p>
299 </div>
300 <div className="info-item">
301 <h3 className="info-title">图片格式</h3>
302 <p className="info-desc">png/jpg/jpeg/webp</p>
303 </div>
304 <div className="info-item">
305 <h3 className="info-title">分辨率</h3>
306 <p className="info-desc">建议720×960及以上</p>
307 </div>
308 </>
309 ) : (
310 <>
311 <div className="info-item">
312 <h3 className="info-title">视频大小</h3>
313 <p className="info-desc">最大2GB,时长≤5分钟</p>
314 </div>
315 <div className="info-item">
316 <h3 className="info-title">视频格式</h3>
317 <p className="info-desc">mp4/mov</p>
318 </div>
319 <div className="info-item">
320 <h3 className="info-title">分辨率</h3>
321 <p className="info-desc">建议720P及以上</p>
322 </div>
323 </>
324 )}
325 </div>
326 </div>
327 )
328}