1.实现效果
2.代码
框架vue3,组件库element-plus
我在这里使用el-table二次封装了一个表格组件
<template>
<div :class="`ycd-table ${props.class}`">
<el-table
ref="tableRef"
:data="props.data"
style="width: 100%"
class="ycd-table"
:header-cell-style="{
backgroundColor: '#f5f7fa',
}"
border
row-key="id"
:highlight-current-row="highlightCurrentRow"
:row-class-name="tableRowClassName"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
@current-change="handleCurrentChange"
:height="props.height"
table-layout="auto"
>
<el-table-column
fixed="left"
type="selection"
width="38"
:reserve-selection="true"
v-if="type === 1"
:selectable="selectableFn"
/>
<slot></slot>
<template #empty>
<el-empty description="暂无数据" />
</template>
</el-table>
</div>
</template>
<script setup>
import { nextTick, ref, watch } from "vue";
const props = defineProps({
type: {
type: Number,
default: 0, // 0: 普通表格 1: 多选表格
},
data: {
type: Array,
default: [],
},
class: {
type: String,
default: "",
},
height: {
type: String,
default: "600",
},
// 高亮当前行,单选时使用
highlightCurrentRow: {
type: Boolean,
default: false,
},
// 默认勾选
selected: {
type: Array,
default: [],
},
// 自定义选择是否禁用
selectableFn: {
type: Function,
default: () => true,
},
// 选中的行
multipleSelection: {
type: Array,
default: [],
},
// 显示时用于清空选择
show: {
type: Boolean,
default: false,
},
});
const emits = defineEmits([
"row-click",
"selection-change",
"current-change",
"update:multipleSelection",
]);
const tableRef = ref();
// 定义选中行的class
const tableRowClassName = ({ row, rowIndex }) => {
if (props.multipleSelection.some((item) => item?.id === row?.id)) {
return "table-selected-row";
}
return "";
};
// *****************主要功能代码***************
// 上一次点击的索引
const lastClickIndex = ref(undefined);
// 上一次选择的方式 1:单选,2:多选,3:连选
let lastSelectStatus = 1;
// 表格行点击事件
const handleRowClick = (row, column, e) => {
// 测试自定义禁用规则,如果这一行通过规则校验为false,说明这一行是禁止选择的,不进行勾选
if (!props.selectableFn(row)) {
return;
}
const { ctrlKey, shiftKey } = e;
const rowIndex = props.data.findIndex((item) => item.id === row.id);
if (!ctrlKey && !shiftKey) {
// ctrl和shift都没有按下时单选
// 如果上次的索引和这次的索引不同 或 上一次不是单选 则清空选择
if (lastClickIndex.value !== rowIndex || lastSelectStatus !== 1) {
tableRef.value.clearSelection();
}
tableRef.value.toggleRowSelection(row, undefined);
lastSelectStatus = 1;
} else if (ctrlKey) {
// 按下ctrl多选
tableRef.value.toggleRowSelection(row, undefined);
lastSelectStatus = 2;
} else if (shiftKey) {
// 按下shift连选
if (lastClickIndex.value === undefined) {
return;
}
let minIndex = 0;
let maxIndex = 0;
if (rowIndex < lastClickIndex.value) {
minIndex = rowIndex;
maxIndex = lastClickIndex.value + 1;
} else {
minIndex = lastClickIndex.value;
maxIndex = rowIndex + 1;
}
props.data
.slice(minIndex, maxIndex)
.forEach((item) => tableRef.value.toggleRowSelection(item, true));
lastSelectStatus = 3;
}
lastClickIndex.value = rowIndex;
};
// 处理表格复选框改变事件
const handleSelectionChange = (val) => {
emits("update:multipleSelection", val);
};
// 当前行变化触发(单选)
const handleCurrentChange = (val) => {
emits("current-change", val);
};
// 默认勾选
watch(
() => props.selected,
async (selectedRow) => {
if (selectedRow.length > 0) {
await nextTick();
tableRef.value.clearSelection();
selectedRow.forEach((row) => {
// 注意:这里的row的引用必须和表格行的引用相同才能生效
// 否则无法实现选中效果
// 也就是说这里初始选择行的数据必须从渲染表格的数据那里取
tableRef.value.toggleRowSelection(row, undefined);
});
} else {
tableRef.value.clearSelection();
}
}
);
// 表格出现时清空选择
watch(
() => props.show,
async (show) => {
if (show) {
await nextTick();
tableRef.value.clearSelection();
}
}
);
</script>
<style scoped lang="scss">
.ycd-table {
user-select: none;
// 去除表格边框
:deep(.el-table th.el-table__cell.is-leaf),
:deep(.el-table td.el-table__cell) {
border: 0;
}
// :deep(.el-table .el-table__row:not(:last-of-type) td.el-table__cell),
:deep(.el-table .el-table__row td.el-table__cell),
:deep(.el-table .el-table__header th.el-table__cell) {
border-bottom: 1px solid #ebeef5;
}
:deep(.el-table__border-left-patch) {
width: 0;
background-color: transparent;
}
:deep(.el-table--border::after),
:deep(.el-table--border::before),
:deep(.el-table--border .el-table__inner-wrapper::after),
:deep(.el-table__inner-wrapper::before) {
width: 0;
background-color: transparent;
}
// 选中行的颜色
:deep(.el-table) .table-selected-row {
--el-table-tr-bg-color: var(--el-color-primary-light-9);
}
}
</style>
使用时如下
multipleSelection为多选选择的数据
<ycd-table
:data="tableDataPage"
v-model:multipleSelection="multipleSelection"
v-loading="loading"
:type="1"
:show="tableShow"
>
// el-table-column列配置
</ycd-table>