Files
klp-oa/klp-ui/src/components/KLPUI/KLPTable/index.vue

209 lines
6.1 KiB
Vue
Raw Normal View History

2025-08-25 11:46:03 +08:00
<template>
<div class="my-table-container">
<!-- 扩展层可后续统一添加如加载动画导出按钮等 -->
<div v-if="loading" class="table-loading">
<el-loading-spinner></el-loading-spinner>
<p class="loading-text">{{ loadingText || "加载中..." }}</p>
</div>
<!-- 原生 Table 核心透传 props/事件/插槽 -->
2025-08-25 11:46:03 +08:00
<el-table
:ref="tableRef"
2025-08-25 11:46:03 +08:00
v-bind="$attrs"
v-on="$listeners"
:class="['my-table', customClass]"
2025-08-25 11:46:03 +08:00
>
<!-- 1. 透传列定义插槽原生 columns 中通过 slot 自定义的表头/单元格 -->
<template
v-for="(column, index) in $attrs.columns || []"
v-slot:[`header-${column.prop}`]="scope"
>
<!-- 调用业务层定义的表头插槽 -->
<slot :name="`header-${column.prop}`" v-bind="scope"></slot>
2025-08-25 11:46:03 +08:00
</template>
<template
v-for="(column, index) in $attrs.columns || []"
v-slot:[column.prop]="scope"
2025-08-25 11:46:03 +08:00
>
<!-- 调用业务层定义的单元格插槽 -->
<slot :name="column.prop" v-bind="scope"></slot>
</template>
<!-- 2. 透传原生内置插槽 empty 空数据插槽append 底部插槽等 -->
<template v-slot:empty="scope">
<slot name="empty" v-bind="scope"></slot>
</template>
<template v-slot:append="scope">
<slot name="append" v-bind="scope"></slot>
</template>
<!-- 3. 透传自定义列插槽业务层直接用 <el-table-column> 嵌套的情况 -->
<slot v-bind:tableRef="tableRef"></slot>
2025-08-28 15:06:03 +08:00
<el-table-column
v-if="selectionColumn"
type="selection"
width="55"
align="center"
></el-table-column>
<el-table-column
v-if="indexColumn"
type="index"
width="55"
align="center"
></el-table-column>
<el-table-column
v-for="(column, index) in customColumns"
v-bind="column"
:width="column.width"
:prop="column.prop"
:align="column.align"
:label="column.label"
:min-width="column.minWidth"
:fixed="column.fixed"
:show-overflow-tooltip="column.showOverflowTooltip"
:sortable="column.sortable"
>
<template v-slot="scope">
<ColumnRender v-if="column.render" :column="column" :data="scope.row" :render="column.render"/>
<Eclipse v-else-if="column.eclipse" :text="scope.row[column.prop]"></Eclipse>
<span v-else>{{ scope.row[column.prop] }}</span>
</template>
</el-table-column>
2025-08-25 11:46:03 +08:00
</el-table>
<!-- 扩展层可后续统一添加分页如与 MyPagination 组件联动 -->
<slot name="pagination"></slot>
2025-08-25 11:46:03 +08:00
</div>
</template>
<script>
2025-08-28 15:06:03 +08:00
import ColumnRender from './ColumnRender.vue';
import Eclipse from './renderer/eclipse.vue';
2025-08-25 11:46:03 +08:00
export default {
2025-08-28 15:06:03 +08:00
name: "KLPTable", // 组件名,便于调试和文档生成
components: {
ColumnRender,
Eclipse
},
2025-08-25 11:46:03 +08:00
props: {
// 1. 扩展 props新增原生 Table 没有的属性(如加载状态)
2025-08-25 11:46:03 +08:00
loading: {
type: Boolean,
default: false
},
loadingText: {
2025-08-25 11:46:03 +08:00
type: String,
default: "加载中..."
2025-08-25 11:46:03 +08:00
},
// 2. 兼容原生 class 用法(原生 el-table 支持 class 属性,此处显式接收避免 $attrs 冲突)
customClass: {
2025-08-25 11:46:03 +08:00
type: String,
default: ""
2025-08-28 15:06:03 +08:00
},
customColumns: {
type: Array,
default: () => []
},
selectionColumn: {
type: Boolean,
default: false
},
indexColumn: {
type: Boolean,
default: false
2025-08-25 11:46:03 +08:00
}
},
data() {
return {
tableRef: "myTableRef" // 表格 ref便于后续通过 ref 调用原生方法
};
},
2025-08-25 11:46:03 +08:00
methods: {
2025-08-28 15:06:03 +08:00
// 核心方法净化scope移除可能导致循环引用的属性
sanitizeScope(scope) {
if (!scope) return {};
// 只保留安全的属性(根据实际需求调整)
const { row, column, $index, store } = scope;
// 深拷贝避免引用传递,同时过滤循环引用
return this.deepClone({ row, column, $index, store });
},
// 安全的深拷贝方法避免JSON.parse/stringify无法处理的类型
deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 避免处理Vue实例通常带有循环引用
if (obj._isVue) return { _isVue: true };
const clone = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = this.deepClone(obj[key]);
}
}
return clone;
},
// 3. 透传原生 Table 实例方法(如 clearSelection、doLayout 等)
// 业务层可通过 this.$refs.myTable.xxx() 调用原生方法
2025-08-25 11:46:03 +08:00
getTableInstance() {
return this.$refs[this.tableRef];
2025-08-25 11:46:03 +08:00
},
// 示例:透传原生 clearSelection 方法
2025-08-25 11:46:03 +08:00
clearSelection() {
const table = this.getTableInstance();
if (table && table.clearSelection) {
table.clearSelection();
2025-08-25 11:46:03 +08:00
}
},
// 可根据需要扩展更多原生方法(如 toggleRowSelection、sort 等)
2025-08-25 11:46:03 +08:00
toggleRowSelection(row, selected) {
const table = this.getTableInstance();
if (table && table.toggleRowSelection) {
table.toggleRowSelection(row, selected);
2025-08-25 11:46:03 +08:00
}
}
},
mounted() {
// 扩展点:后续可统一添加初始化逻辑(如权限控制、默认排序等)
console.log("MyTable 初始化完成,原生实例:", this.getTableInstance());
2025-08-25 11:46:03 +08:00
}
};
2025-08-25 11:46:03 +08:00
</script>
<style scoped>
.my-table-container {
width: 100%;
position: relative;
}
/* 扩展样式:加载状态遮罩(后续可统一调整) */
.table-loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
.loading-text {
margin-top: 12px;
color: #666;
font-size: 14px;
}
/* 原生 Table 样式兼容:避免封装层影响原生样式 */
.my-table {
2025-08-25 11:46:03 +08:00
width: 100%;
}
</style>