<template>
|
<el-dialog v-model="dialogVisible" title="附件管理" width="60%" :before-close="handleClose">
|
<div class="attachment-manager">
|
<!-- 上传区域 -->
|
<div class="upload-section">
|
<el-upload
|
ref="uploadRef"
|
:action="uploadUrl"
|
:headers="uploadHeaders"
|
:before-upload="handleBeforeUpload"
|
:on-success="handleUploadSuccess"
|
:on-error="handleUploadError"
|
:on-remove="handleRemove"
|
:file-list="fileList"
|
multiple
|
:limit="10"
|
:show-file-list="false"
|
:data="{documentId: currentDocumentId}"
|
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt,.xml,.jpg,.jpeg,.png,.gif,.bmp,.rar,.zip,.7z"
|
>
|
<el-button type="primary" :icon="Plus">上传附件</el-button>
|
<template #tip>
|
<div class="el-upload__tip">
|
支持格式:doc,docx,xls,xlsx,ppt,pptx,pdf,txt,xml,jpg,jpeg,png,gif,bmp,rar,zip,7z
|
<br>单个文件大小不超过50MB
|
</div>
|
</template>
|
</el-upload>
|
</div>
|
|
<!-- 附件列表 -->
|
<div class="attachment-list">
|
<el-table :data="fileList" border height="400px" v-loading="loading">
|
<el-table-column label="序号" type="index" width="60" align="center" />
|
<el-table-column label="附件名称" prop="name" min-width="200" show-overflow-tooltip />
|
<el-table-column label="文件大小" prop="size" width="100" align="center">
|
<template #default="scope">
|
{{ formatFileSize(scope.row.size) }}
|
</template>
|
</el-table-column>
|
<el-table-column label="上传时间" prop="uploadTime" width="160" align="center">
|
<template #default="scope">
|
{{ formatDate(scope.row.uploadTime) }}
|
</template>
|
</el-table-column>
|
<el-table-column label="状态" prop="status" width="80" align="center">
|
<template #default="scope">
|
<el-tag :type="scope.row.status === 'success' ? 'success' : 'danger'" size="small">
|
{{ scope.row.status === 'success' ? '成功' : '失败' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column fixed="right" label="操作" width="200" align="center">
|
<template #default="scope">
|
<el-button link type="primary" size="small" @click="previewFile(scope.row)">
|
预览
|
</el-button>
|
<el-button link type="primary" size="small" @click="downloadFile(scope.row)">
|
下载
|
</el-button>
|
<el-button link type="danger" size="small" @click="removeFile(scope.row)">
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
</div>
|
|
<!-- 文件预览组件 -->
|
<filePreview ref="filePreviewRef" />
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { ref, reactive, computed } from 'vue'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { Plus } from '@element-plus/icons-vue'
|
import { getToken } from "@/utils/auth"
|
import { addDocumentationFile, getDocumentationFileList, deleteDocumentationFile } from '@/api/fileManagement/document'
|
import filePreview from '@/components/filePreview/index.vue'
|
|
const props = defineProps({
|
// documentId 通过 open 事件传入,不需要作为 props
|
})
|
|
const emit = defineEmits(['update:attachments'])
|
|
const dialogVisible = ref(false)
|
const loading = ref(false)
|
const fileList = ref([])
|
const uploadRef = ref()
|
const filePreviewRef = ref()
|
const currentDocumentId = ref('') // 内部管理当前文档ID
|
|
// 上传配置
|
const uploadUrl = import.meta.env.VITE_APP_BASE_API + "/file/upload"
|
const uploadHeaders = computed(() => ({
|
Authorization: "Bearer " + getToken()
|
}))
|
|
// 打开弹框
|
const open = (attachments = [], documentId = '') => {
|
dialogVisible.value = true
|
currentDocumentId.value = documentId // 设置当前文档ID
|
// 如果有文档ID,则加载附件列表
|
if (documentId) {
|
loadAttachmentList(documentId)
|
} else {
|
fileList.value = attachments || []
|
// total.value = fileList.value.length // Removed total.value
|
}
|
// currentPage.value = 1 // Removed currentPage.value
|
}
|
|
// 加载附件列表
|
const loadAttachmentList = async (documentId) => {
|
try {
|
loading.value = true
|
const params = {
|
page: 1, // Always load from page 1
|
size: 1000, // Load all for now
|
documentationId: documentId
|
}
|
|
const res = await getDocumentationFileList(params)
|
if (res.code === 200) {
|
const records = res.data
|
|
// 转换数据格式
|
fileList.value = records.map(item => ({
|
id: item.id,
|
name: item.name,
|
size: item.fileSize,
|
url: item.url,
|
uploadTime: item.createTime || item.uploadTime,
|
status: 'success',
|
uid: item.id
|
}))
|
|
// total.value = totalCount // Removed total.value
|
} else {
|
ElMessage.error(res.msg || '获取附件列表失败')
|
fileList.value = []
|
// total.value = 0 // Removed total.value
|
}
|
} catch (error) {
|
console.error('获取附件列表失败:', error)
|
ElMessage.error('获取附件列表失败')
|
fileList.value = []
|
// total.value = 0 // Removed total.value
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 关闭弹框
|
const handleClose = () => {
|
dialogVisible.value = false
|
emit('update:attachments', fileList.value)
|
}
|
|
// 文件上传前校验
|
const handleBeforeUpload = (file) => {
|
// 检查文件大小(50MB)
|
const isLt50M = file.size / 1024 / 1024 < 50
|
if (!isLt50M) {
|
ElMessage.error('文件大小不能超过50MB!')
|
return false
|
}
|
|
// 检查文件类型
|
const allowedTypes = [
|
'application/msword',
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
'application/vnd.ms-excel',
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
'application/vnd.ms-powerpoint',
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
'application/pdf',
|
'text/plain',
|
'text/xml',
|
'image/jpeg',
|
'image/png',
|
'image/gif',
|
'image/bmp',
|
'application/x-rar-compressed',
|
'application/zip',
|
'application/x-7z-compressed'
|
]
|
|
if (!allowedTypes.includes(file.type)) {
|
ElMessage.error('不支持的文件类型!')
|
return false
|
}
|
|
return true
|
}
|
|
// 文件上传成功
|
const handleUploadSuccess = (response, file, fileList) => {
|
console.log('文件上传成功响应:', response);
|
console.log('文件信息:', file);
|
|
if (response.code === 200) {
|
// 构建附件数据 - 确保正确获取URL
|
const attachmentData = {
|
name: file.name,
|
url: response.data.url || response.data.path || response.data.tempPath || file.url,
|
fileSize: file.size,
|
documentationId: currentDocumentId.value
|
};
|
|
console.log('构建的附件数据:', attachmentData);
|
|
// 调用保存附件接口
|
saveAttachment(attachmentData, file, fileList);
|
} else {
|
ElMessage.error(response.msg || '文件上传失败')
|
}
|
}
|
|
// 保存附件信息
|
const saveAttachment = async (attachmentData, file, fileList) => {
|
try {
|
console.log('开始保存附件,数据:', attachmentData);
|
|
// 确保URL字段存在且有效
|
if (!attachmentData.url) {
|
console.error('附件URL为空,无法保存');
|
ElMessage.error('文件URL获取失败,无法保存附件');
|
return;
|
}
|
|
const res = await addDocumentationFile(attachmentData);
|
console.log('保存附件接口响应:', res);
|
|
if (res.code === 200) {
|
const newFile = {
|
id: res.data.id || Date.now(),
|
name: attachmentData.name,
|
size: attachmentData.fileSize,
|
url: attachmentData.url,
|
uploadTime: new Date().toISOString(),
|
status: 'success',
|
uid: file.uid
|
}
|
|
console.log('创建的新文件对象:', newFile);
|
fileList.push(newFile)
|
ElMessage.success('文件上传并保存成功')
|
|
// 保存成功后刷新附件列表
|
if (currentDocumentId.value) {
|
await loadAttachmentList(currentDocumentId.value);
|
}
|
} else {
|
ElMessage.error(res.msg || '保存附件信息失败')
|
// 保存失败时移除文件
|
const index = fileList.findIndex(item => item.uid === file.uid)
|
if (index > -1) {
|
fileList.splice(index, 1)
|
}
|
}
|
} catch (error) {
|
console.error('保存附件失败:', error)
|
ElMessage.error('保存附件信息失败')
|
// 保存失败时移除文件
|
const index = fileList.findIndex(item => item.uid === file.uid)
|
if (index > -1) {
|
fileList.splice(index, 1)
|
}
|
}
|
}
|
|
// 文件上传失败
|
const handleUploadError = (error, file, fileList) => {
|
console.error('文件上传失败:', error);
|
console.error('失败的文件:', file);
|
console.error('当前文件列表:', fileList);
|
|
ElMessage.error('文件上传失败,请检查网络连接或文件格式')
|
}
|
|
// 移除文件
|
const handleRemove = (file, fileList) => {
|
const index = fileList.findIndex(item => item.uid === file.uid)
|
if (index > -1) {
|
fileList.splice(index, 1)
|
// total.value = fileList.length // Removed total.value
|
}
|
}
|
|
// 删除文件
|
const removeFile = (file) => {
|
ElMessageBox.confirm(`确定要删除文件 "${file.name}" 吗?`, '删除确认', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}).then(async () => {
|
try {
|
// 调用删除接口
|
const res = await deleteDocumentationFile([file.id]);
|
if (res.code === 200) {
|
// 从本地列表中移除
|
const index = fileList.value.findIndex(item => item.id === file.id);
|
if (index > -1) {
|
fileList.value.splice(index, 1);
|
}
|
ElMessage.success('删除成功');
|
|
// 如果有文档ID,刷新附件列表
|
if (currentDocumentId.value) {
|
await loadAttachmentList(currentDocumentId.value);
|
}
|
} else {
|
ElMessage.error(res.msg || '删除失败');
|
}
|
} catch (error) {
|
console.error('删除附件失败:', error);
|
ElMessage.error('删除附件失败');
|
}
|
}).catch(() => {
|
// 取消删除
|
})
|
}
|
|
// 预览文件
|
const previewFile = (file) => {
|
if (file.url) {
|
filePreviewRef.value.open(file.url)
|
} else {
|
ElMessage.warning('文件地址无效,无法预览')
|
}
|
}
|
|
// 下载文件
|
const downloadFile = (file) => {
|
if (file.url) {
|
// 创建下载链接
|
const link = document.createElement('a')
|
link.href = file.url
|
link.download = file.name
|
document.body.appendChild(link)
|
link.click()
|
document.body.removeChild(link)
|
ElMessage.success('开始下载文件')
|
} else {
|
ElMessage.warning('文件地址无效,无法下载')
|
}
|
}
|
|
// 格式化文件大小
|
const formatFileSize = (bytes) => {
|
if (bytes === 0) return '0 B'
|
const k = 1024
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
}
|
|
// 格式化日期
|
const formatDate = (dateString) => {
|
if (!dateString) return ''
|
const date = new Date(dateString)
|
return date.toLocaleString('zh-CN', {
|
year: 'numeric',
|
month: '2-digit',
|
day: '2-digit',
|
hour: '2-digit',
|
minute: '2-digit'
|
})
|
}
|
|
// 测试文件上传
|
const testUpload = () => {
|
console.log('当前文档ID:', currentDocumentId.value);
|
console.log('上传URL:', uploadUrl);
|
console.log('上传Headers:', uploadHeaders.value);
|
}
|
|
// 暴露方法
|
defineExpose({
|
open,
|
loadAttachmentList,
|
testUpload
|
})
|
</script>
|
|
<style scoped>
|
.attachment-manager {
|
padding: 20px;
|
}
|
|
.upload-section {
|
margin-bottom: 20px;
|
padding: 20px;
|
background-color: #f8f9fa;
|
border-radius: 8px;
|
border: 2px dashed #d9d9d9;
|
}
|
|
.upload-section:hover {
|
border-color: #409eff;
|
}
|
|
.attachment-list {
|
margin-bottom: 20px;
|
}
|
|
.el-upload__tip {
|
margin-top: 10px;
|
color: #666;
|
font-size: 12px;
|
line-height: 1.5;
|
}
|
|
:deep(.el-upload) {
|
width: 100%;
|
}
|
|
:deep(.el-upload-dragger) {
|
width: 100%;
|
height: 120px;
|
}
|
</style>
|