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