1.代码实现
<template>
<div class="TreePage">
<el-checkbox
v-model="menuExpand"
@change="handleCheckedTreeExpand($event, 'menu')"
>展开/折叠</el-checkbox
>
<el-checkbox
v-model="menuNodeAll"
@change="handleCheckedTreeNodeAll($event, 'menu')"
>全选/全不选</el-checkbox
>
<el-checkbox
v-model="menuCheckStrictly"
@change="handleCheckedTreeConnect($event, 'menu')"
>父子联动</el-checkbox
>
<el-row :gutter="20" style="margin-top: 20px">
<!--村数据-->
<el-col :span="24">
<div class="head-container">
<el-input
v-model="deptName"
placeholder="请输入名称"
clearable
size="small"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
/>
</div>
<!-- 组织树 -->
<div class="head-container">
<el-tree
ref="menu"
:props="defaultProps"
:default-expanded-keys="defaultExpandedKeys"
:default-checked-keys="defaultCheckedKeys"
:expand-on-click-node="false"
:data="menuOptions"
show-checkbox
node-key="deptId"
:check-strictly="!menuCheckStrictly"
empty-text="加载中,请稍后"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
draggable
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
:allow-drop="allowDrop"
:allow-drag="allowDrag"
>
<!--自定义节点内容: 使用 scoped slot 会传入两个参数node和data,分别表示当前节点的 Node 对象和当前节点的数据。 -->
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span>
<el-button type="text" size="mini" @click="() => append(data)">
新增
</el-button>
<el-button
type="text"
size="mini"
@click="() => remove(node, data)"
>
删除
</el-button>
</span>
</span>
</el-tree>
</div>
</el-col>
</el-row>
<el-button type="primary" @click="submitForm">确 定</el-button>
</div>
</template>
<script>
let deptId = 10000;
export default {
name: "TreePage2",
props: {},
data() {
return {
menuExpand: false,
menuNodeAll: false,
menuCheckStrictly: true,
// 模拟数据
menuOptions: [
{
deptId: 100,
parentId: 0,
parentName: null,
ancestors: "0",
deptName: "XX01街道",
children: [
{
deptId: 1001,
parentId: 100,
parentName: null,
ancestors: "0,100",
deptName: "XX1-1村",
children: [
{
deptId: 10011,
parentId: 1001,
parentName: null,
ancestors: "0,100,1001",
deptName: "XX1-1-1村",
children: [],
},
],
},
{
deptId: 1002,
parentId: 100,
parentName: null,
ancestors: "0,100",
deptName: "XX1-2村",
children: [
{
deptId: 10021,
parentId: 1002,
parentName: null,
ancestors: "0,100,1002",
deptName: "XX1-2-1村",
children: [],
disabled: true, //禁用
},
],
},
{
deptId: 1003,
parentId: 100,
parentName: null,
ancestors: "0,100",
deptName: "XX1-3村",
children: [],
},
],
},
{
deptId: 200,
parentId: 0,
parentName: null,
ancestors: "0",
deptName: "XX02街道",
children: [
{
deptId: 2001,
parentId: 200,
parentName: null,
ancestors: "0,200",
deptName: "XX2-1村",
children: [],
},
{
deptId: 2002,
parentId: 200,
parentName: null,
ancestors: "0,200",
deptName: "XX2-2村",
children: [],
},
{
deptId: 2003,
parentId: 200,
parentName: null,
ancestors: "0,200",
deptName: "XX2-3村",
children: [],
},
],
},
],
// 村名称
deptName: undefined,
defaultProps: {
children: "children",
label: "deptName",
},
// 默认展开的节点的 key 的数组
defaultExpandedKeys: [100, 10021],
// 默认展开的节点的 选中 的数组
defaultCheckedKeys: [1001],
};
},
watch: {
// 在需要对节点进行过滤时,调用 Tree 实例的filter方法,参数为关键字。
// 需要注意的是,此时需要设置filter-node-method,值为过滤函数。
// 根据名称筛选村社树
deptName(val) {
this.$refs.menu.filter(val);
},
},
methods: {
// 树权限(展开/折叠)
handleCheckedTreeExpand(value) {
let treeList = this.menuOptions;
for (let i = 0; i < treeList.length; i++) {
this.$refs.menu.store.nodesMap[treeList[i].deptId].expanded = value;
}
},
// 树权限(全选/全不选)
handleCheckedTreeNodeAll(value) {
this.$refs.menu.setCheckedNodes(value ? this.menuOptions : []);
},
// 树权限(父子联动)
handleCheckedTreeConnect(value) {
this.menuCheckStrictly = value ? true : false;
},
// 过滤函数
filterNode(value, data) {
// console.log(value, data)
if (!value) return true;
return data.deptName.indexOf(value) !== -1;
},
// 左侧网格树-节点单击事件
handleNodeClick(data) {
console.log(data, "左侧网格树-节点单击事件");
},
// 所有菜单节点数据
getMenuAllCheckedKeys() {
// 目前被选中的菜单节点
let checkedKeys = this.$refs.menu.getCheckedKeys();
// 半选中的菜单节点
let halfCheckedKeys = this.$refs.menu.getHalfCheckedKeys();
checkedKeys.unshift.apply(checkedKeys, halfCheckedKeys);
return checkedKeys;
},
// 新增节点
append(data) {
const newChild = { deptId: deptId++, deptName: "test", children: [] };
if (!data.children) {
this.$set(data, "children", []);
}
data.children.push(newChild);
},
// 删除节点
remove(node, data) {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex((d) => d.deptId === data.deptId);
children.splice(index, 1);
},
// 节点开始拖拽时触发的事件
handleDragStart(node, ev) {
console.log("drag start", node,ev);
},
// 拖拽进入其他节点时触发的事件
handleDragEnter(draggingNode, dropNode, ev) {
console.log("tree drag enter: ", dropNode.deptName,ev);
},
// 拖拽离开某个节点时触发的事件
handleDragLeave(draggingNode, dropNode, ev) {
console.log("tree drag leave: ", dropNode.deptName,ev);
},
// 在拖拽节点时触发的事件(类似浏览器的 mouseover 事件)
handleDragOver(draggingNode, dropNode, ev) {
console.log("tree drag over: ", dropNode.deptName,ev);
},
// 拖拽结束时(可能未成功)触发的事件
handleDragEnd(draggingNode, dropNode, dropType, ev) {
console.log("tree drag end: ", dropNode && dropNode.deptName, dropType,ev);
},
// 拖拽成功完成时触发的事件
handleDrop(draggingNode, dropNode, dropType, ev) {
console.log("tree drop: ", dropNode.deptName, dropType,ev);
},
// 拖拽时判定目标节点能否被放置。type 参数有三种情况:'prev'、'inner' 和 'next',
// 分别表示放置在目标节点前、插入至目标节点和放置在目标节点后
allowDrop(draggingNode, dropNode, type) {
if (dropNode.data.deptName === "XX1-1-1村") {
return type !== "inner";
} else {
return true;
}
},
// 判断节点能否被拖拽
allowDrag(draggingNode) {
return draggingNode.data.deptName.indexOf("XX1-2-1村") === -1;
},
/** 提交按钮 */
submitForm() {
let menuIds = this.getMenuAllCheckedKeys();
console.log(menuIds, "menuIds");
},
},
};
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>
2. 效果图
3. 使用到的部分属性说明
- default-expanded-keys: 默认展开的节点的 key 的数组;
- expand-on-click-node: 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点;
- node-key :每个树节点用来作为唯一标识的属性,整棵树应该是唯一的;
- filter-node-method: 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏;
- node-click :节点被点击时的回调;
- accordion: 是否每次只打开一个同级树节点展开;
- default-expand-all: 是否默认展开所有节点 ;
- 通过 draggable: 属性可让节点变为可拖拽;
4. 更多属性配置查看element官网
https://element.eleme.cn/#/zh-CN/component/tree