前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >vue3 实现对el-tree的增删改查

vue3 实现对el-tree的增删改查

作者头像
zhouzhouya
发布2023-10-26 17:27:17
8240
发布2023-10-26 17:27:17
举报
文章被收录于专栏:一名前端开发一名前端开发

el-tree说简单很简单,说难也难,毕竟里面很多属性需要灵活运用。最近项目开发中运用到el-tree的相关操作,整理如下:

1. 先把实现的页面展示:

WechatIMG265.png
WechatIMG265.png

鼠标划入时的状态

image.png
image.png

点击新增时的状态

image.png
image.png

点击编辑时的状态

image.png
image.png

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

代码语言:javascript
复制
<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

代码语言:javascript
复制
<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

代码语言:javascript
复制
.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 {
      }
    }
本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 先把实现的页面展示:
  • 2. 需求介绍:
  • 3.思路讲解
    • 3.1出现...
      • 3.2给树节点添加属性
        • 3.3添加完节点后树形结构不自动收起
        • 4.完整代码
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
        http://www.vxiaotou.com