blob: e6db8610ac3ee713e1332e7c3e08bcac39e5e85b [file] [log] [blame]
22301008af173152025-06-15 10:46:25 +08001import React, { useState } from 'react'
2import { Image, Video } from 'lucide-react'
3
4
5export default function UploadPage() {
6 const [activeTab, setActiveTab] = useState('image')
7 const [isDragOver, setIsDragOver] = useState(false)
8 const [isUploading, setIsUploading] = useState(false)
9 const [uploadedFiles, setUploadedFiles] = useState([])
10 const [uploadProgress, setUploadProgress] = useState(0)
11
12 const validateFiles = files => {
13 const imgTypes = ['image/jpeg','image/jpg','image/png','image/webp']
14 const vidTypes = ['video/mp4','video/mov','video/avi']
15 const types = activeTab==='video'? vidTypes : imgTypes
16 const max = activeTab==='video'? 2*1024*1024*1024 : 32*1024*1024
17
18 const invalid = files.filter(f => !types.includes(f.type) || f.size > max)
19 if (invalid.length) {
20 alert(`发现 ${invalid.length} 个无效文件,请检查文件格式和大小`)
21 return false
22 }
23 return true
24 }
25
26 const simulateUpload = files => {
27 setIsUploading(true)
28 setUploadProgress(0)
29 setUploadedFiles(files)
30 const iv = setInterval(() => {
31 setUploadProgress(p => {
32 if (p >= 100) {
33 clearInterval(iv)
34 setIsUploading(false)
35 alert(`成功上传了 ${files.length} 个文件`)
36 return 100
37 }
38 return p + 10
39 })
40 }, 200)
41 }
42
43 const handleFileUpload = () => {
44 if (isUploading) return
45 const input = document.createElement('input')
46 input.type = 'file'
47 input.accept = activeTab==='video'? 'video/*' : 'image/*'
48 input.multiple = activeTab==='image'
49 input.onchange = e => {
50 const files = Array.from(e.target.files)
51 if (files.length > 0 && validateFiles(files)) simulateUpload(files)
52 }
53 input.click()
54 }
55
56 const handleDragOver = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(true) }
57 const handleDragLeave = e => { e.preventDefault(); e.stopPropagation(); setIsDragOver(false) }
58 const handleDrop = e => {
59 e.preventDefault(); e.stopPropagation(); setIsDragOver(false)
60 if (isUploading) return
61 const files = Array.from(e.dataTransfer.files)
62 if (files.length > 0 && validateFiles(files)) simulateUpload(files)
63 }
64
65 const clearFiles = () => setUploadedFiles([])
66 const removeFile = idx => setUploadedFiles(f => f.filter((_,i) => i!==idx))
67
68 return (
69 <>
70 <div className="upload-tabs">
71 <button
72 className={`upload-tab${activeTab==='video'?' active':''}`}
73 onClick={() => setActiveTab('video')}
74 >上传视频</button>
75 <button
76 className={`upload-tab${activeTab==='image'?' active':''}`}
77 onClick={() => setActiveTab('image')}
78 >上传图文</button>
79 </div>
80
81 <div
82 className={`upload-area${isDragOver?' drag-over':''}`}
83 onDragOver={handleDragOver}
84 onDragLeave={handleDragLeave}
85 onDrop={handleDrop}
86 >
87 <div className="upload-icon">
88 {activeTab==='video'? <Video/> : <Image/>}
89 </div>
90 <h2 className="upload-title">
91 {activeTab==='video'
92 ? '拖拽视频到此处或点击上传'
93 : '拖拽图片到此处或点击上传'
94 }
95 </h2>
96 <p className="upload-subtitle">(需支持上传格式)</p>
97 <button
98 className={`upload-btn${isUploading?' uploading':''}`}
99 onClick={handleFileUpload}
100 disabled={isUploading}
101 >
102 {isUploading
103 ? `上传中... ${uploadProgress}%`
104 : activeTab==='video'
105 ? '上传视频'
106 : '上传图片'
107 }
108 </button>
109
110 {isUploading && (
111 <div className="progress-container">
112 <div className="progress-bar">
113 <div
114 className="progress-fill"
115 style={{ width: `${uploadProgress}%` }}
116 />
117 </div>
118 <div className="progress-text">{uploadProgress}%</div>
119 </div>
120 )}
121 </div>
122
123 {uploadedFiles.length > 0 && (
124 <div className="file-preview-area">
125 <div className="preview-header">
126 <h3 className="preview-title">已上传文件 ({uploadedFiles.length})</h3>
127 <button className="clear-files-btn" onClick={clearFiles}>
128 清除所有
129 </button>
130 </div>
131 <div className="file-grid">
132 {uploadedFiles.map((file, i) => (
133 <div key={i} className="file-item">
134 <button
135 className="remove-file-btn"
136 onClick={() => removeFile(i)}
137 title="删除文件"
138 >×</button>
139 {file.type.startsWith('image/') ? (
140 <div className="file-thumbnail">
141 <img src={URL.createObjectURL(file)} alt={file.name} />
142 </div>
143 ) : (
144 <div className="file-thumbnail video-thumbnail">
145 <Video size={24} />
146 </div>
147 )}
148 <div className="file-info">
149 <div className="file-name" title={file.name}>
150 {file.name.length > 20
151 ? file.name.slice(0,17) + '...'
152 : file.name
153 }
154 </div>
155 <div className="file-size">
156 {(file.size/1024/1024).toFixed(2)} MB
157 </div>
158 </div>
159 </div>
160 ))}
161 </div>
162 </div>
163 )}
164
165 <div className="upload-info fade-in">
166 {activeTab==='image' ? (
167 <>
168 <div className="info-item">
169 <h3 className="info-title">图片大小</h3>
170 <p className="info-desc">最大32MB</p>
171 </div>
172 <div className="info-item">
173 <h3 className="info-title">图片格式</h3>
174 <p className="info-desc">png/jpg/jpeg/webp</p>
175 </div>
176 <div className="info-item">
177 <h3 className="info-title">分辨率</h3>
178 <p className="info-desc">建议720×960及以上</p>
179 </div>
180 </>
181 ) : (
182 <>
183 <div className="info-item">
184 <h3 className="info-title">视频大小</h3>
185 <p className="info-desc">最大2GB,时长≤5分钟</p>
186 </div>
187 <div className="info-item">
188 <h3 className="info-title">视频格式</h3>
189 <p className="info-desc">mp4/mov</p>
190 </div>
191 <div className="info-item">
192 <h3 className="info-title">分辨率</h3>
193 <p className="info-desc">建议720P及以上</p>
194 </div>
195 </>
196 )}
197 </div>
198 </>
199 )
200}