Files
template-nuxt4/app/pages/admin/downloads.vue
2026-04-29 01:33:33 +08:00

245 lines
8.0 KiB
Vue

<template>
<div class="admin-downloads">
<div class="toolbar">
<div class="toolbar-left">
<h3 class="page-title">资料下载管理</h3>
<a-tag> {{ total }} 个文件</a-tag>
</div>
<a-button type="primary" @click="handleAdd">
<template #icon><PlusOutlined /></template>
上传文件
</a-button>
</div>
<!-- 分类筛选 -->
<div class="filter-bar">
<a-radio-group v-model:value="activeCategory" button-style="solid" @change="loadData">
<a-radio-button value="">全部</a-radio-button>
<a-radio-button value="form">申请表格</a-radio-button>
<a-radio-button value="report">研究报告</a-radio-button>
<a-radio-button value="policy">政策文件</a-radio-button>
<a-radio-button value="other">其他</a-radio-button>
</a-radio-group>
</div>
<div class="table-card">
<a-table
:columns="columns"
:data-source="dataSource"
:loading="loading"
:pagination="{ total, pageSize: 15, showTotal: (t: number) => `共 ${t} 条` }"
row-key="id"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'fileName'">
<div class="file-info">
<span class="file-icon">{{ getFileIcon(record.fileType) }}</span>
<span class="file-name">{{ record.fileName }}</span>
</div>
</template>
<template v-if="column.key === 'category'">
<a-tag>{{ getCategoryLabel(record.category) }}</a-tag>
</template>
<template v-if="column.key === 'memberOnly'">
<a-tag :color="record.memberOnly ? 'blue' : 'default'">
{{ record.memberOnly ? '会员专享' : '公开' }}
</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a-button size="small" @click="handleEdit(record)">编辑</a-button>
<a-button size="small" @click="previewFile(record)">预览</a-button>
<a-popconfirm title="确定删除此文件?" @confirm="handleDelete(record)">
<a-button danger size="small">删除</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
</div>
<!-- 上传/编辑弹窗 -->
<a-modal
v-model:open="modalVisible"
:confirm-loading="saving"
:title="editingRecord ? '编辑文件信息' : '上传文件'"
width="560px"
@ok="handleSave"
>
<a-form :model="formData" layout="vertical">
<a-form-item v-if="!editingRecord" label="文件">
<a-upload
v-model:file-list="fileList"
:before-upload="() => false"
:max-count="1"
>
<a-button><template #icon>📎</template>选择文件</a-button>
</a-upload>
</a-form-item>
<a-form-item label="显示名称" required>
<a-input v-model:value="formData.fileName" placeholder="请输入文件显示名称" />
</a-form-item>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="文件分类">
<a-select v-model:value="formData.category">
<a-select-option value="form">申请表格</a-select-option>
<a-select-option value="report">研究报告</a-select-option>
<a-select-option value="policy">政策文件</a-select-option>
<a-select-option value="other">其他</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="访问权限">
<a-radio-group v-model:value="formData.memberOnly">
<a-radio :value="false">公开</a-radio>
<a-radio :value="true">会员专享</a-radio>
</a-radio-group>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="文件描述">
<a-textarea v-model:value="formData.description" :rows="3" placeholder="请输入文件描述" />
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script lang="ts" setup>
import { message } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
definePageMeta({ layout: 'admin' })
useHead({ title: '资料下载管理' })
const loading = ref(false)
const saving = ref(false)
const modalVisible = ref(false)
const editingRecord = ref<any>(null)
const fileList = ref<any[]>([])
const activeCategory = ref('')
const total = ref(0)
const formData = reactive({
fileName: '',
category: 'form',
memberOnly: false,
description: '',
})
const columns = [
{ title: '文件名称', key: 'fileName' },
{ title: '分类', key: 'category', width: 110 },
{ title: '文件大小', dataIndex: 'fileSize', key: 'fileSize', width: 100 },
{ title: '下载次数', dataIndex: 'downloadCount', key: 'downloadCount', width: 100 },
{ title: '权限', key: 'memberOnly', width: 100 },
{ title: '上传时间', dataIndex: 'uploadTime', key: 'uploadTime', width: 150 },
{ title: '操作', key: 'action', width: 200 },
]
const dataSource = ref([
{ id: 1, fileName: '企业会员入会申请表.docx', fileType: 'docx', category: 'form', fileSize: '35KB', downloadCount: 128, memberOnly: false, uploadTime: '2024-11-01' },
{ id: 2, fileName: '个人会员入会申请表.docx', fileType: 'docx', category: 'form', fileSize: '32KB', downloadCount: 96, memberOnly: false, uploadTime: '2024-11-01' },
{ id: 3, fileName: '专家申请表.docx', fileType: 'docx', category: 'form', fileSize: '40KB', downloadCount: 65, memberOnly: false, uploadTime: '2024-11-01' },
{ id: 4, fileName: '广西经济社会发展研究报告2024.pdf', fileType: 'pdf', category: 'report', fileSize: '2.8MB', downloadCount: 342, memberOnly: true, uploadTime: '2024-12-01' },
{ id: 5, fileName: '广西数字经济政策汇编.pdf', fileType: 'pdf', category: 'policy', fileSize: '1.2MB', downloadCount: 215, memberOnly: false, uploadTime: '2024-11-15' },
])
function getFileIcon(type: string) {
const iconMap: Record<string, string> = { pdf: '📕', docx: '📘', doc: '📘', xlsx: '📗', pptx: '📙', zip: '📦' }
return iconMap[type] || '📄'
}
function getCategoryLabel(cat: string) {
const map: Record<string, string> = { form: '申请表格', report: '研究报告', policy: '政策文件', other: '其他' }
return map[cat] || cat
}
function handleAdd() {
editingRecord.value = null
Object.assign(formData, { fileName: '', category: 'form', memberOnly: false, description: '' })
fileList.value = []
modalVisible.value = true
}
function handleEdit(record: any) {
editingRecord.value = record
Object.assign(formData, { fileName: record.fileName, category: record.category, memberOnly: record.memberOnly, description: record.description || '' })
modalVisible.value = true
}
async function handleSave() {
saving.value = true
try {
message.success(editingRecord.value ? '文件信息已更新' : '文件上传成功')
modalVisible.value = false
loadData()
} finally {
saving.value = false
}
}
async function handleDelete(record: any) {
// TODO: 调用API
message.success('文件已删除')
}
function previewFile(record: any) {
message.info(`预览:${record.fileName}`)
}
async function loadData() {
total.value = dataSource.value.length
}
onMounted(() => {
loadData()
})
</script>
<style scoped>
.admin-downloads { display: flex; flex-direction: column; gap: 16px; }
.toolbar, .filter-bar, .table-card {
background: #fff;
border-radius: 12px;
padding: 16px 20px;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
}
.toolbar-left {
display: flex;
align-items: center;
gap: 12px;
}
.page-title {
font-size: 16px;
font-weight: 700;
color: #1f2937;
margin: 0;
}
.file-info {
display: flex;
align-items: center;
gap: 8px;
}
.file-icon { font-size: 18px; }
.file-name {
font-size: 14px;
color: #1f2937;
font-weight: 500;
}
</style>