22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 1 | // export default PublishSeed; |
| 2 | import React, { useState, useRef, useEffect } from 'react'; |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 3 | import axios from 'axios'; |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 4 | import Header from '../../components/Header'; |
| 5 | import './PublishSeed.css'; |
| 6 | import { useUser } from '../../context/UserContext'; |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 7 | import { uploadFile } from '../../api/file'; |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 8 | |
| 9 | const PublishSeed = () => { |
| 10 | const [title, setTitle] = useState(''); |
| 11 | const [description, setDescription] = useState(''); |
| 12 | const [tags, setTags] = useState([]); |
| 13 | const [category, setCategory] = useState('movie'); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 14 | const [message, setMessage] = useState(''); |
| 15 | const [isLoading, setIsLoading] = useState(false); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 16 | const [fileName, setFileName] = useState(''); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 17 | const [imageFile, setImageFile] = useState(null); |
| 18 | const [previewUrl, setPreviewUrl] = useState(''); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 19 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 20 | const fileInputRef = useRef(null); |
| 21 | const imageInputRef = useRef(null); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 22 | const { user } = useUser(); |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 23 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 24 | useEffect(() => { |
| 25 | console.log('[DEBUG] 当前用户:', user); |
| 26 | }, [user]); |
| 27 | |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 28 | const handleTagsChange = (e) => { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 29 | console.log('[DEBUG] 标签输入变化:', e.target.value); |
| 30 | setTags(e.target.value.split(',').map(tag => tag.trim()).filter(Boolean)); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 31 | }; |
| 32 | |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 33 | const handleFileButtonClick = () => { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 34 | console.log('[DEBUG] 触发文件选择按钮点击'); |
| 35 | fileInputRef.current?.click(); |
| 36 | }; |
| 37 | |
| 38 | const handleImageButtonClick = () => { |
| 39 | console.log('[DEBUG] 触发封面图片选择按钮点击'); |
| 40 | imageInputRef.current?.click(); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 41 | }; |
| 42 | |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 43 | const handleFileChange = (e) => { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 44 | console.log('[DEBUG] 种子文件选择变化:', e.target.files); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 45 | const selectedFile = e.target.files[0]; |
| 46 | if (selectedFile) { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 47 | setFileName(selectedFile.name); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 48 | } |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 49 | }; |
| 50 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 51 | const handleImageChange = (e) => { |
| 52 | console.log('[DEBUG] 封面图片选择变化:', e.target.files); |
| 53 | const img = e.target.files[0]; |
| 54 | if (!img) return; |
| 55 | setImageFile(img); |
| 56 | setPreviewUrl(URL.createObjectURL(img)); |
| 57 | }; |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 58 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 59 | const handleSubmit = async (e) => { |
| 60 | console.log('[DEBUG] handleSubmit 被触发', e); |
| 61 | e.preventDefault(); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 62 | |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 63 | setIsLoading(true); |
| 64 | setMessage(''); |
| 65 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 66 | const currentFile = fileInputRef.current?.files[0]; |
| 67 | console.log('[DEBUG] 当前选择文件:', currentFile); |
| 68 | |
| 69 | if (!user || !user.userId) { |
| 70 | console.log('[DEBUG] 用户未登录,阻止上传'); |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 71 | setMessage('请先登录'); |
| 72 | setIsLoading(false); |
| 73 | return; |
| 74 | } |
| 75 | |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 76 | if (!currentFile || !currentFile.name.toLowerCase().endsWith('.torrent')) { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 77 | console.log('[DEBUG] 文件校验失败'); |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 78 | setMessage('请上传一个 .torrent 文件'); |
| 79 | setIsLoading(false); |
| 80 | return; |
| 81 | } |
| 82 | |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 83 | const formData = new FormData(); |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 84 | formData.append('file', currentFile); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 85 | formData.append('title', title); |
| 86 | formData.append('description', description); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 87 | formData.append('category', category); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 88 | formData.append('tags', tags.join(',')); // 逗号分隔字符串 |
| 89 | formData.append('uploader', user.userId); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 90 | if (imageFile) { |
22301009 | 99cf316 | 2025-06-06 00:19:25 +0800 | [diff] [blame] | 91 | formData.append('coverImage', imageFile); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 92 | } |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 93 | |
| 94 | try { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 95 | console.log('[DEBUG] 发送上传请求...'); |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 96 | const response = await axios.post('/seeds/upload', formData, { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 97 | // axios 会自动处理 multipart/form-data Content-Type 边界,不用手动设置 |
| 98 | // headers: { 'Content-Type': 'multipart/form-data' }, |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 99 | }); |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 100 | console.log('[DEBUG] 请求成功,响应:', response.data); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 101 | |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 102 | if (response.data.status === 'success') { |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 103 | setMessage('种子上传成功'); |
| 104 | } else { |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 105 | setMessage(response.data.message || '上传失败,请稍后再试'); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 106 | } |
| 107 | } catch (error) { |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 108 | console.error('[handleSubmit] 上传失败:', error); |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 109 | setMessage('上传失败,发生了错误'); |
| 110 | } finally { |
| 111 | setIsLoading(false); |
| 112 | } |
| 113 | }; |
| 114 | |
| 115 | return ( |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 116 | <div className="ps-container"> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 117 | <Header /> |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 118 | <div className="ps-card"> |
| 119 | {message && <div className="ps-message">{message}</div>} |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 120 | <form |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 121 | className="ps-form" |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 122 | onSubmit={(e) => { |
| 123 | console.log('[DEBUG] form onSubmit 触发'); |
| 124 | handleSubmit(e); |
| 125 | }} |
| 126 | encType="multipart/form-data" |
| 127 | > |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 128 | <div> |
| 129 | <label className="ps-label">标题</label> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 130 | <input |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 131 | className="ps-input-text" |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 132 | type="text" |
| 133 | value={title} |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 134 | onChange={(e) => { |
| 135 | console.log('[DEBUG] 标题输入变化:', e.target.value); |
| 136 | setTitle(e.target.value); |
| 137 | }} |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 138 | required |
| 139 | /> |
| 140 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 141 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 142 | <div> |
| 143 | <label className="ps-label">描述</label> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 144 | <textarea |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 145 | className="ps-textarea" |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 146 | value={description} |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 147 | onChange={(e) => { |
| 148 | console.log('[DEBUG] 描述输入变化:', e.target.value); |
| 149 | setDescription(e.target.value); |
| 150 | }} |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 151 | required |
| 152 | /> |
| 153 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 154 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 155 | <div> |
| 156 | <label className="ps-label">标签 (逗号分隔)</label> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 157 | <input |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 158 | className="ps-input-text" |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 159 | type="text" |
| 160 | value={tags.join(', ')} |
| 161 | onChange={handleTagsChange} |
| 162 | placeholder="例如:科幻, 动作" |
| 163 | required |
| 164 | /> |
| 165 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 166 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 167 | <div> |
| 168 | <label className="ps-label">分类</label> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 169 | <select |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 170 | className="ps-select" |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 171 | value={category} |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 172 | onChange={(e) => { |
| 173 | console.log('[DEBUG] 分类选择变化:', e.target.value); |
| 174 | setCategory(e.target.value); |
| 175 | }} |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 176 | required |
| 177 | > |
| 178 | <option value="movie">电影</option> |
| 179 | <option value="tv">电视剧</option> |
| 180 | <option value="music">音乐</option> |
22301111 | a289e26 | 2025-06-07 22:38:46 +0800 | [diff] [blame] | 181 | <option value="anime">动漫</option> |
| 182 | <option value="game">游戏</option> |
| 183 | <option value="variety">综艺</option> |
| 184 | <option value="software">软件</option> |
| 185 | <option value="sports">体育</option> |
| 186 | <option value="study">学习</option> |
| 187 | <option value="documentary">纪录片</option> |
| 188 | <option value="other">其他</option> |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 189 | </select> |
| 190 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 191 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 192 | <div> |
| 193 | <label className="ps-label">种子文件</label> |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 194 | <div |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 195 | className="ps-seed-file-label" |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 196 | onClick={handleFileButtonClick} |
| 197 | style={{ cursor: 'pointer' }} |
| 198 | > |
Krishya | f1d0ea8 | 2025-05-03 17:01:58 +0800 | [diff] [blame] | 199 | 点击选择文件 |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 200 | </div> |
| 201 | <input |
| 202 | type="file" |
| 203 | accept=".torrent" |
| 204 | ref={fileInputRef} |
| 205 | onChange={handleFileChange} |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 206 | className="ps-seed-file-input" |
22301009 | df48f96 | 2025-06-05 13:40:44 +0800 | [diff] [blame] | 207 | /> |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 208 | {fileName && <div style={{ marginTop: '5px', color: '#5F4437' }}>{fileName}</div>} |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 209 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 210 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 211 | <div> |
| 212 | <label className="ps-label">封面图</label> |
| 213 | <div> |
| 214 | <button |
| 215 | type="button" |
| 216 | onClick={handleImageButtonClick} |
| 217 | className="ps-cover-upload-button" |
| 218 | > |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 219 | 上传图片 |
| 220 | </button> |
| 221 | <input |
| 222 | type="file" |
| 223 | accept="image/*" |
| 224 | ref={imageInputRef} |
| 225 | onChange={handleImageChange} |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 226 | className="ps-cover-upload-input" |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 227 | /> |
| 228 | </div> |
| 229 | {previewUrl && ( |
| 230 | <div style={{ marginTop: '10px' }}> |
| 231 | <img |
| 232 | src={previewUrl} |
| 233 | alt="封面预览" |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 234 | className="ps-img-preview" |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 235 | /> |
| 236 | </div> |
| 237 | )} |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 238 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 239 | |
22301009 | 1e2aea7 | 2025-06-08 16:35:54 +0800 | [diff] [blame^] | 240 | <div className="ps-upload-button"> |
22301009 | 80aaf0d | 2025-06-05 23:20:05 +0800 | [diff] [blame] | 241 | <button |
| 242 | type="submit" |
| 243 | disabled={isLoading} |
| 244 | onClick={() => console.log('[DEBUG] 上传按钮 onClick 触发')} |
| 245 | > |
22301009 | 6401163 | 2025-06-04 21:57:22 +0800 | [diff] [blame] | 246 | {isLoading ? '正在上传...' : '上传种子'} |
| 247 | </button> |
| 248 | </div> |
| 249 | </form> |
Krishya | c0f7e9b | 2025-04-22 15:28:28 +0800 | [diff] [blame] | 250 | </div> |
22301009 | 5b28c67 | 2025-04-10 20:12:45 +0800 | [diff] [blame] | 251 | </div> |
| 252 | ); |
| 253 | }; |
| 254 | |
| 255 | export default PublishSeed; |