• vue项目 el-tree的界面自定义 实现增删改查操作


    一、介绍:el-tree在element文档中有查询全树的代码,本文主要是在此基础上添加了增加、删除、修改的界面样式与功能。

    二、具体来说:

    1、鼠标移动到树上显示删除和修改:

      点击删除,当前节点删除;
    
      点击修改,树的选中节点变成input可以重新输入名称并且右边出现取消或确认的icon。
    

    2、点击底部添加按钮,如果未选中树节点,则在最外层新增input进行名称输入,右边同样有取消或确认的icon。若选中树节点,则在选中节点下添加子节点,同样以input形式输入名称。
    image
    2、data里的代码:
    image
    3、节点的数据格式
    image
    4、methods里的代码:
    image
    image
    image
    image
    image
    image
    image
    项目要求,最多两层组:
    image
    image
    到这里这个整个代码就介绍完了,这里还有个坑。

    问题:当点击新增节点,再点击取消的icon,这时候观察fetchData方法中两处this.curNode的打印,会发现打印结果是不一样的。这个问题其实并不影响树功能的实现,因为在fetchData中获取数据以后,进行了this.curNode=undefined。只是出于好奇,为什么会出现这个现象?
    image
    分析:观察cancelUpdate执行后是不是走了其他方法?没有,也没有触发watch。会不会是引用数据类型的地址引用问题?发现没有。最后发现,是因为点击取消的时候冒泡了!!触发了handleNodeClick方法。
    解决:
    image
    image
    五、demo代码(以下代码根据小伙伴需要已经改成不限深度的模板组插入,并把原来深度为2的模板组插入改成了注释)

    <template>
      <div>
        <el-input placeholder="请搜索" v-model="filterText"></el-input>
        <el-tree
          :highlight-current="true"
          class="filter-tree"
          :data="templateTree"
          :props="defaultProps"
          default-expand-all
          :filter-node-method="filterNode"
          ref="tree"
           @node-click="handleNodeClick"
          :expand-on-click-node="false"
          :render-content="isUpdateGroup ? updateRenderContent :renderContent"
        ></el-tree>
        <el-button type="primary" @click="handleAddGroup">添加组</el-button>
      </div>
    </template>
     <script>
    export default {
      name:'tree',
      data() {
        return {
          templateTree: [
            //树的数据
            {
              id: "1",
              text: "模板组1",
              nodeId: "11",
              depth: 1,
              typeName: "模板组",
              childrenNum: 2,
              nodes: [
                {
                  id: "12",
                  text: "模板组2",
                  nodeId: "122",
                  depth: 2,
                  typeName: "模板组",
                  childrenNum: 0,
                  nodes: [
                    {
                      id: "21",
                      text: "模板组3",
                      nodeId: "144",
                      depth: 3,
                      typeName: "模板",
                      childrenNum: 0,
                      nodes:[]
                    }
                  ]
                },
                {
                  id: "13",
                  text: "模板1",
                  nodeId: "133",
                  depth: 2,
                  typeName: "模板",
                  childrenNum: 0
                }
              ]
            }
          ],
          defaultProps: {
            children: "nodes",
            label: "text"
          },
          isact: "", //当前hover的节点
          isactTitle: "", //记录修改节点名称
          curNode: undefined, //当前选中节点
          isUpdateGroup: false, //是否在修改模板组
          filterText: "",
          indexRecord:[],//记录节点轨迹
          isBreak:false,//是否结束循环
        };
      },
      watch: {
        filterText(val) {
          this.$refs.tree.filter(val);
        }
      },
      methods: {
        filterNode(value, data) {
          if (!value) return true;
          return data.text.indexOf(value) !== -1;
        },
     
        renderContent(h, { node, data, store }) {
          return (
            <span
              style="flex: 1; display: flex; align-items: center; justify-content: space-between; padding-right: 8px;"
              on-mouseenter={() => this.mouseenteract(data)}
              on-mouseleave={() => this.mouseleaveact(data)}
            >
              <span>
                <span>{node.label}</span>
              </span>
              {this.isact == data ? (
                <span>
                  {this.isact.typeName == "模板组" ? (
                    <el-button
                      class="m-r-10"
                      type="text"
                      icon="el-icon-edit"
                      on-click={() => this.handleUpdateGroup(node, data)}
                    ></el-button>
                  ) : (
                    <span></span>
                  )}
                  <el-button
                    type="text"
                    icon="el-icon-delete"
                    on-click={(e) => this.handleDelete(node, data, e)}
                  ></el-button>
                </span>
              ) : (
                <span></span>
              )}
            </span>
          );
        },
        updateRenderContent(h, { node, data, store }) {
          return (
            <span style="flex: 1; display: flex; align-items: center; justify-content: space-between; padding-right: 8px;">
              <span>
                {this.isact == data ? (
                  <input
                    type="text"
                    onChange={this.handleChangeTitle.bind(this)}
                    value={node.label}
                  />
                ) : (
                  <span>{node.label}</span>
                )}
              </span>
              {this.isact == data ? (
                <span>
                  {this.isact.typeName == "模板组" ? (
                    <span>
                      <el-button
                        class="m-r-10"
                        type="text"
                        icon="el-icon-check"
                        on-click={() => this.updateGroup(node, data)}
                      ></el-button>
                      <el-button
                        class="m-r-10"
                        type="text"
                        icon="el-icon-close"
                        on-click={(e) => this.cancelUpdate(node, data, e)}
                      ></el-button>
                    </span>
                  ) : (
                    <span></span>
                  )}
                </span>
              ) : (
                <span></span>
              )}
            </span>
          );
        },
        //获取鼠标进入节点的数据
        mouseenteract(da) {
          this.isact = da;
        },
        mouseleaveact(da) {
          this.isact = "";
        },
        handleNodeClick(pdata) {
           this.curNode = pdata;
          document
            .getElementsByClassName("el-tree-node__content")[0]
            .setAttribute("class", "el-tree-node__content");
        },
        handleDelete(node, data, e) {
          e.stopPropagation();
          //存在则添加到子级
            const parent = node.parent;
            const children = parent.data.nodes || parent.data;
            //若parent.data是对象,操作的是子级;如果是数组,操作的是最外层
          if(Array.isArray(parent.data)){
            const parentIndex = parent.data.findIndex(d => d.id === data.id);
            parent.data.splice(parentIndex, 1);
          }else{
            const childIndex = children.findIndex(d => d.id === data.id) ;
            children.splice(childIndex, 1);
          }
          this.curNode = undefined;
        },
        //新增组
        handleAddGroup() {
          /**
           *  如果模版深度最多两层,取消该部分注释
           * //最多只有两层组 不可能添加在dept2的组上 curNode存在 并且  深度超过1
             if (this.curNode != undefined && this.curNode.depth != 1) { 
             this.$message.warning("不能添加超过两层");  
             return; 
           }
           */
          
           //如果isUpdateGroup 已经是true了 说明重复点击了
          if(this.isUpdateGroup){
            return;
          }
         
          let id = ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
            (
              c ^
              (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
            ).toString(16)
          );
          let newChild = {
            parentId: "", //如果有这个id 是插入第二层 否则是第一层 可有可无
            text: "", //必须有 this.templateContent.tempName
            nodes: [],
            id: id,
            typeName: "模板组",
            temporaryData: "1" //用来区分临时数据
          };
          /* 如果模版深度最多两层,以下条件改成该部分注释
            this.curNode && this.curNode.depth == 1 
          */
          this.indexRecord=[]
          if (this.curNode) {
            if(!this.curNode.nodes){
              this.$message.warning('模板不可添加')
              return
            }
            newChild.parentId = this.curNode.id;
             /* 如果模版深度最多两层,以下条件改成该部分注释
              const index = this.templateTree.findIndex(
                item => item.id == this.curNode.id
              );
              this.templateTree[index].nodes.push(newChild);
            */
             //找到tree中的index轨迹
             this.getTemplateTreeNode(this.curNode.id,this.templateTree, 0)
             //按照index轨迹插入节点
             this.insertNode(newChild, this.templateTree, this.indexRecord, this.indexRecord.length )
             this.isBreak = false
          } else if (this.curNode == undefined) {
            //没有选中的时候 添加到最外层
            newChild.depth = 1
            this.templateTree.push(newChild);
          }
          //调用出updateRender的input
          this.isact = newChild;
          this.isUpdateGroup = true;
        },
        //递归遍历获得选中node
        getTemplateTreeNode(target, list, dept){
          //空数组直接返回
          if(list.length == 0) return;
          let dataLen = list.length;
          for(let i = 0; i < dataLen; i++){
            //如果不匹配
            if(target != list[i].id){
              //存在nodes 遍历nodes里的节点
              if(list[i].nodes){
                this.indexRecord[dept] = i 
                let recordDept = dept+1
                this.getTemplateTreeNode(target,list[i].nodes, recordDept)
              }else{
                //不存在nodes 继续遍历
                continue;
              }
            }else{
              //匹配,则修改下标数组
              this.indexRecord[dept] = i 
              this.isBreak = true
              break
            }
            //删除不匹配的轨迹 如果已经break了说明已经找到正确的节点,就不用再删了
            if(!this.isBreak){
              this.indexRecord.pop()
            }
          }
        },
        //插入节点
        insertNode(insertChild, tree, indexArr, len){
          let index = indexArr.length - len
          if(len == 0) {
            tree.push(insertChild)
          } else{
            this.insertNode(insertChild, tree[indexArr[index]].nodes, indexArr, len-1)
          }
        },
        //修改组
        handleUpdateGroup() {
          this.isUpdateGroup = true;
        },
        //修改组名时获取title
        handleChangeTitle(e) {
          let value = e.target.value;
          this.isactTitle = value;
        },
        updateGroup(node, data) {
          //先handleChangeTitle获取title 再调用
          setTimeout(() => {
            if (this.isactTitle.trim() == "") {
              this.$message.warning("名称不能为空");
              return;
            }
            //修改数据组
            this.isUpdateGroup = false;
            const parent = node.parent;
            const children = parent.data.nodes || parent.data;
            const index = children.findIndex(d => d.id === data.id);
            let temp = data;
            temp.text = this.isactTitle;
            children.splice(index, 1, temp);
          }, 500);
        },
        cancelUpdate(node, data, e) {
          this.$message.info("已取消");
          this.isUpdateGroup = false;
          //如果是插入操作 需要移除数据
          if (this.isact.temporaryData) {
            this.handleDelete(node, data, e);
          }
        }
      }
    };
    </script>
    <style lang="scss" scoped>
    .el-tree-node__content {
      .el-button {
        display: none;
      }
    }
    .el-tree-node__content:hover {
      .el-button {
        display: inline;
      }
    }
    .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
      background-color: #eaebed;
      color: #4796ec;
      font-weight: bold;
    }
    .el-tree {
      height: 350px;
      overflow-y: auto !important;
      .el-tree-node__content span {
        text-overflow: ellipsis;
        white-space: nowrap;
        overflow: hidden;
      }
    }
    </style>
    
    

    ————————————————
    版权声明:本文为CSDN博主「川上饺子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/outlierQiqi/article/details/106858900

  • 相关阅读:
    EntityFramework 插入自增ID主从表数据
    API接收数据反序列化失败问题查找
    Linq Group by获取数量和数据
    git 常用命令
    Redis安装及局域网访问配置CentOS
    Azure虚拟机网站部署 防火墙设置
    .NET基础拾遗(1)类型语法基础和内存管理基础【转】
    ASP.NET CSRF 解决【网摘】
    win7 oracle 冷恢复
    $.ajax中contentType: “application/json” 的用法[转]
  • 原文地址:https://www.cnblogs.com/wgy0528/p/15420647.html
Copyright © 2020-2023  润新知