f7c3be1d30
- Created ImageForm.vue as standalone page for add/edit image functionality - Removed dialog-based image form from VmImages.vue - Implemented tagsViewStore for global tab state management - Added automatic tab closing on form cancel/back - Fixed data persistence issue when switching between image edits - Removed quick actions section from ImageForm - Updated router configuration for new image form route
836 lines
25 KiB
Vue
836 lines
25 KiB
Vue
<template>
|
||
<div class="system-file-container">
|
||
<!-- 主容器 -->
|
||
<el-card class="main-container" shadow="never">
|
||
<!-- 搜索和操作栏 -->
|
||
<div class="filter-section">
|
||
<div class="filter-content">
|
||
<el-form :inline="true" :model="queryParams" class="search-form">
|
||
<el-form-item label="关键词筛选">
|
||
<el-input v-model="queryParams.key" placeholder="请输入关键词" clearable style="width: 200px" />
|
||
</el-form-item>
|
||
<el-form-item label="筛选用户">
|
||
<el-input-number v-model="queryParams.user_id" placeholder="请输入用户ID" :controls="false" clearable style="width: 150px" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="handleQuery">
|
||
<el-icon><Search /></el-icon>查询
|
||
</el-button>
|
||
<el-button @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
<div class="action-bar">
|
||
<el-button type="primary" @click="handleUpload">
|
||
<el-icon><Upload /></el-icon>上传文件
|
||
</el-button>
|
||
|
||
<el-button type="success" @click="fetchFileList">
|
||
<el-icon><Refresh/></el-icon>刷新
|
||
</el-button>
|
||
|
||
<el-button type="danger" :disabled="!selectedRows.length" @click="handleBatchDelete">
|
||
<el-icon><Delete /></el-icon>批量删除
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件列表 -->
|
||
<div class="table-section">
|
||
<el-table
|
||
v-loading="loading"
|
||
:data="fileList"
|
||
@selection-change="handleSelectionChange"
|
||
style="width: 100%"
|
||
:header-cell-style="{ background: '#fafafa', color: '#333', fontWeight: 600 }"
|
||
>
|
||
<el-table-column type="selection" width="55" />
|
||
<el-table-column prop="id" label="ID" width="80" />
|
||
<el-table-column prop="realName" label="真实文件名" min-width="200" />
|
||
<el-table-column prop="saveName" label="保存名称" min-width="150" />
|
||
<el-table-column prop="savePath" label="保存路径" min-width="250" show-overflow-tooltip />
|
||
<el-table-column prop="size" label="文件大小" width="120">
|
||
<template #default="{ row }">
|
||
{{ formatFileSize(row.size) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="type" label="文件类型" width="120">
|
||
<template #default="{ row }">
|
||
<el-tag :type="getFileTypeColor(row.type)">
|
||
{{ row.type || '未知' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="userId" label="用户ID" width="100" />
|
||
<el-table-column label="是否公开" width="100">
|
||
<template #default="{ row }">
|
||
<el-tag :type="row.openDow ? 'success' : 'info'">
|
||
{{ row.openDow ? '公开' : '私有' }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="创建时间" width="180">
|
||
<template #default="{ row }">
|
||
{{ formatDate(row.CreatedAt) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="200" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button type="primary" link @click="handleView(row)">查看</el-button>
|
||
<el-button type="success" link @click="handleDownload(row)">下载</el-button>
|
||
<el-button type="warning" link @click="handleEdit(row)">编辑</el-button>
|
||
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<el-pagination
|
||
v-model:current-page="queryParams.page"
|
||
v-model:page-size="queryParams.count"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="total"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
background
|
||
class="pagination"
|
||
/>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- 文件详情对话框 -->
|
||
<el-dialog
|
||
v-model="detailDialogVisible"
|
||
title="文件详情"
|
||
width="700px"
|
||
destroy-on-close
|
||
>
|
||
<div v-if="fileDetail" class="file-detail-container">
|
||
<!-- 文件预览区域 -->
|
||
<div class="file-preview-section">
|
||
<div class="preview-label">文件预览</div>
|
||
<div class="preview-content">
|
||
<el-image
|
||
v-if="isImageFile(fileDetail.type) && fileDetail.url"
|
||
:src="fileDetail.url"
|
||
fit="contain"
|
||
style="max-width: 100%; max-height: 400px; border-radius: 8px;"
|
||
:preview-src-list="[fileDetail.url]"
|
||
:initial-index="0"
|
||
>
|
||
<template #error>
|
||
<div class="image-error">
|
||
<el-icon size="40"><Picture /></el-icon>
|
||
<div>图片加载失败</div>
|
||
</div>
|
||
</template>
|
||
</el-image>
|
||
<div v-else class="file-icon-large">
|
||
<el-icon size="80"><Document /></el-icon>
|
||
<div class="file-type-text">{{ fileDetail.type || '未知类型' }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件信息 -->
|
||
<el-descriptions :column="2" border class="file-info-descriptions">
|
||
<el-descriptions-item label="文件ID" label-align="right">{{ fileDetail.id }}</el-descriptions-item>
|
||
<el-descriptions-item label="用户ID" label-align="right">{{ fileDetail.userId }}</el-descriptions-item>
|
||
<el-descriptions-item label="真实文件名" label-align="right" :span="2">{{ fileDetail.realName }}</el-descriptions-item>
|
||
<el-descriptions-item label="保存名称" label-align="right">{{ fileDetail.saveName }}</el-descriptions-item>
|
||
<el-descriptions-item label="文件类型" label-align="right">
|
||
<el-tag :type="getFileTypeColor(fileDetail.type)">{{ fileDetail.type || '未知' }}</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="文件大小" label-align="right">{{ formatFileSize(fileDetail.size) }}</el-descriptions-item>
|
||
<el-descriptions-item label="是否公开" label-align="right">
|
||
<el-tag :type="fileDetail.openDow ? 'success' : 'info'">
|
||
{{ fileDetail.openDow ? '公开访问' : '私有' }}
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="保存路径" label-align="right" :span="2">
|
||
<span class="file-path">{{ fileDetail.savePath }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="文件URL" label-align="right" :span="2">
|
||
<el-link :href="fileDetail.url" target="_blank" type="primary" v-if="fileDetail.url">
|
||
点击查看文件
|
||
</el-link>
|
||
<span v-else style="color: #909399;">无URL</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="创建时间" label-align="right">{{ formatDate(fileDetail.CreatedAt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="更新时间" label-align="right">{{ formatDate(fileDetail.UpdatedAt) }}</el-descriptions-item>
|
||
<el-descriptions-item label="备注" label-align="right" :span="2">{{ fileDetail.content || '无' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
</div>
|
||
</el-dialog>
|
||
|
||
<!-- 文件编辑对话框 -->
|
||
<el-dialog
|
||
v-model="editDialogVisible"
|
||
title="编辑文件信息"
|
||
width="500px"
|
||
>
|
||
<el-form
|
||
ref="fileFormRef"
|
||
:model="fileForm"
|
||
:rules="fileRules"
|
||
label-width="120px"
|
||
>
|
||
<el-form-item label="文件ID">
|
||
<el-input v-model="fileForm.file_id" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="用户ID" prop="user_id">
|
||
<el-input-number v-model="fileForm.user_id" placeholder="请输入用户ID" :controls="false" style="width: 100%" />
|
||
</el-form-item>
|
||
<el-form-item label="是否允许公开">
|
||
<el-switch v-model="fileForm.open_dow" />
|
||
<span style="margin-left: 10px; color: #909399; font-size: 12px;">
|
||
开启后允许公开访问
|
||
</span>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="submitEditForm">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 文件上传对话框 -->
|
||
<el-dialog
|
||
v-model="uploadDialogVisible"
|
||
title="上传文件"
|
||
width="600px"
|
||
>
|
||
<el-form
|
||
ref="uploadFormRef"
|
||
:model="uploadForm"
|
||
label-width="120px"
|
||
>
|
||
<el-form-item label="上传类型" prop="update_type">
|
||
<el-select v-model="uploadForm.update_type" placeholder="请选择上传类型" style="width: 100%">
|
||
<el-option label="工单文件" value="work_order" />
|
||
<el-option label="封面" value="cover" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="是否开放下载">
|
||
<el-switch v-model="uploadForm.open_down" />
|
||
<span style="margin-left: 10px; color: #909399; font-size: 12px;">
|
||
开启后允许公开下载
|
||
</span>
|
||
</el-form-item>
|
||
<el-form-item label="上传文件">
|
||
<el-upload
|
||
ref="uploadRef"
|
||
:http-request="handleCustomUpload"
|
||
:on-success="handleUploadSuccess"
|
||
:on-error="handleUploadError"
|
||
:on-remove="handleRemoveFile"
|
||
:on-change="handleFileChange"
|
||
:before-upload="beforeUpload"
|
||
:file-list="uploadFileList"
|
||
:auto-upload="false"
|
||
drag
|
||
multiple
|
||
>
|
||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||
<div class="el-upload__text">
|
||
将文件拖到此处,或<em>点击上传</em>
|
||
</div>
|
||
<template #tip>
|
||
<div class="el-upload__tip">
|
||
支持 jpg/png/gif/pdf/doc/docx 文件,且不超过 10MB
|
||
</div>
|
||
</template>
|
||
</el-upload>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="handleCloseUpload">取消</el-button>
|
||
<el-button type="primary" @click="handleSubmitUpload">确定上传</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { Upload, Delete, Search, Document, VideoPlay, Folder, UploadFilled, Picture, Refresh } from '@element-plus/icons-vue'
|
||
import { getFileList, getFileDetail, updateFile, deleteFile, uploadFile } from '@/api/admin/file'
|
||
|
||
// 查询参数
|
||
const queryParams = reactive({
|
||
key: '',
|
||
user_id: undefined,
|
||
page: 1,
|
||
count: 10
|
||
})
|
||
|
||
// 文件表单
|
||
const fileForm = reactive({
|
||
file_id: undefined,
|
||
user_id: undefined,
|
||
open_dow: false
|
||
})
|
||
|
||
const fileRules = {
|
||
user_id: [
|
||
{ required: true, message: '请输入用户ID', trigger: 'blur' }
|
||
]
|
||
}
|
||
|
||
// 状态数据
|
||
const loading = ref(false)
|
||
const fileList = ref([])
|
||
const fileDetail = ref(null)
|
||
const total = ref(0)
|
||
const selectedRows = ref([])
|
||
const detailDialogVisible = ref(false)
|
||
const editDialogVisible = ref(false)
|
||
const uploadDialogVisible = ref(false)
|
||
const fileFormRef = ref(null)
|
||
const uploadRef = ref(null)
|
||
const uploadFormRef = ref(null)
|
||
|
||
// 上传表单
|
||
const uploadForm = reactive({
|
||
update_type: 'work_order',
|
||
open_down: false
|
||
})
|
||
|
||
// 上传文件列表
|
||
const uploadFileList = ref([])
|
||
|
||
// 判断是否为图片文件
|
||
const isImageFile = (type) => {
|
||
const imageTypes = ['cover', 'image', 'avatar', 'photo', 'picture']
|
||
return imageTypes.includes(type?.toLowerCase())
|
||
}
|
||
|
||
// 获取文件类型颜色
|
||
const getFileTypeColor = (type) => {
|
||
if (isImageFile(type)) return 'success'
|
||
const colorMap = {
|
||
'document': 'primary',
|
||
'video': 'warning',
|
||
'audio': 'info',
|
||
'file': ''
|
||
}
|
||
return colorMap[type?.toLowerCase()] || 'info'
|
||
}
|
||
|
||
// 格式化日期时间
|
||
const formatDate = (dateString) => {
|
||
if (!dateString) return '-'
|
||
const date = new Date(dateString)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
const hours = String(date.getHours()).padStart(2, '0')
|
||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||
}
|
||
|
||
// 格式化文件大小
|
||
const formatFileSize = (size) => {
|
||
if (size < 1024) return size + ' B'
|
||
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + ' KB'
|
||
if (size < 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(2) + ' MB'
|
||
return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
|
||
}
|
||
|
||
// 获取文件列表
|
||
const fetchFileList = async () => {
|
||
loading.value = true
|
||
try {
|
||
const res = await getFileList(queryParams)
|
||
console.log('文件列表数据:', res.data)
|
||
if (res.data.code === 200) {
|
||
fileList.value = res.data.data.list || []
|
||
total.value = res.data.data.all_count || 0
|
||
}
|
||
} catch (error) {
|
||
console.error('获取文件列表失败:', error)
|
||
ElMessage.error('获取文件列表失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 查询
|
||
const handleQuery = () => {
|
||
queryParams.page = 1
|
||
fetchFileList()
|
||
}
|
||
|
||
// 重置查询
|
||
const resetQuery = () => {
|
||
queryParams.key = ''
|
||
queryParams.user_id = undefined
|
||
queryParams.page = 1
|
||
fetchFileList()
|
||
}
|
||
|
||
// 选择项变化
|
||
const handleSelectionChange = (selection) => {
|
||
selectedRows.value = selection
|
||
}
|
||
|
||
// 分页
|
||
const handleSizeChange = (size) => {
|
||
queryParams.count = size
|
||
fetchFileList()
|
||
}
|
||
|
||
const handleCurrentChange = (page) => {
|
||
queryParams.page = page
|
||
fetchFileList()
|
||
}
|
||
|
||
// 查看文件详情
|
||
const handleView = async (row) => {
|
||
try {
|
||
const res = await getFileDetail({ file_id: row.id })
|
||
console.log('文件详情数据:', res.data)
|
||
if (res.data.code === 200) {
|
||
fileDetail.value = res.data.data.data
|
||
fileDetail.value.url = res.data.data.url
|
||
detailDialogVisible.value = true
|
||
}
|
||
} catch (error) {
|
||
console.error('获取文件详情失败:', error)
|
||
ElMessage.error('获取文件详情失败')
|
||
}
|
||
}
|
||
|
||
// 下载文件
|
||
const handleDownload = async (row) => {
|
||
try {
|
||
// 先获取文件详情以获取完整URL
|
||
const res = await getFileDetail({ file_id: row.id })
|
||
if (res.data.code === 200 && res.data.data.url) {
|
||
const link = document.createElement('a')
|
||
link.href = res.data.data.url
|
||
link.download = row.realName
|
||
link.target = '_blank'
|
||
link.click()
|
||
ElMessage.success('开始下载文件')
|
||
} else {
|
||
ElMessage.error('获取文件下载链接失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('下载文件失败:', error)
|
||
ElMessage.error('下载文件失败')
|
||
}
|
||
}
|
||
|
||
// 编辑文件
|
||
const handleEdit = (row) => {
|
||
Object.assign(fileForm, {
|
||
file_id: row.id,
|
||
user_id: row.userId || undefined,
|
||
open_dow: row.openDow || false
|
||
})
|
||
editDialogVisible.value = true
|
||
}
|
||
|
||
// 删除文件
|
||
const handleDelete = (row) => {
|
||
ElMessageBox.confirm(`确认删除文件 ${row.realName} 吗?`, '警告', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(async () => {
|
||
try {
|
||
const res = await deleteFile({ file_id: row.id })
|
||
console.log('删除文件响应:', res.data)
|
||
if (res.data.code === 200) {
|
||
ElMessage.success('删除成功')
|
||
fetchFileList()
|
||
}
|
||
} catch (error) {
|
||
console.error('删除失败:', error)
|
||
ElMessage.error(error.response?.data?.message || '删除失败')
|
||
}
|
||
}).catch(() => {})
|
||
}
|
||
|
||
// 批量删除
|
||
const handleBatchDelete = () => {
|
||
console.log("批量选择的值:",selectedRows.value)
|
||
if (selectedRows.value.length === 0) {
|
||
ElMessage.warning('请至少选择一条记录')
|
||
return
|
||
}
|
||
ElMessageBox.confirm(`确认删除选中的 ${selectedRows.value.length} 条记录吗?`, '警告', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(async() => {
|
||
try{
|
||
|
||
const deleteMap = selectedRows.value.map(f => deleteFile({file_id:f.id}))
|
||
//等待所有删除完毕
|
||
await Promise.all(deleteMap)
|
||
ElMessage.success('批量删除成功')
|
||
//刷新文件列表
|
||
fetchFileList()
|
||
}catch(error){
|
||
console.error('删除失败:', error)
|
||
ElMessage.error(error.response?.data?.message || '删除失败')
|
||
}
|
||
|
||
}).catch(() => {})
|
||
}
|
||
|
||
// 上传文件
|
||
const handleUpload = () => {
|
||
uploadForm.update_type = 'work_order'
|
||
uploadForm.open_down = false
|
||
uploadFileList.value = []
|
||
uploadDialogVisible.value = true
|
||
}
|
||
|
||
// 关闭上传对话框
|
||
const handleCloseUpload = () => {
|
||
uploadDialogVisible.value = false
|
||
uploadFileList.value = []
|
||
}
|
||
|
||
// 文件列表变化
|
||
const handleFileChange = (file, fileList) => {
|
||
console.log('文件列表变化:', file, fileList)
|
||
uploadFileList.value = fileList
|
||
}
|
||
|
||
// 移除文件
|
||
const handleRemoveFile = (file, fileList) => {
|
||
console.log('移除文件:', file, fileList)
|
||
uploadFileList.value = fileList
|
||
}
|
||
|
||
// 提交上传
|
||
const handleSubmitUpload = () => {
|
||
if (uploadFileList.value.length === 0) {
|
||
ElMessage.warning('请至少选择一个文件')
|
||
return
|
||
}
|
||
// 触发所有待上传文件的上传
|
||
const filesToUpload = uploadFileList.value.filter(file =>
|
||
file.status !== 'success' && file.status !== 'uploading'
|
||
)
|
||
if (filesToUpload.length === 0) {
|
||
ElMessage.info('所有文件已上传完成')
|
||
return
|
||
}
|
||
// 逐个提交文件
|
||
uploadRef.value?.submit()
|
||
|
||
}
|
||
|
||
// 上传前检查(只做提示,不阻止文件添加到列表)
|
||
const beforeUpload = (file) => {
|
||
const isValidType = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type)
|
||
const isLt10M = file.size / 1024 / 1024 < 10
|
||
console.log('beforeUpload', file)
|
||
|
||
if (!isValidType) {
|
||
ElMessage.warning(`文件 ${file.name} 格式不符合要求(仅支持 JPG/PNG/GIF/PDF/DOC/DOCX)`)
|
||
}
|
||
if (!isLt10M) {
|
||
ElMessage.warning(`文件 ${file.name} 大小超过 10MB`)
|
||
}
|
||
// 允许文件添加到列表,在上传时再进行验证
|
||
return true
|
||
}
|
||
|
||
// 自定义上传方法
|
||
const handleCustomUpload = async (options) => {
|
||
const { file, onSuccess, onError } = options
|
||
console.log('开始上传文件:', file)
|
||
|
||
// 在上传前进行验证
|
||
const isValidType = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'].includes(file.type)
|
||
const isLt10M = file.size / 1024 / 1024 < 10
|
||
|
||
if (!isValidType) {
|
||
const error = new Error(`文件 ${file.name} 格式不符合要求(仅支持 JPG/PNG/GIF/PDF/DOC/DOCX)`)
|
||
// 标记为校验类错误,on-error 中不再弹 error 提示
|
||
error.isValidation = true
|
||
onError(error, file)
|
||
return
|
||
}
|
||
if (!isLt10M) {
|
||
const error = new Error(`文件 ${file.name} 大小超过 10MB`)
|
||
error.isValidation = true
|
||
onError(error, file)
|
||
return
|
||
}
|
||
|
||
try {
|
||
const formData = new FormData()
|
||
|
||
// 根据 API 文档,字段名应该是 files(复数)
|
||
formData.append('files', file)
|
||
|
||
// 添加文件名列表(虽然 API 文档说是数组,但实际传递时直接传字符串)
|
||
formData.append('file_names', file.name)
|
||
|
||
// 添加上传类型
|
||
if (uploadForm.update_type) {
|
||
formData.append('update_type', uploadForm.update_type)
|
||
}
|
||
|
||
// 添加是否开放下载
|
||
formData.append('open_down', uploadForm.open_down ? '1' : '0')
|
||
|
||
console.log('上传参数:', {
|
||
files: file.name,
|
||
file_names: [file.name],
|
||
update_type: uploadForm.update_type,
|
||
open_down: uploadForm.open_down
|
||
})
|
||
|
||
const res = await uploadFile(formData)
|
||
console.log('上传响应:', res)
|
||
|
||
// 根据返回码严格区分成功和失败
|
||
if (res && res.data && res.data.code === 200) {
|
||
onSuccess(res.data.data, file)
|
||
} else {
|
||
const errorMsg = res?.data?.message || res?.data?.msg || '上传失败'
|
||
const error = new Error(errorMsg)
|
||
onError(error, file)
|
||
}
|
||
} catch (error) {
|
||
console.error('上传文件失败:', error)
|
||
const err = new Error(error?.response?.data?.message || error?.message || '上传失败')
|
||
onError(err, file)
|
||
}
|
||
}
|
||
|
||
// 上传成功
|
||
const handleUploadSuccess = (response, file, fileList) => {
|
||
console.log('上传成功文件:', file)
|
||
console.log('上传成功文件列表:',fileList)
|
||
|
||
|
||
// 成功回调只会在 code === 200 时触发
|
||
// ElMessage.success(`文件 ${file.name} 上传成功`)
|
||
// 更新文件列表状态
|
||
uploadFileList.value = fileList
|
||
// 如果所有文件都上传成功,关闭对话框并刷新列表
|
||
const allSuccess = fileList.every(f => f.status === 'success')
|
||
const uploadList = fileList.some(f => f.status === 'uploading')
|
||
if (allSuccess && !uploadList && fileList.length > 0) {
|
||
ElMessage.success(`已成功上传${fileList.length}个文件`)
|
||
setTimeout(() => {
|
||
uploadDialogVisible.value = false
|
||
uploadFileList.value = []
|
||
fetchFileList()
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
// 上传失败
|
||
const handleUploadError = (error, file, fileList) => {
|
||
console.error('上传失败:', error, file, fileList)
|
||
// 对校验类错误仅在 beforeUpload 中提示过一次 warning,这里不再重复报错
|
||
if (error?.isValidation) return
|
||
ElMessage.error(error?.message || '上传失败,请检查网络连接或联系管理员')
|
||
}
|
||
|
||
// 提交编辑表单
|
||
const submitEditForm = () => {
|
||
fileFormRef.value?.validate(async (valid) => {
|
||
if (valid) {
|
||
try {
|
||
const submitData = {
|
||
file_id: fileForm.file_id,
|
||
user_id: Number(fileForm.user_id),
|
||
open_dow: fileForm.open_dow
|
||
}
|
||
console.log('提交文件信息数据:', submitData)
|
||
const res = await updateFile(submitData)
|
||
if (res.data.code === 200) {
|
||
ElMessage.success('修改成功')
|
||
editDialogVisible.value = false
|
||
fetchFileList()
|
||
}
|
||
} catch (error) {
|
||
console.error('修改失败:', error)
|
||
ElMessage.error(error.response?.data?.message || '修改失败')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 初始化
|
||
onMounted(() => {
|
||
fetchFileList()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.system-file-container {
|
||
padding: 0;
|
||
}
|
||
|
||
.main-container {
|
||
border: 1px solid #e1e8ed;
|
||
background: #ffffff;
|
||
}
|
||
|
||
.filter-section {
|
||
padding: 0;
|
||
border-bottom: 1px solid #e1e8ed;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
.filter-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.search-form {
|
||
margin: 0;
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.search-form :deep(.el-form-item) {
|
||
margin-bottom: 0;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
gap: 12px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.table-section {
|
||
padding: 0;
|
||
}
|
||
|
||
.pagination {
|
||
margin-top: 20px;
|
||
padding: 16px 20px;
|
||
border-top: 1px solid #e1e8ed;
|
||
background: #fafbfc;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.file-icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 60px;
|
||
height: 60px;
|
||
background-color: #f5f7fa;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.file-detail-container {
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.file-preview-section {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.preview-label {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.preview-content {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background-color: #f5f7fa;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
min-height: 200px;
|
||
}
|
||
|
||
.file-icon-large {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #909399;
|
||
gap: 12px;
|
||
}
|
||
|
||
.file-type-text {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
}
|
||
|
||
.image-error {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #909399;
|
||
gap: 8px;
|
||
}
|
||
|
||
.file-info-descriptions {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.file-path {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 12px;
|
||
color: #606266;
|
||
word-break: break-all;
|
||
}
|
||
|
||
:deep(.el-descriptions__label) {
|
||
width: 120px;
|
||
}
|
||
|
||
/* 表格样式优化 */
|
||
:deep(.el-table) {
|
||
border: none;
|
||
color: #2c3e50;
|
||
}
|
||
|
||
:deep(.el-table__header) {
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
:deep(.el-table th) {
|
||
background: #f8f9fa !important;
|
||
border-bottom: 2px solid #e1e8ed;
|
||
color: #2c3e50;
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
}
|
||
|
||
:deep(.el-table td) {
|
||
border-bottom: 1px solid #f0f2f5;
|
||
color: #34495e;
|
||
}
|
||
|
||
:deep(.el-table tr:hover > td) {
|
||
background-color: #f8f9fa !important;
|
||
}
|
||
|
||
:deep(.el-card__body) {
|
||
padding: 0;
|
||
}
|
||
</style>
|