blob: 80c366044c731e88c4e1e407c9599f016c089db9 [file] [log] [blame]
Xing Jinwenff16b1e2025-06-05 00:29:26 +08001<template>
2 <div class="upload-page">
3 <div class="page-header">
4 <h1>上传种子</h1>
5 <p class="page-description">分享你的资源,为社区做贡献</p>
6 </div>
7
8 <div class="upload-form">
9 <el-form
10 ref="uploadFormRef"
11 :model="uploadForm"
12 :rules="formRules"
13 label-width="120px"
14 size="large"
15 >
16 <!-- 种子文件上传 -->
17 <el-form-item label="种子文件" prop="torrentFile" required>
18 <el-upload
19 ref="torrentUploadRef"
20 :auto-upload="false"
21 :limit="1"
22 accept=".torrent"
23 :on-change="handleTorrentChange"
24 :on-remove="handleTorrentRemove"
25 :before-upload="beforeTorrentUpload"
26 drag
27 class="torrent-upload"
28 >
29 <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
30 <div class="el-upload__text">
31 将 .torrent 文件拖到此处,或<em>点击上传</em>
32 </div>
33 <template #tip>
34 <div class="el-upload__tip">
35 只能上传 .torrent 文件,且不超过 10MB
36 </div>
37 </template>
38 </el-upload>
39 </el-form-item>
40
41 <!-- 基本信息 -->
42 <el-form-item label="资源标题" prop="title" required>
43 <el-input
44 v-model="uploadForm.title"
45 placeholder="请输入资源标题"
46 maxlength="200"
47 show-word-limit
48 />
49 </el-form-item>
50
51 <el-form-item label="资源分类" prop="category" required>
52 <el-select v-model="uploadForm.category" placeholder="请选择分类">
53 <el-option label="电影" value="movie" />
54 <el-option label="电视剧" value="tv" />
55 <el-option label="音乐" value="music" />
56 <el-option label="软件" value="software" />
57 <el-option label="游戏" value="game" />
58 <el-option label="电子书" value="ebook" />
59 <el-option label="其他" value="other" />
60 </el-select>
61 </el-form-item>
62
63 <el-form-item label="子分类" prop="subcategory">
64 <el-select v-model="uploadForm.subcategory" placeholder="请选择子分类">
65 <el-option
66 v-for="sub in getSubcategories(uploadForm.category)"
67 :key="sub.value"
68 :label="sub.label"
69 :value="sub.value"
70 />
71 </el-select>
72 </el-form-item>
73
74 <!-- 详细描述 -->
75 <el-form-item label="资源描述" prop="description">
76 <el-input
77 v-model="uploadForm.description"
78 type="textarea"
79 :rows="6"
80 placeholder="请详细描述资源内容,包括格式、质量、语言等信息"
81 maxlength="2000"
82 show-word-limit
83 />
84 </el-form-item>
85
86 <!-- 标签 -->
87 <el-form-item label="标签">
88 <div class="tags-input">
89 <el-tag
90 v-for="tag in uploadForm.tags"
91 :key="tag"
92 closable
93 @close="removeTag(tag)"
94 class="tag-item"
95 >
96 {{ tag }}
97 </el-tag>
98 <el-input
99 v-if="tagInputVisible"
100 ref="tagInputRef"
101 v-model="tagInputValue"
102 size="small"
103 @keyup.enter="addTag"
104 @blur="addTag"
105 class="tag-input"
106 />
107 <el-button
108 v-else
109 size="small"
110 @click="showTagInput"
111 class="add-tag-btn"
112 >
113 + 添加标签
114 </el-button>
115 </div>
116 </el-form-item>
117
118 <!-- 封面图片 -->
119 <el-form-item label="封面图片">
120 <el-upload
121 ref="imageUploadRef"
122 :auto-upload="false"
123 :limit="1"
124 accept="image/*"
125 :on-change="handleImageChange"
126 :on-remove="handleImageRemove"
127 list-type="picture-card"
128 class="image-upload"
129 >
130 <el-icon><Plus /></el-icon>
131 <template #tip>
132 <div class="el-upload__tip">
133 支持 JPG、PNG 格式,建议尺寸 300x400,不超过 5MB
134 </div>
135 </template>
136 </el-upload>
137 </el-form-item>
138
139 <!-- 高级选项 -->
140 <el-form-item>
141 <el-collapse>
142 <el-collapse-item title="高级选项" name="advanced">
143 <el-form-item label="免费时间">
144 <el-select v-model="uploadForm.freeTime" placeholder="选择免费时间">
145 <el-option label="永久免费" value="forever" />
146 <el-option label="24小时" value="24h" />
147 <el-option label="48小时" value="48h" />
148 <el-option label="7天" value="7d" />
149 <el-option label="30天" value="30d" />
150 </el-select>
151 </el-form-item>
152
153 <el-form-item label="匿名上传">
154 <el-switch v-model="uploadForm.anonymous" />
155 <span class="form-tip">开启后将不显示上传者信息</span>
156 </el-form-item>
157
158 <el-form-item label="允许HR">
159 <el-switch v-model="uploadForm.allowHR" />
160 <span class="form-tip">允许此种子参与HR考核</span>
161 </el-form-item>
162 </el-collapse-item>
163 </el-collapse>
164 </el-form-item>
165
166 <!-- 提交按钮 -->
167 <el-form-item>
168 <div class="submit-buttons">
169 <el-button @click="resetForm">重置</el-button>
170 <el-button type="primary" @click="submitForm" :loading="uploading">
171 {{ uploading ? '上传中...' : '提交种子' }}
172 </el-button>
173 </div>
174 </el-form-item>
175 </el-form>
176 </div>
177 </div>
178</template>
179
180<script>
181import { ref, reactive, nextTick } from 'vue'
182import { useRouter } from 'vue-router'
183import { ElMessage } from 'element-plus'
184import {
185 UploadFilled,
186 Plus
187} from '@element-plus/icons-vue'
188
189export default {
190 name: 'UploadView',
191 setup() {
192 const router = useRouter()
193 const uploadFormRef = ref(null)
194 const torrentUploadRef = ref(null)
195 const imageUploadRef = ref(null)
196 const tagInputRef = ref(null)
197
198 const uploading = ref(false)
199 const tagInputVisible = ref(false)
200 const tagInputValue = ref('')
201
202 const uploadForm = reactive({
203 torrentFile: null,
204 title: '',
205 category: '',
206 subcategory: '',
207 description: '',
208 tags: [],
209 coverImage: null,
210 freeTime: '',
211 anonymous: false,
212 allowHR: true
213 })
214
215 const formRules = {
216 title: [
217 { required: true, message: '请输入资源标题', trigger: 'blur' },
218 { min: 5, max: 200, message: '标题长度在 5 到 200 个字符', trigger: 'blur' }
219 ],
220 category: [
221 { required: true, message: '请选择资源分类', trigger: 'change' }
222 ],
223 description: [
224 { min: 10, max: 2000, message: '描述长度在 10 到 2000 个字符', trigger: 'blur' }
225 ]
226 }
227
228 const subcategories = {
229 movie: [
230 { label: '动作片', value: 'action' },
231 { label: '喜剧片', value: 'comedy' },
232 { label: '科幻片', value: 'scifi' },
233 { label: '恐怖片', value: 'horror' },
234 { label: '剧情片', value: 'drama' }
235 ],
236 tv: [
237 { label: '美剧', value: 'us' },
238 { label: '国产剧', value: 'cn' },
239 { label: '日韩剧', value: 'asia' },
240 { label: '英剧', value: 'uk' },
241 { label: '纪录片', value: 'documentary' }
242 ],
243 music: [
244 { label: '流行音乐', value: 'pop' },
245 { label: '古典音乐', value: 'classical' },
246 { label: '摇滚音乐', value: 'rock' },
247 { label: '电子音乐', value: 'electronic' },
248 { label: '其他', value: 'other' }
249 ],
250 software: [
251 { label: '操作系统', value: 'os' },
252 { label: '办公软件', value: 'office' },
253 { label: '开发工具', value: 'dev' },
254 { label: '设计软件', value: 'design' },
255 { label: '其他', value: 'other' }
256 ],
257 game: [
258 { label: 'PC游戏', value: 'pc' },
259 { label: '主机游戏', value: 'console' },
260 { label: '手机游戏', value: 'mobile' },
261 { label: '其他', value: 'other' }
262 ]
263 }
264
265 const getSubcategories = (category) => {
266 return subcategories[category] || []
267 }
268
269 const handleTorrentChange = (file) => {
270 uploadForm.torrentFile = file.raw
271 // 这里可以解析torrent文件获取基本信息
272 parseTorrentFile(file.raw)
273 }
274
275 const handleTorrentRemove = () => {
276 uploadForm.torrentFile = null
277 }
278
279 const beforeTorrentUpload = (file) => {
280 const isTorrent = file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent')
281 const isLt10M = file.size / 1024 / 1024 < 10
282
283 if (!isTorrent) {
284 ElMessage.error('只能上传 .torrent 文件!')
285 return false
286 }
287 if (!isLt10M) {
288 ElMessage.error('种子文件大小不能超过 10MB!')
289 return false
290 }
291 return true
292 }
293
294 const parseTorrentFile = (file) => {
295 // 这里应该实现torrent文件解析
296 // 可以使用 parse-torrent 库
297 console.log('解析种子文件:', file.name)
298
299 // 模拟解析结果自动填入表单
300 if (!uploadForm.title) {
301 uploadForm.title = file.name.replace('.torrent', '')
302 }
303 }
304
305 const handleImageChange = (file) => {
306 uploadForm.coverImage = file.raw
307 }
308
309 const handleImageRemove = () => {
310 uploadForm.coverImage = null
311 }
312
313 const showTagInput = () => {
314 tagInputVisible.value = true
315 nextTick(() => {
316 tagInputRef.value?.focus()
317 })
318 }
319
320 const addTag = () => {
321 const tag = tagInputValue.value.trim()
322 if (tag && !uploadForm.tags.includes(tag)) {
323 uploadForm.tags.push(tag)
324 }
325 tagInputVisible.value = false
326 tagInputValue.value = ''
327 }
328
329 const removeTag = (tag) => {
330 const index = uploadForm.tags.indexOf(tag)
331 if (index > -1) {
332 uploadForm.tags.splice(index, 1)
333 }
334 }
335
336 const submitForm = async () => {
337 if (!uploadForm.torrentFile) {
338 ElMessage.error('请上传种子文件')
339 return
340 }
341
342 try {
343 await uploadFormRef.value?.validate()
344
345 uploading.value = true
346
347 // 模拟上传过程
348 await new Promise(resolve => setTimeout(resolve, 2000))
349
350 ElMessage.success('种子上传成功!')
351 router.push('/torrents')
352
353 } catch (error) {
354 console.error('表单验证失败:', error)
355 } finally {
356 uploading.value = false
357 }
358 }
359
360 const resetForm = () => {
361 uploadFormRef.value?.resetFields()
362 uploadForm.torrentFile = null
363 uploadForm.coverImage = null
364 uploadForm.tags = []
365 torrentUploadRef.value?.clearFiles()
366 imageUploadRef.value?.clearFiles()
367 }
368
369 return {
370 uploadFormRef,
371 torrentUploadRef,
372 imageUploadRef,
373 tagInputRef,
374 uploading,
375 tagInputVisible,
376 tagInputValue,
377 uploadForm,
378 formRules,
379 getSubcategories,
380 handleTorrentChange,
381 handleTorrentRemove,
382 beforeTorrentUpload,
383 handleImageChange,
384 handleImageRemove,
385 showTagInput,
386 addTag,
387 removeTag,
388 submitForm,
389 resetForm,
390 UploadFilled,
391 Plus
392 }
393 }
394}
395</script>
396
397<style lang="scss" scoped>
398.upload-page {
399 max-width: 800px;
400 margin: 0 auto;
401 padding: 24px;
402}
403
404.page-header {
405 text-align: center;
406 margin-bottom: 32px;
407
408 h1 {
409 font-size: 28px;
410 font-weight: 600;
411 color: #2c3e50;
412 margin: 0 0 8px 0;
413 }
414
415 .page-description {
416 font-size: 16px;
417 color: #7f8c8d;
418 margin: 0;
419 }
420}
421
422.upload-form {
423 background: #fff;
424 border-radius: 12px;
425 padding: 32px;
426 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
427
428 .torrent-upload {
429 width: 100%;
430
431 :deep(.el-upload-dragger) {
432 width: 100%;
433 height: 180px;
434 border: 2px dashed #d9d9d9;
435 border-radius: 8px;
436
437 &:hover {
438 border-color: #409eff;
439 }
440 }
441 }
442
443 .tags-input {
444 display: flex;
445 flex-wrap: wrap;
446 gap: 8px;
447 align-items: center;
448
449 .tag-item {
450 margin: 0;
451 }
452
453 .tag-input {
454 width: 100px;
455 }
456
457 .add-tag-btn {
458 border: 1px dashed #d9d9d9;
459 color: #999;
460
461 &:hover {
462 border-color: #409eff;
463 color: #409eff;
464 }
465 }
466 }
467
468 .image-upload {
469 :deep(.el-upload--picture-card) {
470 width: 148px;
471 height: 148px;
472 }
473 }
474
475 .form-tip {
476 margin-left: 8px;
477 font-size: 12px;
478 color: #909399;
479 }
480
481 .submit-buttons {
482 display: flex;
483 gap: 16px;
484 justify-content: center;
485 margin-top: 24px;
486 }
487}
488
489@media (max-width: 768px) {
490 .upload-page {
491 padding: 16px;
492 }
493
494 .upload-form {
495 padding: 24px 16px;
496 }
497
498 .submit-buttons {
499 flex-direction: column;
500
501 .el-button {
502 width: 100%;
503 }
504 }
505}
xingjinwend652cc62025-06-04 19:52:19 +0800506</style>