foreign-admin-ui/src/components/common/Table.vue

149 lines
3.7 KiB
Vue

<template>
<div class="table-container">
<el-table
ref="tableRef"
height="100%"
style="width: 100%"
:data="tableData"
:row-key="rowKey"
:tree-props="treeProps"
:border="border"
:highlight-current-row="highlightCurrentRow"
v-bind="$attrs"
@row-click="handleRowClick"
@row-contextmenu="handleRowContextmenu"
>
<!-- 动态表头列 -->
<template v-for="(column, index) in columns" :key="index">
<el-table-column
v-if="column.type === 'index'"
type="index"
:label="column.label"
:width="column.width || 60"
:align="column.align || 'center'"
/>
<el-table-column
v-else-if="!column.slotName"
:prop="column.prop"
:label="column.label"
:width="column.width"
:align="column.align || 'left'"
:sortable="column.sortable"
/>
<!-- 具名插槽列 -->
<el-table-column
v-else
:label="column.label"
:width="column.width"
:align="column.align || 'left'"
>
<template #default="{ row }">
<slot :name="column.slotName" :row="row" />
</template>
</el-table-column>
</template>
</el-table>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { TableInstance } from 'element-plus'
// 定义表格列配置接口
interface TableColumn {
prop?: string
type?: string
label: string
width?: string | number
align?: 'left' | 'center' | 'right'
slotName?: string // 具名插槽名称
sortable?: boolean // 是否可排序
}
// 定义表格组件属性接口
interface Props {
tableData: any[]
rowKey?: string
treeProps?: {
children: string
}
columns?: TableColumn[]
border?: boolean // 是否显示边框
highlightCurrentRow?: boolean // 是否高亮当前行
}
const props = withDefaults(defineProps<Props>(), {
border: true,
highlightCurrentRow: true,
})
// 定义组件抛出的事件
const emit = defineEmits<{
(e: 'row-click', row: any): void // 保留原有行点击事件
(e: 'row-contextmenu', row: any, column: any, event: MouseEvent): void
}>()
const tableRef = ref<TableInstance>()
// 维护当前选中行状态,用于二次点击取消选中
const selectedRow = ref<any>(null)
/**
* 处理行点击事件(支持二次点击取消选中)
*/
const handleRowClick = (row: any) => {
// 增加空值保护,避免传递 null 给父组件
if (!row) return
// 判断是否是二次点击同一行
if (selectedRow.value && selectedRow.value === row) {
// 二次点击:取消选中
selectedRow.value = null
tableRef.value?.setCurrentRow() // 不传参即清除选中 [^14^]
} else {
// 首次点击或点击不同行:选中该行
selectedRow.value = row
tableRef.value?.setCurrentRow(row)
}
emit('row-click', selectedRow.value)
}
/**
* 处理行右键菜单事件
*/
const handleRowContextmenu = (row: any, column: any, event: MouseEvent) => {
console.log('row', row)
emit('row-contextmenu', row, column, event)
}
// 监听 tableData 变化,数据更新时取消选中
watch(
() => props.tableData,
() => {
// 数据更新时,清除选中状态
selectedRow.value = null
tableRef.value?.setCurrentRow() // 不传参即清除选中 [^14^]
},
{ deep: true }, // 深度监听,防止数组元素变化未触发
)
// 暴露清除当前选中行的方法
defineExpose({
clearCurrentRow: () => {
selectedRow.value = null
tableRef.value?.setCurrentRow()
},
})
</script>
<style scoped lang="scss">
.table-container {
width: 100%;
height: 100%;
// overflow: hidden;
}
</style>