el-tree说简单很简单,说难也难,毕竟里面很多属性需要灵活运用。最近项目开发中运用到el-tree的相关操作,整理如下:
1. 先把实现的页面展示:
鼠标划入时的状态
点击新增时的状态
点击编辑时的状态
2. 需求介绍:
代码语言:javascript复制1)点击新增一级在el-tree的最底部出现输入框
2)鼠标划入树形节点时出现`...`,鼠标划入`...`时出现新增修改删除
3)点击新增时,输入框出现在当前节点的子节点的最下方,且输入框聚焦
4)现在el-tree的层级最多为5级,在第5级时只能出现编辑和删除,不可出现新增。
3.思路讲解
3.1出现...
鼠标划入的时候出现...
,在鼠标未输入时设置标签为visibility: hidden;当鼠标划入时设置visibility: visible;
3.2给树节点添加属性
代码语言:javascript复制const filterAddParms = (tree, paramsName) => {
if (!tree || !Array.isArray(tree)) return null; // 出口 3-1
return tree.map((item) => {
// 2-4 1-4
item[paramsName] = false; // 1-1, 2-1
item.children = filterAddParms(item.children, paramsName); // 1-2 2-2
return item; // 2-3 1-3
});
};
3.3添加完节点后树形结构不自动收起
这块遇到了一个坑,起初default-expand-all来控制节点的展开与收缩,但是添加完节点后改变default-expand-all绑定的值使节点打开却一直是自动关闭,后查阅文档需通过default-expanded-keys来实现默认展开的key数组
4.完整代码
html
<div class="add-folder" @click="addNodeTreeList">
<span class="add-folder-span">
<svg-icon icon-class="addIcon"></svg-icon>
新增一级
</span>
</div>
<div class="all">全部分类</div>
<div class="tree-show">
<el-tree
ref="treeRef"
:data="treeList"
node-key="id"
:props="defaultProps"
@node-click="handleNodeClick"
@node-collapse="nodeCollapse"
:default-expand-all="nodeShow"
:default-expanded-keys="defaultExpandedkeys"
accordion
>
<template #default="{ node, data }">
<div class="custom-tree-node">
<span v-if="!data.isAddNode">{{ node.label }}</span>
<el-dropdown class="edit-tree-dropdown" v-if="!data.isAddNode">
<span>
<a class="showEllipsis"> ... </a>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
><span @click.stop="addAllNode(node, data)"
>新增</span
></el-dropdown-item
>
<el-dropdown-item @click.stop="editAllNode(node, data)"
>编辑</el-dropdown-item
>
<el-dropdown-item @click.stop="delAllNode(node, data)"
>删除</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
//点击新增时的输入框
<el-input
v-model="newChildNode"
v-if="data.isAddNode"
@keyup.enter.stop.native="handleAddEnter(node, data)"
@blur="removeTreeNode(node, data)"
@change="handleAddNode(node, data)"
ref="addRef"
class="add-new-child-node">
</el-input>
//点击修改时的输入框
<el-input
v-model="data.name"
v-show="data.isEditNode"
@change="handleEditNode(node, data)"
@keyup.enter.stop.native="handleEditEnter(node.data)"
class="edit-child-node">
</el-input>
</div>
</template>
</el-tree>
//点击新增一级显示的输入框
<div class="add-input" v-if="inputShow">
<el-input
v-model="addNodeTree"
placeholder="输入中"
@change="addClassification"
@keyup.enter.stop.native="handleEnter"
ref="newNodeRef"
></el-input>
</div>
</div>
js
<script setup>
import { reactive, ref, onMounted, nextTick } from "vue";
// 获取列表
const treeList = ref([]);
const getTreeList = () => {
http.get("/获取树列表接口").then((res) => {
treeList.value = res.data.children;
filterAddParms(treeList.value, "isOper");
});
};
//点击树节点时触发的方法
const input = ref("");
const parentId = ref("");
const handleNodeClick = (node, data) => {
parentId.value = node.parentId;
};
// 点击新增一级(新增一级时parentid默认为0)
const newNodeRef = ref(null);
const addNodeTree = ref("");
const inputShow = ref(false);
const addNodeTreeList = () => {
inputShow.value = true;
setTimeout(() => {
newNodeRef.value && newNodeRef.value.focus();
}, 800);
};
//新增一级出现输入框时通过change事件输入内容请求方法添加节点
const addClassification = () => {
const data = {
name: addNodeTree.value,
parentId: 0,
};
console.log(addNodeTree.value === "");
if (addNodeTree.value != "") {
http.post("新增一级保存接口", data).then((res) => {
console.log(res);
inputShow.value = false;
getTreeList();
addNodeTree.value = "";
});
} else {
inputShow.value = false;
}
};
//此方法为用户在输入框不输入任何东西时回车输入框不显示
const handleEnter = () => {
inputShow.value = false;
};
//节点被关闭时触发的事件,节点关闭时输入框也消失
const nodeCollapse = (data) => {
console.log("data: ", data);
// 如果有input框, 删除节点
if (nodeShow.value) {
data.children.pop();
nodeShow.value = false;
}
};
//点击新增,出现输入框
const nodeShow = ref(false);
const addRef = ref(null);
const newChildNode = ref("");
const addAllNode = (node, data) => {
if (nodeShow.value) return;
nodeShow.value = true;
if (!data.children || !Array.isArray(data.children)) {
data.children = [];
}
// 展开
console.log("treeRef.value: ", data.id, treeRef.value);
//使树形结构图展开
node.expanded = true;
//为了使输入框出现在最下层。为树结构添加节点
nextTick(() => {
data.children.push({
isAddNode: true,
isOper: null,
name: "",
parentId: data.id,
});
setTimeout(() => {
addRef.value && addRef.value.focus();
}, 800);
});
console.log("data: ", data);
};
// 删除节点。添加完节点后删除节点
function removeTreeNode(node, data) {
const parent = node.parent;
const children = parent.data.children || parent.data;
const index = children.findIndex((d) => d.id === data.id);
children.splice(index, 1);
treeList.value = [...treeList.value];
nodeShow.value = false;
}
//输入框输入内容添加数据
const defaultExpandedkeys = ref([]);
const handleAddNode = (node, data) => {
defaultExpandedkeys.value.push(node.data.parentId);
const params = {
parentId: data.parentId,
name: newChildNode.value,
};
console.log("params: ", params);
http.post("点击新增接口", params).then((res) => {
console.log(addRef.value);
nextTick(() => {
data.isAddNode = false;
addRef.value && addRef.value.focus();
});
getTreeList();
newChildNode.value = "";
});
};
//此方法为用户在输入框不输入任何东西时回车输入框不显示
const handleAddEnter = (node, data) => {
// if(data.isAddNode) return
nextTick(() => {
data.isAddNode = false;
addRef.value && addRef.value.focus();
});
};
//点击编辑,出现输入框
const editAllNode = (node, data) => {
nextTick(() => {
data.isEditNode = true;
});
};
//编辑完之后请求接口保存编辑完的数据
const handleEditNode = (node, data) => {
console.log("node: ", node);
console.log(data);
const editParams = {
name: data.name,
id: data.id,
parentId: data.parentId,
};
console.log(editParams);
http.put("点击编辑接口", editParams).then((res) => {
nextTick(() => {
data.isEditNode = false;
});
getTreeList();
});
};
//此方法为用户在输入框不输入任何东西时回车输入框不显示
const handleEditEnter = (node, data) => {
nextTick(() => {
data.isEditNode = false;
});
};
</script>
//删除节点
const delAllNode = (node, data) => {
console.log("data: ", data);
// nextTick(() => {
// data.isDelNode = true;
// });
http.delete(`删除接口/${data.path}`).then((res) => {
console.log("res: ", res);
getTreeList();
});
};
代码语言:javascript复制引入的js文件
export default function useTree() {
// 给树的节点添加属性
// 递归
const filterAddParms = (tree, paramsName) => {
if (!tree || !Array.isArray(tree)) return null; // 出口 3-1
return tree.map((item) => {
// 2-4 1-4
item[paramsName] = false; // 1-1, 2-1
item.children = filterAddParms(item.children, paramsName); // 1-2 2-2
return item; // 2-3 1-3
});
};
return {
filterAddParms
}
}
css
.content-left {
width: 280px;
border-radius: 20px;
margin-right: 20px;
background: rgba(255, 255, 255, 0.6);
height: 100%;
.title {
padding-top: 15px;
font-size: 14px;
font-weight: 500;
color: #333333;
padding-left: 20px;
}
.el-input {
padding-left: 20px;
:deep(.el-input__wrapper) {
width: 240px;
height: 28px;
background: rgba(255, 255, 255, 0.6);
border-radius: 8px;
border: 1px solid #e1e1f0;
margin-right: 20px;
margin-top: 10px;
}
:deep(.el-input__inner) {
font-size: 12px;
color: #8894a8;
}
}
.el-divider {
margin-top: 12px;
margin-bottom: 15px;
}
.tree-show {
padding-left: 15px;
overflow-y: auto;
height: calc(100% - 180px);
.el-tree {
position: relative;
background: none;
:deep(.el-tree-node) {
position: relative;
}
:deep(.el-tree-node__content) {
height: 34px;
.el-input__wrapper {
margin-top: 0;
}
.custom-tree-node {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 20px;
width: 100%;
font-size: 14px;
.edit-tree-dropdown {
position: relative;
visibility: hidden;
.showEllipsis {
width: 18px;
height: 18px;
background: #fff;
border-radius: 50%;
vertical-align: text-top;
line-height: 14px;
display: block;
text-align: center;
color: #5d63da;
}
}
}
//此处为鼠标输入时显示三个点
&:hover {
.custom-tree-node {
.edit-tree-dropdown {
visibility: visible;
}
}
}
}
.add-new-child-node {
z-index: 20;
width: calc(100% - 10px);
padding-left: 0px;
background: rgba(255, 255, 255, 0.6);
:deep(.el-input__wrapper) {
background: #fff;
}
}
.edit-child-node {
width: calc(100% - 10px);
background: rgba(255, 255, 255, 0.6);
position: absolute;
padding-left: 10px;
left: -15px;
// top: 0;
:deep(.el-input__wrapper) {
background: #fff;
}
}
}
::v-deep {
/* 设置树形最外层的背景颜色和字体颜色 */
.is-leaf::before {
display: none;
}
}
}
.add-folder {
line-height: 32px;
height: 32px;
font-size: 14px;
cursor: pointer;
border-left: 3px solid transparent;
.add-folder-span {
background-repeat: no-repeat;
background-position: 0px 50%;
margin-left: 8px;
color: #5d63da;
.svg-icon {
font-size: 18px;
margin-left: 6px;
cursor: pointer;
vertical-align: -4px;
}
}
}
.all {
padding-left: 38px;
font-size: 14px;
font-weight: 400;
color: #666666;
padding-bottom: 5px;
font-size: 14px;
}
.add-input {
}
}