| <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> |