| <template> | |
| <div class="upload-page"> | |
| <div class="page-header"> | |
| <h1>上传种子</h1> | |
| <p class="page-description">分享你的资源,为社区做贡献</p> | |
| </div> | |
| <div class="upload-form"> | |
| <el-form | |
| ref="uploadFormRef" | |
| :model="uploadForm" | |
| :rules="formRules" | |
| label-width="120px" | |
| size="large" | |
| > | |
| <!-- 种子文件上传 --> | |
| <el-form-item label="种子文件" prop="torrentFile" required> | |
| <el-upload | |
| ref="torrentUploadRef" | |
| :auto-upload="false" | |
| :limit="1" | |
| accept=".torrent" | |
| :on-change="handleTorrentChange" | |
| :on-remove="handleTorrentRemove" | |
| :before-upload="beforeTorrentUpload" | |
| drag | |
| class="torrent-upload" | |
| > | |
| <el-icon class="el-icon--upload"><UploadFilled /></el-icon> | |
| <div class="el-upload__text"> | |
| 将 .torrent 文件拖到此处,或<em>点击上传</em> | |
| </div> | |
| <template #tip> | |
| <div class="el-upload__tip"> | |
| 只能上传 .torrent 文件,且不超过 10MB | |
| </div> | |
| </template> | |
| </el-upload> | |
| </el-form-item> | |
| <!-- 基本信息 --> | |
| <el-form-item label="资源标题" prop="title" required> | |
| <el-input | |
| v-model="uploadForm.title" | |
| placeholder="请输入资源标题" | |
| maxlength="200" | |
| show-word-limit | |
| /> | |
| </el-form-item> | |
| <el-form-item label="资源分类" prop="category" required> | |
| <el-select v-model="uploadForm.category" placeholder="请选择分类"> | |
| <el-option label="电影" value="movie" /> | |
| <el-option label="电视剧" value="tv" /> | |
| <el-option label="音乐" value="music" /> | |
| <el-option label="软件" value="software" /> | |
| <el-option label="游戏" value="game" /> | |
| <el-option label="电子书" value="ebook" /> | |
| <el-option label="其他" value="other" /> | |
| </el-select> | |
| </el-form-item> | |
| <el-form-item label="子分类" prop="subcategory"> | |
| <el-select v-model="uploadForm.subcategory" placeholder="请选择子分类"> | |
| <el-option | |
| v-for="sub in getSubcategories(uploadForm.category)" | |
| :key="sub.value" | |
| :label="sub.label" | |
| :value="sub.value" | |
| /> | |
| </el-select> | |
| </el-form-item> | |
| <!-- 详细描述 --> | |
| <el-form-item label="资源描述" prop="description"> | |
| <el-input | |
| v-model="uploadForm.description" | |
| type="textarea" | |
| :rows="6" | |
| placeholder="请详细描述资源内容,包括格式、质量、语言等信息" | |
| maxlength="2000" | |
| show-word-limit | |
| /> | |
| </el-form-item> | |
| <!-- 标签 --> | |
| <el-form-item label="标签"> | |
| <div class="tags-input"> | |
| <el-tag | |
| v-for="tag in uploadForm.tags" | |
| :key="tag" | |
| closable | |
| @close="removeTag(tag)" | |
| class="tag-item" | |
| > | |
| {{ tag }} | |
| </el-tag> | |
| <el-input | |
| v-if="tagInputVisible" | |
| ref="tagInputRef" | |
| v-model="tagInputValue" | |
| size="small" | |
| @keyup.enter="addTag" | |
| @blur="addTag" | |
| class="tag-input" | |
| /> | |
| <el-button | |
| v-else | |
| size="small" | |
| @click="showTagInput" | |
| class="add-tag-btn" | |
| > | |
| + 添加标签 | |
| </el-button> | |
| </div> | |
| </el-form-item> | |
| <!-- 封面图片 --> | |
| <el-form-item label="封面图片"> | |
| <el-upload | |
| ref="imageUploadRef" | |
| :auto-upload="false" | |
| :limit="1" | |
| accept="image/*" | |
| :on-change="handleImageChange" | |
| :on-remove="handleImageRemove" | |
| list-type="picture-card" | |
| class="image-upload" | |
| > | |
| <el-icon><Plus /></el-icon> | |
| <template #tip> | |
| <div class="el-upload__tip"> | |
| 支持 JPG、PNG 格式,建议尺寸 300x400,不超过 5MB | |
| </div> | |
| </template> | |
| </el-upload> | |
| </el-form-item> | |
| <!-- 高级选项 --> | |
| <el-form-item> | |
| <el-collapse> | |
| <el-collapse-item title="高级选项" name="advanced"> | |
| <el-form-item label="免费时间"> | |
| <el-select v-model="uploadForm.freeTime" placeholder="选择免费时间"> | |
| <el-option label="永久免费" value="forever" /> | |
| <el-option label="24小时" value="24h" /> | |
| <el-option label="48小时" value="48h" /> | |
| <el-option label="7天" value="7d" /> | |
| <el-option label="30天" value="30d" /> | |
| </el-select> | |
| </el-form-item> | |
| <el-form-item label="匿名上传"> | |
| <el-switch v-model="uploadForm.anonymous" /> | |
| <span class="form-tip">开启后将不显示上传者信息</span> | |
| </el-form-item> | |
| <el-form-item label="允许HR"> | |
| <el-switch v-model="uploadForm.allowHR" /> | |
| <span class="form-tip">允许此种子参与HR考核</span> | |
| </el-form-item> | |
| </el-collapse-item> | |
| </el-collapse> | |
| </el-form-item> | |
| <!-- 提交按钮 --> | |
| <el-form-item> | |
| <div class="submit-buttons"> | |
| <el-button @click="resetForm">重置</el-button> | |
| <el-button type="primary" @click="submitForm" :loading="uploading"> | |
| {{ uploading ? '上传中...' : '提交种子' }} | |
| </el-button> | |
| </div> | |
| </el-form-item> | |
| </el-form> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| import { ref, reactive, nextTick } from 'vue' | |
| import { useRouter } from 'vue-router' | |
| import { ElMessage } from 'element-plus' | |
| import { | |
| UploadFilled, | |
| Plus | |
| } from '@element-plus/icons-vue' | |
| export default { | |
| name: 'UploadView', | |
| setup() { | |
| const router = useRouter() | |
| const uploadFormRef = ref(null) | |
| const torrentUploadRef = ref(null) | |
| const imageUploadRef = ref(null) | |
| const tagInputRef = ref(null) | |
| const uploading = ref(false) | |
| const tagInputVisible = ref(false) | |
| const tagInputValue = ref('') | |
| const uploadForm = reactive({ | |
| torrentFile: null, | |
| title: '', | |
| category: '', | |
| subcategory: '', | |
| description: '', | |
| tags: [], | |
| coverImage: null, | |
| freeTime: '', | |
| anonymous: false, | |
| allowHR: true | |
| }) | |
| const formRules = { | |
| title: [ | |
| { required: true, message: '请输入资源标题', trigger: 'blur' }, | |
| { min: 5, max: 200, message: '标题长度在 5 到 200 个字符', trigger: 'blur' } | |
| ], | |
| category: [ | |
| { required: true, message: '请选择资源分类', trigger: 'change' } | |
| ], | |
| description: [ | |
| { min: 10, max: 2000, message: '描述长度在 10 到 2000 个字符', trigger: 'blur' } | |
| ] | |
| } | |
| const subcategories = { | |
| movie: [ | |
| { label: '动作片', value: 'action' }, | |
| { label: '喜剧片', value: 'comedy' }, | |
| { label: '科幻片', value: 'scifi' }, | |
| { label: '恐怖片', value: 'horror' }, | |
| { label: '剧情片', value: 'drama' } | |
| ], | |
| tv: [ | |
| { label: '美剧', value: 'us' }, | |
| { label: '国产剧', value: 'cn' }, | |
| { label: '日韩剧', value: 'asia' }, | |
| { label: '英剧', value: 'uk' }, | |
| { label: '纪录片', value: 'documentary' } | |
| ], | |
| music: [ | |
| { label: '流行音乐', value: 'pop' }, | |
| { label: '古典音乐', value: 'classical' }, | |
| { label: '摇滚音乐', value: 'rock' }, | |
| { label: '电子音乐', value: 'electronic' }, | |
| { label: '其他', value: 'other' } | |
| ], | |
| software: [ | |
| { label: '操作系统', value: 'os' }, | |
| { label: '办公软件', value: 'office' }, | |
| { label: '开发工具', value: 'dev' }, | |
| { label: '设计软件', value: 'design' }, | |
| { label: '其他', value: 'other' } | |
| ], | |
| game: [ | |
| { label: 'PC游戏', value: 'pc' }, | |
| { label: '主机游戏', value: 'console' }, | |
| { label: '手机游戏', value: 'mobile' }, | |
| { label: '其他', value: 'other' } | |
| ] | |
| } | |
| const getSubcategories = (category) => { | |
| return subcategories[category] || [] | |
| } | |
| const handleTorrentChange = (file) => { | |
| uploadForm.torrentFile = file.raw | |
| // 这里可以解析torrent文件获取基本信息 | |
| parseTorrentFile(file.raw) | |
| } | |
| const handleTorrentRemove = () => { | |
| uploadForm.torrentFile = null | |
| } | |
| const beforeTorrentUpload = (file) => { | |
| const isTorrent = file.type === 'application/x-bittorrent' || file.name.endsWith('.torrent') | |
| const isLt10M = file.size / 1024 / 1024 < 10 | |
| if (!isTorrent) { | |
| ElMessage.error('只能上传 .torrent 文件!') | |
| return false | |
| } | |
| if (!isLt10M) { | |
| ElMessage.error('种子文件大小不能超过 10MB!') | |
| return false | |
| } | |
| return true | |
| } | |
| const parseTorrentFile = (file) => { | |
| // 这里应该实现torrent文件解析 | |
| // 可以使用 parse-torrent 库 | |
| console.log('解析种子文件:', file.name) | |
| // 模拟解析结果自动填入表单 | |
| if (!uploadForm.title) { | |
| uploadForm.title = file.name.replace('.torrent', '') | |
| } | |
| } | |
| const handleImageChange = (file) => { | |
| uploadForm.coverImage = file.raw | |
| } | |
| const handleImageRemove = () => { | |
| uploadForm.coverImage = null | |
| } | |
| const showTagInput = () => { | |
| tagInputVisible.value = true | |
| nextTick(() => { | |
| tagInputRef.value?.focus() | |
| }) | |
| } | |
| const addTag = () => { | |
| const tag = tagInputValue.value.trim() | |
| if (tag && !uploadForm.tags.includes(tag)) { | |
| uploadForm.tags.push(tag) | |
| } | |
| tagInputVisible.value = false | |
| tagInputValue.value = '' | |
| } | |
| const removeTag = (tag) => { | |
| const index = uploadForm.tags.indexOf(tag) | |
| if (index > -1) { | |
| uploadForm.tags.splice(index, 1) | |
| } | |
| } | |
| const submitForm = async () => { | |
| if (!uploadForm.torrentFile) { | |
| ElMessage.error('请上传种子文件') | |
| return | |
| } | |
| try { | |
| await uploadFormRef.value?.validate() | |
| uploading.value = true | |
| // 模拟上传过程 | |
| await new Promise(resolve => setTimeout(resolve, 2000)) | |
| ElMessage.success('种子上传成功!') | |
| router.push('/torrents') | |
| } catch (error) { | |
| console.error('表单验证失败:', error) | |
| } finally { | |
| uploading.value = false | |
| } | |
| } | |
| const resetForm = () => { | |
| uploadFormRef.value?.resetFields() | |
| uploadForm.torrentFile = null | |
| uploadForm.coverImage = null | |
| uploadForm.tags = [] | |
| torrentUploadRef.value?.clearFiles() | |
| imageUploadRef.value?.clearFiles() | |
| } | |
| return { | |
| uploadFormRef, | |
| torrentUploadRef, | |
| imageUploadRef, | |
| tagInputRef, | |
| uploading, | |
| tagInputVisible, | |
| tagInputValue, | |
| uploadForm, | |
| formRules, | |
| getSubcategories, | |
| handleTorrentChange, | |
| handleTorrentRemove, | |
| beforeTorrentUpload, | |
| handleImageChange, | |
| handleImageRemove, | |
| showTagInput, | |
| addTag, | |
| removeTag, | |
| submitForm, | |
| resetForm, | |
| UploadFilled, | |
| Plus | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| .upload-page { | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 24px; | |
| } | |
| .page-header { | |
| text-align: center; | |
| margin-bottom: 32px; | |
| h1 { | |
| font-size: 28px; | |
| font-weight: 600; | |
| color: #2c3e50; | |
| margin: 0 0 8px 0; | |
| } | |
| .page-description { | |
| font-size: 16px; | |
| color: #7f8c8d; | |
| margin: 0; | |
| } | |
| } | |
| .upload-form { | |
| background: #fff; | |
| border-radius: 12px; | |
| padding: 32px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); | |
| .torrent-upload { | |
| width: 100%; | |
| :deep(.el-upload-dragger) { | |
| width: 100%; | |
| height: 180px; | |
| border: 2px dashed #d9d9d9; | |
| border-radius: 8px; | |
| &:hover { | |
| border-color: #409eff; | |
| } | |
| } | |
| } | |
| .tags-input { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| align-items: center; | |
| .tag-item { | |
| margin: 0; | |
| } | |
| .tag-input { | |
| width: 100px; | |
| } | |
| .add-tag-btn { | |
| border: 1px dashed #d9d9d9; | |
| color: #999; | |
| &:hover { | |
| border-color: #409eff; | |
| color: #409eff; | |
| } | |
| } | |
| } | |
| .image-upload { | |
| :deep(.el-upload--picture-card) { | |
| width: 148px; | |
| height: 148px; | |
| } | |
| } | |
| .form-tip { | |
| margin-left: 8px; | |
| font-size: 12px; | |
| color: #909399; | |
| } | |
| .submit-buttons { | |
| display: flex; | |
| gap: 16px; | |
| justify-content: center; | |
| margin-top: 24px; | |
| } | |
| } | |
| @media (max-width: 768px) { | |
| .upload-page { | |
| padding: 16px; | |
| } | |
| .upload-form { | |
| padding: 24px 16px; | |
| } | |
| .submit-buttons { | |
| flex-direction: column; | |
| .el-button { | |
| width: 100%; | |
| } | |
| } | |
| } | |
| </style> |