<template>
|
<el-table
|
ref="multipleTable"
|
v-loading="tableLoading"
|
:border="border"
|
:data="tableData"
|
:header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
|
height="calc(100vh - 18.5em)"
|
:highlight-current-row="highlightCurrentRow"
|
:row-class-name="rowClassName"
|
:row-style="rowStyle"
|
:row-key="rowKey"
|
style="width: 100%"
|
tooltip-effect="dark"
|
@row-click="rowClick"
|
@current-change="currentChange"
|
:show-summary="isShowSummary"
|
:summary-method="summaryMethod"
|
@selection-change="handleSelectionChange"
|
class="lims-table"
|
>
|
<el-table-column
|
align="center"
|
type="selection"
|
width="55"
|
v-if="isSelection"
|
/>
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
|
<el-table-column
|
v-for="(item, index) in column"
|
:key="index"
|
:column-key="item.columnKey"
|
:filter-method="item.filterHandler"
|
:filter-multiple="item.filterMultiple"
|
:filtered-value="item.filteredValue"
|
:filters="item.filters"
|
:fixed="item.fixed"
|
:label="item.label"
|
:prop="item.prop"
|
show-overflow-tooltip
|
:align="item.align"
|
:sortable="!!item.sortable"
|
:type="item.type"
|
:width="item.width"
|
>
|
<template
|
v-if="item.hasOwnProperty('colunmTemplate')"
|
#[item.colunmTemplate]="scope"
|
>
|
<slot
|
v-if="item.theadSlot"
|
:name="item.theadSlot"
|
:index="scope.$index"
|
:row="scope.row"
|
/>
|
</template>
|
|
<template #default="scope">
|
<!-- 插槽 -->
|
<div v-if="item.dataType == 'slot'">
|
<slot
|
v-if="item.slot"
|
:index="scope.$index"
|
:name="item.slot"
|
:row="scope.row"
|
/>
|
</div>
|
<!-- 进度条 -->
|
<div v-else-if="item.dataType == 'progress'">
|
<el-progress :percentage="Number(scope.row[item.prop])" />
|
</div>
|
<!-- 图片 -->
|
<div v-else-if="item.dataType == 'image'">
|
<img
|
:src="javaApi + '/img/' + scope.row[item.prop]"
|
alt=""
|
style="width: 40px; height: 40px; margin-top: 10px"
|
/>
|
</div>
|
|
<!-- tag -->
|
<div v-else-if="item.dataType == 'tag'">
|
<el-tag
|
v-if="
|
typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
|
'string'
|
"
|
:title="formatters(scope.row[item.prop], item.formatData)"
|
:type="formatType(scope.row[item.prop], item.formatType)"
|
>
|
{{ formatters(scope.row[item.prop], item.formatData) }}
|
</el-tag>
|
|
<el-tag
|
v-for="(tag, index) in dataTypeFn(
|
scope.row[item.prop],
|
item.formatData
|
)"
|
v-else-if="
|
typeof dataTypeFn(scope.row[item.prop], item.formatData) ===
|
'object'
|
"
|
:key="index"
|
:title="formatters(scope.row[item.prop], item.formatData)"
|
:type="formatType(tag, item.formatType)"
|
>
|
{{ item.tagGroup ? tag[item.tagGroup.label] ?? tag : tag }}
|
</el-tag>
|
|
<el-tag
|
v-else
|
:title="formatters(scope.row[item.prop], item.formatData)"
|
:type="formatType(scope.row[item.prop], item.formatType)"
|
>
|
{{ formatters(scope.row[item.prop], item.formatData) }}
|
</el-tag>
|
</div>
|
|
<!-- 按钮 -->
|
<div v-else-if="item.dataType == 'action'">
|
<template v-for="(o, key) in item.operation" :key="key">
|
<el-button
|
v-show="o.type != 'upload'"
|
size="small"
|
v-if="o.showHide ? o.showHide(scope.row) : true"
|
:disabled="o.disabled ? o.disabled(scope.row) : false"
|
:plain="o.plain"
|
type="primary"
|
:style="{
|
color:
|
o.name === '删除' || o.name === 'delete'
|
? '#f56c6c'
|
: o.color,
|
}"
|
link
|
@click="o.clickFun(scope.row)"
|
:key="key"
|
>
|
{{ o.name }}
|
</el-button>
|
<el-upload
|
:action="
|
javaApi +
|
o.url +
|
'?id=' +
|
(o.uploadIdFun ? o.uploadIdFun(scope.row) : scope.row.id)
|
"
|
ref="uploadRef"
|
size="small"
|
:multiple="o.multiple ? o.multiple : false"
|
:limit="1"
|
:disabled="o.disabled ? o.disabled(scope.row) : false"
|
:accept="
|
o.accept
|
? o.accept
|
: '.jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar'
|
"
|
v-if="o.type == 'upload'"
|
style="display: inline-block; width: 50px"
|
v-show="o.showHide ? o.showHide(scope.row) : true"
|
:headers="uploadHeader"
|
:before-upload="(file) => beforeUpload(file, scope.$index)"
|
:on-change="
|
(file, fileList) => handleChange(file, fileList, scope.$index)
|
"
|
:on-error="
|
(error, file, fileList) =>
|
onError(error, file, fileList, scope.$index)
|
"
|
:on-success="
|
(response, file, fileList) =>
|
handleSuccessUp(response, file, fileList, scope.$index)
|
"
|
:on-exceed="onExceed"
|
:show-file-list="false"
|
>
|
<el-button
|
:size="o.size ? o.size : 'small'"
|
link
|
type="primary"
|
:disabled="o.disabled ? o.disabled(scope.row) : false"
|
>{{ o.name }}</el-button
|
>
|
</el-upload>
|
</template>
|
</div>
|
<!-- 可点击的文字 -->
|
<div
|
v-else-if="item.dataType == 'link'"
|
class="cell link"
|
style="width: 100%"
|
@click="goLink(scope.row, item.linkMethod)"
|
>
|
<span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
|
</div>
|
<!-- 默认纯展示数据 -->
|
<div v-else class="cell" style="width: 100%">
|
<span v-if="!item.formatData">{{ scope.row[item.prop] }}</span>
|
<span v-else>{{
|
formatters(scope.row[item.prop], item.formatData)
|
}}</span>
|
</div>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination
|
v-show="total > 0"
|
:total="total"
|
:layout="page.layout"
|
:page="page.current"
|
:limit="page.size"
|
@pagination="paginationSearch"
|
/>
|
</template>
|
|
<script setup>
|
import pagination from "./Pagination.vue";
|
import { ref, inject, getCurrentInstance } from "vue";
|
import { ElMessage } from "element-plus";
|
|
// 获取全局的 uploadHeader
|
const { proxy } = getCurrentInstance();
|
const uploadHeader = proxy.uploadHeader;
|
const javaApi = proxy.javaApi;
|
|
const emit = defineEmits(["pagination"]);
|
|
// Filters
|
const typeFn = (val, row) => {
|
return typeof val === "function" ? val(row) : val;
|
};
|
|
const formatters = (val, format) => {
|
return typeof format === "function" ? format(val) : val;
|
};
|
|
// Props(使用 defineProps 的非 TS 形式)
|
const props = defineProps({
|
tableLoading: {
|
type: Boolean,
|
default: false,
|
},
|
handleSelectionChange: {
|
type: Function,
|
default: () => {},
|
},
|
summaryMethod: {
|
type: Function,
|
default: () => {},
|
},
|
rowClick: {
|
type: Function,
|
default: () => {},
|
},
|
currentChange: {
|
type: Function,
|
default: () => {},
|
},
|
border: {
|
type: Boolean,
|
default: true,
|
},
|
isSelection: {
|
type: Boolean,
|
default: false,
|
},
|
isShowSummary: {
|
type: Boolean,
|
default: false,
|
},
|
highlightCurrentRow: {
|
type: Boolean,
|
default: false,
|
},
|
headerCellStyle: {
|
type: Object,
|
default: () => ({}),
|
},
|
column: {
|
type: Array,
|
default: () => [],
|
},
|
rowClassName: {
|
type: Function,
|
default: () => "",
|
},
|
rowStyle: {
|
type: [Object, Function],
|
default: () => ({}),
|
},
|
tableData: {
|
type: Array,
|
default: () => [],
|
},
|
rowKey: {
|
type: String,
|
default: undefined,
|
},
|
page: {
|
type: Object,
|
default: () => ({
|
total: 0,
|
current: 0,
|
size: 10,
|
layout: "total, sizes, prev, pager, next, jumper",
|
}),
|
},
|
total: {
|
type: Number,
|
default: 0,
|
},
|
});
|
|
// Data
|
const uploadRefs = ref([]);
|
const currentFiles = ref({});
|
const uploadKeys = ref({});
|
|
const indexMethod = (index) => {
|
return (props.page.current - 1) * props.page.size + index + 1;
|
};
|
|
// 点击 link 事件
|
const goLink = (row, linkMethod) => {
|
if (!linkMethod) {
|
return ElMessage.warning("请配置 link 事件");
|
}
|
const parentMethod = getParentMethod(linkMethod);
|
if (typeof parentMethod === "function") {
|
parentMethod(row);
|
} else {
|
console.warn(`父组件中未找到方法: ${linkMethod}`);
|
}
|
};
|
|
// 获取父组件方法(示例实现)
|
const getParentMethod = (methodName) => {
|
const parentMethods = inject("parentMethods", {});
|
return parentMethods[methodName];
|
};
|
|
const dataTypeFn = (val, format) => {
|
if (typeof format === "function") {
|
return format(val);
|
} else return val;
|
};
|
|
const formatType = (val, format) => {
|
if (typeof format === "function") {
|
return format(val);
|
} else return "";
|
};
|
|
// 文件变化处理
|
const handleChange = (file, fileList, index) => {
|
if (fileList.length > 1) {
|
const earliestFile = fileList[0];
|
uploadRefs.value[index]?.handleRemove(earliestFile);
|
}
|
currentFiles.value[index] = file;
|
};
|
|
// 文件上传前校验
|
const beforeUpload = (rawFile, index) => {
|
currentFiles.value[index] = {};
|
if (rawfile.size > 1024 * 1024 * 10 * 10) {
|
ElMessage.error("上传文件不超过10M");
|
return false;
|
}
|
return true;
|
};
|
|
// 上传成功
|
const handleSuccessUp = (response, file, fileList, index) => {
|
if (response.code == 200) {
|
if (uploadRefs[index]) {
|
uploadRefs[index].clearFiles();
|
}
|
currentFiles[index] = file;
|
ElMessage.success("上传成功");
|
resetUploadComponent(index);
|
} else {
|
ElMessage.error(response.message);
|
}
|
};
|
|
const resetUploadComponent = (index) => {
|
uploadKeys[index] = Date.now();
|
};
|
|
// 上传失败
|
const onError = (error, file, fileList, index) => {
|
ElMessage.error("文件上传失败,请重试");
|
if (uploadRefs.value[index]) {
|
uploadRefs.value[index].clearFiles();
|
}
|
};
|
|
// 文件数量超限提示
|
const onExceed = () => {
|
ElMessage.warning("超出文件个数");
|
};
|
|
const paginationSearch = ({ page, limit }) => {
|
emit("pagination", { page: page, limit: limit });
|
};
|
</script>
|
|
<style scoped lang="scss">
|
.cell {
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
padding-right: 0 !important;
|
padding-left: 0 !important;
|
}
|
</style>
|