<template> | |
<div class="upload-page"> | |
<Navbar /> | |
<div class="upload-container"> | |
<h2>上传种子</h2> | |
<el-form | |
ref="uploadFormRef" | |
:model="uploadForm" | |
:rules="uploadRules" | |
label-width="120px" | |
class="upload-form" | |
> | |
<!-- 种子文件上传 --> | |
<el-form-item label="种子文件" prop="file"> | |
<el-upload | |
ref="uploadRef" | |
:auto-upload="false" | |
:limit="1" | |
accept=".torrent" | |
:on-change="handleTorrentChange" | |
:on-remove="handleTorrentRemove" | |
:file-list="fileList" | |
> | |
<template #trigger> | |
<el-button type="primary">选择文件</el-button> | |
</template> | |
<template #tip> | |
<div class="el-upload__tip"> | |
只能上传 .torrent 文件 | |
</div> | |
</template> | |
</el-upload> | |
</el-form-item> | |
<!-- 基本信息 --> | |
<el-form-item label="标题" prop="title"> | |
<el-input v-model="uploadForm.title" placeholder="请输入种子标题" /> | |
</el-form-item> | |
<el-form-item label="副标题" prop="subtitle"> | |
<el-input v-model="uploadForm.subtitle" placeholder="请输入副标题(可选)" /> | |
</el-form-item> | |
<el-form-item label="分类" prop="category"> | |
<el-select v-model="uploadForm.category" placeholder="请选择分类"> | |
<el-option | |
v-for="category in categories" | |
:key="category.id" | |
:label="category.name" | |
:value="category.slug" | |
/> | |
</el-select> | |
</el-form-item> | |
<!-- 标签 --> | |
<el-form-item label="标签" prop="tag"> | |
<el-select | |
v-model="uploadForm.tag" | |
multiple | |
filterable | |
allow-create | |
placeholder="请选择或输入标签" | |
> | |
<el-option | |
v-for="tag in availableTags" | |
:key="tag.id" | |
:label="tag.name" | |
:value="tag.name" | |
/> | |
</el-select> | |
</el-form-item> | |
<!-- 描述信息 --> | |
<el-form-item label="描述" prop="description"> | |
<el-input | |
v-model="uploadForm.description" | |
type="textarea" | |
:rows="6" | |
placeholder="请输入种子描述,支持 Markdown 格式" | |
/> | |
</el-form-item> | |
<!-- 匿名发布 --> | |
<el-form-item> | |
<el-checkbox v-model="uploadForm.anonymous">匿名发布</el-checkbox> | |
</el-form-item> | |
<!-- 提交按钮 --> | |
<el-form-item> | |
<el-button type="primary" @click="submitUpload" :loading="uploading"> | |
上传种子 | |
</el-button> | |
<el-button @click="resetForm">重置表单</el-button> | |
</el-form-item> | |
</el-form> | |
</div> | |
</div> | |
</template> | |
<script> | |
import { ref, reactive, onMounted } from 'vue' | |
import { useRouter } from 'vue-router' | |
import { ElMessage } from 'element-plus' | |
import { uploadTorrent, getCategories, getTags } from '@/api/torrent' | |
import Navbar from '@/components/Navbar.vue' | |
export default { | |
name: 'UploadView', | |
components: { | |
Navbar | |
}, | |
setup() { | |
const router = useRouter() | |
const uploadFormRef = ref(null) | |
const uploadRef = ref(null) | |
const uploading = ref(false) | |
const fileList = ref([]) | |
const uploadForm = reactive({ | |
title: '', | |
subtitle: '', | |
category: '', | |
tag: [], | |
description: '', | |
anonymous: false, | |
file: null | |
}) | |
const uploadRules = { | |
title: [ | |
{ required: true, message: '请输入种子标题', trigger: 'blur' }, | |
{ min: 3, max: 100, message: '标题长度应在 3 到 100 个字符之间', trigger: 'blur' } | |
], | |
category: [ | |
{ required: true, message: '请选择分类', trigger: 'change' } | |
], | |
description: [ | |
{ required: true, message: '请输入种子描述', trigger: 'blur' }, | |
{ min: 10, message: '描述至少需要 10 个字符', trigger: 'blur' } | |
], | |
file: [ | |
{ required: true, message: '请上传种子文件', trigger: 'change' } | |
] | |
} | |
const categories = ref([]) | |
const availableTags = ref([]) | |
// 获取分类列表 | |
const loadCategories = async () => { | |
try { | |
console.log('开始加载分类列表...') | |
console.log('当前token(分类列表):', localStorage.getItem('token')) | |
const response = await getCategories() | |
console.log('分类列表响应:', response) | |
const list = Array.isArray(response) ? response : response.data | |
if (list && list.length > 0) { | |
categories.value = list | |
console.log('分类列表加载成功:', categories.value) | |
} else { | |
console.warn('分类列表数据为空') | |
categories.value = [] | |
} | |
} catch (error) { | |
console.error('Failed to load categories:', error) | |
console.error('错误详情:', { | |
message: error.message, | |
response: error.response?.data, | |
status: error.response?.status, | |
config: error.config | |
}) | |
// 根据错误类型显示不同的提示 | |
if (error.response?.status === 401) { | |
ElMessage.error('请先登录') | |
router.push('/login') | |
} else if (error.response?.status === 403) { | |
ElMessage.error('没有权限访问分类列表') | |
} else if (error.code === 'ERR_NETWORK') { | |
ElMessage.error('无法连接到服务器,请检查后端服务是否启动') | |
} else { | |
ElMessage.error(`获取分类列表失败: ${error.message}`) | |
} | |
} | |
} | |
// 获取标签列表 | |
const loadTags = async () => { | |
try { | |
console.log('开始加载标签列表...') | |
const response = await getTags() | |
console.log('标签列表响应:', response) | |
if (response && response.data) { | |
availableTags.value = response.data | |
console.log('标签列表加载成功:', availableTags.value) | |
} else { | |
console.warn('标签列表数据为空') | |
availableTags.value = [] | |
} | |
} catch (error) { | |
console.error('Failed to load tags:', error) | |
console.error('错误详情:', { | |
message: error.message, | |
response: error.response?.data, | |
status: error.response?.status | |
}) | |
// 如果是网络错误,提供更详细的提示 | |
if (error.code === 'ERR_NETWORK') { | |
ElMessage.error('无法连接到服务器,请检查后端服务是否启动') | |
} else { | |
ElMessage.error(`获取标签列表失败: ${error.message}`) | |
} | |
} | |
} | |
const handleTorrentChange = (file) => { | |
if (file) { | |
uploadForm.file = file.raw | |
fileList.value = [file] | |
// 自动校验文件字段 | |
uploadFormRef.value?.validateField('file') | |
} | |
} | |
const handleTorrentRemove = () => { | |
uploadForm.file = null | |
fileList.value = [] | |
} | |
const submitUpload = async () => { | |
if (!uploadForm.file) { | |
ElMessage.warning('请先选择种子文件') | |
return | |
} | |
try { | |
await uploadFormRef.value?.validate() | |
uploading.value = true | |
const formData = new FormData() | |
formData.append('file', uploadForm.file) | |
formData.append('title', uploadForm.title) | |
formData.append('subtitle', uploadForm.subtitle || '') | |
formData.append('category', uploadForm.category) | |
formData.append('description', uploadForm.description) | |
formData.append('anonymous', uploadForm.anonymous) | |
// 处理标签数组 | |
if (uploadForm.tag && uploadForm.tag.length > 0) { | |
uploadForm.tag.forEach(tag => { | |
formData.append('tag', tag) | |
}) | |
} | |
const response = await uploadTorrent(formData) | |
ElMessage.success('种子上传成功') | |
// 根据后端返回的数据跳转到种子详情页 | |
if (response.data && response.data.infoHash) { | |
router.push(`/torrent/${response.data.infoHash}`) | |
} else { | |
router.push('/torrents') // 或者跳转到种子列表页 | |
} | |
} catch (error) { | |
console.error('Failed to upload torrent:', error) | |
// 根据后端返回的错误信息显示不同的提示 | |
let errorMessage = '种子上传失败' | |
if (error.response?.data?.message) { | |
errorMessage = error.response.data.message | |
} else if (error.response?.data?.error) { | |
errorMessage = error.response.data.error | |
} | |
ElMessage.error(errorMessage) | |
} finally { | |
uploading.value = false | |
} | |
} | |
const resetForm = () => { | |
uploadFormRef.value?.resetFields() | |
uploadRef.value?.clearFiles() | |
uploadForm.file = null | |
fileList.value = [] | |
} | |
// 组件挂载时加载数据 | |
onMounted(() => { | |
loadCategories() | |
loadTags() | |
}) | |
return { | |
uploadFormRef, | |
uploadRef, | |
uploading, | |
uploadForm, | |
uploadRules, | |
categories, | |
availableTags, | |
fileList, | |
handleTorrentChange, | |
handleTorrentRemove, | |
submitUpload, | |
resetForm | |
} | |
} | |
} | |
</script> | |
<style lang="scss" scoped> | |
.upload-page { | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 24px; | |
} | |
.upload-container { | |
background: #fff; | |
border-radius: 8px; | |
padding: 24px; | |
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); | |
h2 { | |
margin: 0 0 24px; | |
padding-bottom: 16px; | |
border-bottom: 1px solid #eee; | |
color: #2c3e50; | |
} | |
} | |
.upload-form { | |
.el-upload { | |
width: 100%; | |
} | |
.el-upload__tip { | |
line-height: 1.2; | |
padding: 8px 0; | |
color: #909399; | |
} | |
} | |
@media (max-width: 768px) { | |
.upload-page { | |
padding: 16px; | |
} | |
.upload-container { | |
padding: 16px; | |
} | |
:deep(.el-form-item__label) { | |
float: none; | |
display: block; | |
text-align: left; | |
padding: 0 0 8px; | |
} | |
:deep(.el-form-item__content) { | |
margin-left: 0 !important; | |
} | |
} | |
</style> |