<template>
|
<el-dialog v-model="dialogVisible"
|
:title="title"
|
:width="width"
|
:before-close="handleClose">
|
<div class="file-list-toolbar"
|
v-if="showToolbar">
|
<template v-if="useBuiltInUpload">
|
<el-upload v-model:file-list="uploadFileList"
|
class="upload-demo"
|
:action="uploadAction"
|
:headers="uploadHeaders"
|
:show-file-list="false"
|
:on-success="handleDefaultUploadSuccess"
|
:on-error="handleDefaultUploadError">
|
<el-button v-if="showUploadButton"
|
type="primary"
|
size="small">
|
上传附件
|
</el-button>
|
</el-upload>
|
</template>
|
<template v-else>
|
<el-button v-if="showUploadButton"
|
type="primary"
|
size="small"
|
@click="handleUpload">
|
新增附件
|
</el-button>
|
</template>
|
</div>
|
<el-table :data="tableData"
|
border
|
:height="tableHeight">
|
<el-table-column :label="nameColumnLabel"
|
:prop="nameColumnProp"
|
:min-width="nameColumnMinWidth"
|
show-overflow-tooltip />
|
<el-table-column v-if="showActions"
|
fixed="right"
|
label="操作"
|
:width="actionColumnWidth"
|
align="center">
|
<template #default="scope">
|
<el-button v-if="showDownload"
|
link
|
type="primary"
|
size="small"
|
@click="handleDownload(scope.row)">
|
下载
|
</el-button>
|
<el-button v-if="showPreview"
|
link
|
type="primary"
|
size="small"
|
@click="handlePreview(scope.row)">
|
预览
|
</el-button>
|
<el-button v-if="showDeleteButton"
|
link
|
type="danger"
|
size="small"
|
@click="handleDelete(scope.row, scope.$index)">
|
删除
|
</el-button>
|
<slot name="actions"
|
:row="scope.row"></slot>
|
</template>
|
</el-table-column>
|
<slot name="columns"></slot>
|
</el-table>
|
<pagination v-if="isShowPagination"
|
style="margin-bottom: 20px;"
|
:total="page.total"
|
:page="page.current"
|
:limit="page.size"
|
@pagination="paginationSearch"
|
@change="handleChange" />
|
</el-dialog>
|
<filePreview v-if="showPreview"
|
ref="filePreviewRef" />
|
</template>
|
|
<script setup>
|
import { ref, computed, getCurrentInstance } from "vue";
|
import pagination from "@/components/Pagination/index.vue";
|
import { ElMessage } from "element-plus";
|
import filePreview from "@/components/filePreview/index.vue";
|
import { getToken } from "@/utils/auth";
|
|
const props = defineProps({
|
modelValue: {
|
type: Boolean,
|
default: false,
|
},
|
title: {
|
type: String,
|
default: "附件",
|
},
|
width: {
|
type: String,
|
default: "40%",
|
},
|
tableHeight: {
|
type: String,
|
default: "40vh",
|
},
|
nameColumnLabel: {
|
type: String,
|
default: "附件名称",
|
},
|
nameColumnProp: {
|
type: String,
|
default: "name",
|
},
|
nameColumnMinWidth: {
|
type: [String, Number],
|
default: 400,
|
},
|
actionColumnWidth: {
|
type: [String, Number],
|
default: 160,
|
},
|
showActions: {
|
type: Boolean,
|
default: true,
|
},
|
showDownload: {
|
type: Boolean,
|
default: true,
|
},
|
showPreview: {
|
type: Boolean,
|
default: true,
|
},
|
showUploadButton: {
|
type: Boolean,
|
default: false,
|
},
|
showDeleteButton: {
|
type: Boolean,
|
default: false,
|
},
|
urlField: {
|
type: String,
|
default: "url",
|
},
|
downloadMethod: {
|
type: Function,
|
default: null,
|
},
|
previewMethod: {
|
type: Function,
|
default: null,
|
},
|
uploadMethod: {
|
type: Function,
|
default: null,
|
},
|
deleteMethod: {
|
type: Function,
|
default: null,
|
},
|
rulesRegulationsManagementId: {
|
type: [String, Number],
|
default: "",
|
},
|
uploadUrl: {
|
type: String,
|
default: `${import.meta.env.VITE_APP_BASE_API}/file/upload`,
|
},
|
isShowPagination: {
|
type: Boolean,
|
default: false,
|
},
|
page: {
|
type: Object,
|
default: () => ({
|
current: 1,
|
size: 10,
|
total: 0,
|
}),
|
},
|
});
|
|
const emit = defineEmits([
|
"update:modelValue",
|
"close",
|
"download",
|
"preview",
|
"upload",
|
"delete",
|
]);
|
|
const { proxy } = getCurrentInstance();
|
const filePreviewRef = ref(null);
|
const uploadFileList = ref([]);
|
|
const dialogVisible = computed({
|
get: () => props.modelValue,
|
set: val => emit("update:modelValue", val),
|
});
|
|
const tableData = ref([]);
|
const showToolbar = computed(() => props.showUploadButton);
|
const useBuiltInUpload = computed(() => !props.uploadMethod);
|
const uploadAction = computed(() => props.uploadUrl);
|
const uploadHeaders = computed(() => ({
|
Authorization: `Bearer ${getToken()}`,
|
}));
|
|
const handleClose = () => {
|
emit("close");
|
dialogVisible.value = false;
|
};
|
|
const handleDownload = row => {
|
if (props.downloadMethod) {
|
props.downloadMethod(row);
|
} else {
|
// 默认下载方法
|
proxy.$download.name(row[props.urlField]);
|
}
|
emit("download", row);
|
};
|
|
const handlePreview = row => {
|
if (props.previewMethod) {
|
props.previewMethod(row);
|
} else {
|
// 默认预览方法
|
if (filePreviewRef.value) {
|
filePreviewRef.value.open(row[props.urlField]);
|
}
|
}
|
emit("preview", row);
|
};
|
const paginationSearch = page => {
|
props.page.current = page.page;
|
props.page.size = page.limit;
|
emit("pagination", page.page, page.limit);
|
};
|
|
const open = list => {
|
dialogVisible.value = true;
|
tableData.value = list || [];
|
};
|
|
const handleUpload = async () => {
|
if (props.uploadMethod) {
|
// 如果提供了自定义上传方法,由父组件负责更新列表(通过 setList)
|
// 这里不再自动添加,避免与父组件的 setList 重复
|
await props.uploadMethod();
|
}
|
emit("upload");
|
};
|
|
const handleDelete = async (row, index) => {
|
if (props.deleteMethod) {
|
const result = await props.deleteMethod(row, index);
|
if (result === false) {
|
return;
|
}
|
// 如果提供了 deleteMethod,由父组件负责刷新列表,不在这里删除
|
} else {
|
// 如果没有提供 deleteMethod,才在组件内部删除
|
removeAttachment(index);
|
}
|
emit("delete", row);
|
};
|
|
const addAttachment = item => {
|
tableData.value = [...tableData.value, item];
|
};
|
|
const handleDefaultUploadSuccess = async (res, file) => {
|
if (res?.code !== 200) {
|
ElMessage.error(res?.msg || "文件上传失败");
|
return;
|
}
|
if (!props.rulesRegulationsManagementId) {
|
ElMessage.error("缺少规章制度ID,无法保存附件");
|
return;
|
}
|
const fileName = res?.data?.originalName || file?.name;
|
const fileUrl = res?.data?.tempPath || res?.data?.url;
|
const payload = {
|
fileName,
|
fileUrl,
|
rulesRegulationsManagementId: props.rulesRegulationsManagementId,
|
raw: res?.data || {},
|
};
|
emit("upload", payload);
|
};
|
|
const handleDefaultUploadError = () => {
|
ElMessage.error("文件上传失败");
|
};
|
|
const removeAttachment = index => {
|
if (index > -1 && index < tableData.value.length) {
|
const newList = [...tableData.value];
|
newList.splice(index, 1);
|
tableData.value = newList;
|
}
|
};
|
|
const setList = list => {
|
tableData.value = list || [];
|
};
|
|
defineExpose({
|
open,
|
addAttachment,
|
removeAttachment,
|
setList,
|
handleUpload,
|
handleDelete,
|
});
|
</script>
|
|
<style scoped>
|
.file-list-toolbar {
|
margin-bottom: 8px;
|
text-align: right;
|
}
|
</style>
|