我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情
el-tree说简单很简单,说难也难,毕竟里面很多属性需要灵活运用。最近项目开发中运用到el-tree的相关操作,整理如下:
1. 先把实现的页面展示:
鼠标划入时的状态
点击新增时的状态
点击编辑时的状态
2. 需求介绍:
go代码解读复制代码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
ini代码解读复制代码<div @click="addNodeTreeList"> <span > <svg-icon icon-></svg-icon> 新增一级 </span> </div> <div >全部分类</div> <div > <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 > <span v-if="!data.isAddNode">{{ node.label }}</span> <el-dropdown v-if="!data.isAddNode"> <span> <a > ... </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" > </el-input> //点击修改时的输入框 <el-input v-model="data.name" v-show="data.isEditNode" @change="handleEditNode(node, data)" @keyup.enter.stop.native="handleEditEnter(node.data)" > </el-input> </div> </template> </el-tree> //点击新增一级显示的输入框 <div v-if="inputShow"> <el-input v-model="addNodeTree" placeholder="输入中" @change="addClassification" @keyup.enter.stop.native="handleEnter" ref="newNodeRef" ></el-input> </div> </div>
js
ini代码解读复制代码<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
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 { } }