一、看效果:
按住ctrl键实现单个多选 按住shift实现区间范围多选
二、代码:
vue页面
<template>
<el-tree
class="w100%"
:data="$.treeData"
ref="treeTableListRef"
:props="$.defaultProps"
highlight-current
:expand-on-click-node="false"
key="id"
:default-expand-all="true"
@node-click="(data, node) => $.tableFieldsNodeClick(data, node, treeTableListRef)"
>
<template #default="{ data }">
<div style="user-select: none">{{ data.name }}</div>
</template>
</el-tree>
</template>
<script setup lang="ts">
import { useData } from "./hooks/index";
const treeTableListRef = ref();
let { $data: $ } = useData();
onMounted(() => {});
onBeforeMount(() => {
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
});
// 按下为true
const handleKeyDown = (event: any) => {
// 代表按下的是ctrl键
if (event.key == "Control") {
$.ctrlKeyPressed = true;
}
// 代表按下的是shift键
if (event.key == "Shift") {
$.shiftKeyPressed = true;
}
};
// 释放为false
const handleKeyUp = (event: any) => {
// 代表按下的是ctrl键
if (event.key == "Control") {
$.ctrlKeyPressed = false;
}
// 代表按下的是shift键
if (event.key == "Shift") {
$.shiftKeyPressed = false;
}
};
</script>
<style scoped lang="scss">
</style>
引入的hooks文件,index.ts
export function useData() {
const $data: any = reactive({
ctrlKeyPressed: false,
shiftKeyPressed: false,
shiftKeyFelid: [],
defaultProps: {
children: "children",
label: "name",
},
treeData: [
{
name: '一级1',
id: 1,
children: [{
name: '二级1',
id: 2,
children: [{
name: '三级1',
id: 2,
}, {
name: '三级2',
id: 3,
}, {
name: '三级3',
id: 4,
}, {
name: '三级4',
id: 5,
}, {
name: '三级5',
id: 6,
}]
}, {
name: '二级2',
id: 3,
}, {
name: '二级3',
id: 4,
}, {
name: '二级4',
id: 5,
}, {
name: '二级5',
id: 6,
}]
}, {
name: '一级2',
id: 7,
children: [{
name: '二级1',
id: 8,
}, {
name: '二级2',
id: 9,
}, {
name: '二级3',
id: 10,
}, {
name: '二级4',
id: 11,
}, {
name: '二级5',
id: 12,
}]
}],
selectNodes: []
})
// 节点选中事件
$data.tableFieldsNodeClick = (nodeData: any, node: any, treeTableListRef: any) => {
const nodes = treeTableListRef.store._getAllNodes();//所有node节点
const ishas = $data.selectNodes.includes(node.id)
// 递归遍历节点数组进行ID存放
function addSelectId(arr: any) {
for (const item of arr) {
$data.selectNodes.push(item.id)
if (Array.isArray(item.children) && item.children.length) {
addSelectId(item.children)
}
}
}
// 递归遍历删除节点id
function delSelectId(arr: any) {
for (const item of arr) {
const index = $data.selectNodes.findIndex((x: any) => x == item.id);
$data.selectNodes.splice(index, 1);
if (Array.isArray(item.children) && item.children.length) {
delSelectId(item.children);
}
}
}
// 按住了ctrl键,可以进行单个多选
if ($data.ctrlKeyPressed) {
// 如果为true代表当前选中的节点已存在
if (ishas) {
// 查找当前选中的节点的索引
const index = $data.selectNodes.findIndex((x: any) => x == node.id);
// 删除父节点
$data.selectNodes.splice(index, 1);
// 删除子节点
if (Array.isArray(node.childNodes) && node.childNodes.length) {
deleteSelectId(node.childNodes);
}
} else {
// 否则当前选中的节点不存在,就加入到已选节点数组序列
$data.selectNodes.push(node.id)
// 防止选中的是父节点,就需要递归将子节点加入
if (Array.isArray(node.childNodes) && node.childNodes.length) {
addSelectId(node.childNodes);
}
}
node.isCurrent = !node.isCurrent;
// 按下了shift键,可以进行范围多选
} else if ($data.shiftKeyPressed) {
// 先清空
$data.selectNodes = []
// 将当前节点放入
$data.selectNodes.push(node.id)
$data.shiftKeyFelid.push(node.id);
if ($data.shiftKeyFelid.length > 1) {
// 首索引
const sIndex = nodes.findIndex((x: any) => x.id == $data.shiftKeyFelid[0])
// 尾索引
const eIndex = nodes.findIndex((x: any) => x.id == $data.shiftKeyFelid[$data.shiftKeyFelid.length - 1]);
// 根据首尾索引,存入中间节点
const s = sIndex < eIndex ? sIndex : eIndex //取小值当开头索引
const e = sIndex < eIndex ? eIndex : sIndex//取大值当结尾索引
for (let i = s; i < e; i++) {
// 放入该区间节点id
$data.selectNodes.push(nodes[i].id);
}
}
} else {
// 否则就是单机选择
$data.shiftKeyFelid = [];
$data.selectNodes = [];
$data.selectNodes = [node.id];
}
// 下面是对已选中的节点,进行高亮展示
// 通过控制elementui中节点上的isCurrent属性
// isCurrent为true是高亮,否则取消高亮
for (const item of nodes) {
if ($data.selectNodes.includes(item.id)) {
item.isCurrent = true;
} else {
item.isCurrent = false;
}
}
};
return {
$data: $data
}
}
三、注意:
1、重点是要获取当前所选节点数组
2、通过循环节点数组来更新nodes节点中isCurrent属性,控制高亮