欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

vue封装可实现选中、新增、修改、删除功能的树组件

程序员文章站 2024-03-17 20:26:22
...

我们在开发项目的时候经常会遇到一些树形结构的数据结构,比如部门、班级、关系等等层级结构的数据,通常常规的展示方式一般是通过树的结构来展示出来,现在我们自己就来实现一个可以新增、修改、删除的树。

实现的过程很简单,树一般都会采用递归思路,然后将数据展示出来。

<div class="tree" id="Tree" ref="TreeNode">
    <ul>
      <li v-for="(v, n) in treeData" :key="n">
        <span @click="nodeClick(v, $event)" :class="currentId === v.id ? 'active' : ''">
          <strong v-if="!v.isExpend" @click.stop="expendNode(v)">+</strong>
          <strong v-if="v.isExpend" @click.stop="expendNode(v)">-</strong>
          {{v.name}}
          <em @click.stop="addNode(v)" class="del-btn add-btn" v-if="isEdit">新增</em>
          <em @click.stop="editNode(v)" class="del-btn edit-btn" v-if="isEdit">编辑</em>
          <em @click.stop="delNode(v)" class="del-btn del" v-if="isEdit">删除</em>
        </span>
        <Tree
          :treeData="v.children"
          :isEdit="isEdit"
          v-if="v.children"
          v-show="v.isExpend"
          @nodeClick="nodeClick"
          @expendNode="expendNode"
          @delNode="delNode"
          @addNode="addNode"
          @editNode="editNode"
        />
      </li>
    </ul>
  </div>

然后抛出对应的节点,通过递归的方式获取到对应的数据结构的节点,然后就可以对节点进行增加,修改,查询了。

// 递归操作根据id获取节点操作
    getNode (data, id, type) {
      let that = this;
      data.map((item, index) => {
        if (item.id == id) {
          that.currentNode = item; // 结果赋值
          // 删除操作
          if (type === 'del') {
            if (window.confirm('确定要删除节点?')) {
              data.splice(index, 1);
              alert('删除成功!');
            }
          }
          // 展开收缩
          if (type === 'expend') {
            item.isExpend = !item.isExpend;
          }
          // 编辑操作
          if (type === 'edit') {

          }
          // 新增操作
          if (type === 'add') {
            if (!item.children) {
              that.$set(item, 'children', []);
            }
            item.children.push({
              id: new Date().getTime(), // 用时间戳标识唯一id,具体情况可以根据自己定一个唯一标识
              name: '新增节点' + new Date().getTime(),
              isExpend: false
            });
            // 当没有子节点的时候新增一个节点后展开节点
            if (item.children.length > 0) {
              item.isExpend = true;
            }
          }
        } else {
          if (item.children) {
            this.getNode(item.children, id, type);
          }
        }
      });
    }

整个过程主要的思路就是递归遍历输出树,然后通过点击节点递归获取树中对应的节点。

整个代码如下:

Tree组件部分:

<template>
  <div class="tree" id="Tree" ref="TreeNode">
    <ul>
      <li v-for="(v, n) in treeData" :key="n">
        <span @click="nodeClick(v, $event)" :class="currentId === v.id ? 'active' : ''">
          <strong v-if="!v.isExpend" @click.stop="expendNode(v)">+</strong>
          <strong v-if="v.isExpend" @click.stop="expendNode(v)">-</strong>
          {{v.name}}
          <em @click.stop="addNode(v)" class="del-btn add-btn" v-if="isEdit">新增</em>
          <em @click.stop="editNode(v)" class="del-btn edit-btn" v-if="isEdit">编辑</em>
          <em @click.stop="delNode(v)" class="del-btn del" v-if="isEdit">删除</em>
        </span>
        <Tree
          :treeData="v.children"
          :isEdit="isEdit"
          v-if="v.children"
          v-show="v.isExpend"
          @nodeClick="nodeClick"
          @expendNode="expendNode"
          @delNode="delNode"
          @addNode="addNode"
          @editNode="editNode"
        />
      </li>
    </ul>
  </div>
</template>

<script>
import Tree from './tree.vue';
export default {
  name: 'Tree',
  data () {
    return {
      currentId: 1 // 高亮当前节点标识
    };
  },
  computed: {
  },
  props: ['treeData', 'isEdit'],
  components: { Tree },
  created () {
  },
  mounted () {
  },
  methods: {
    // 删除节点
    delNode (v) {
      this.$emit('delNode', v);
    },
    // 新增节点
    addNode (v) {
      this.$emit('addNode', v);
    },
    // 修改节点
    editNode (v) {
      this.$emit('editNode', v);
    },
    // dom方法设置当前节点高亮显示
    setCurrentNode (node) {
      document.querySelector('#Tree span.active').classList.remove('active');
      node.classList.add('active');
    },
    // 点击当前节点
    nodeClick (v, $event) {
      this.setCurrentNode($event.target);
      this.$emit('nodeClick', v, $event);
    },
    // 收缩展开节点
    expendNode (v) {
      this.$emit('expendNode', v);
    }
  }
};
</script>
<style lang="less" scoped>
  .tree {
    user-select: none;
    ul li {
      padding:15px 0 0 15px;
      span {
        &.active {
          color:red;
          font-weight: bolder;
        }
        &:hover {
          cursor: pointer;
        }
        display:block;
        font-size: 20px;
        padding: 15px 0;
        color: #fff;
        strong {
          margin-right: 20px;
          display:inline-block;
          width: 20px;
          height: 20px;
          background:#909090;
          color:#333;
          font-size: 20px;
          text-align:center;
          line-height:20px;
          border-radius:10px;
        }
        em {
          margin-left: 15px;
          font-style:normal;
          color:#1a63de;
        }
        .add-btn {
          color: green;
        }
        em.del {
          color: red;
        }
      }
    }
  }
</style>

外层引入:

<template>
  <div id="app">
    <div class="loading" v-if="this.$store.state.loadingFlag">
      <img src="static/images/loading.gif" />
    </div>
    <div id="mainBox">
      <Tree
        :treeData="treeData"
        :isEdit="isEdit"
        @nodeClick="nodeClick"
        @expendNode="expendNode"
        @delNode="delNode"
        @addNode="addNode"
        @editNode="editNode"
      />
      <router-view />
    </div>
  </div>
</template>

<script>
import Tree from '@/pages/tree/Tree.vue';
export default {
  name: 'App',
  data () {
    return {
      isEdit: true,
      currentNode: null, // 当前点击的节点
      treeData: [
        {
          id: 1,
          name: '一级标题 1',
          isExpend: true,
          children: [
            {
              id: 2,
              name: '二级标题 2-1',
              isExpend: true,
              children: [
                {
                  id: 5,
                  name: '三级标题 3-1',
                  isExpend: true,
                  children: [
                    {
                      id: 7,
                      name: '四级标题 4-1',
                      isExpend: false
                    },
                    {
                      id: 8,
                      name: '四级标题 4-2',
                      isExpend: false
                    }
                  ]
                },
                {
                  id: 6,
                  name: '三级标题 3-1',
                  isExpend: false
                }
              ]
            },
            {
              id: 3,
              name: '一级标题 2-1',
              isExpend: false
            },
            {
              id: 4,
              name: '一级标题 2-1',
              isExpend: false
            }
          ]
        }
      ]
    };
  },
  components: { Tree },
  methods: {
    // 递归操作根据id获取节点操作
    getNode (data, id, type) {
      let that = this;
      data.map((item, index) => {
        if (item.id == id) {
          that.currentNode = item; // 结果赋值
          // 删除操作
          if (type === 'del') {
            if (window.confirm('确定要删除节点?')) {
              data.splice(index, 1);
              alert('删除成功!');
            }
          }
          // 展开收缩
          if (type === 'expend') {
            item.isExpend = !item.isExpend;
          }
          // 编辑操作
          if (type === 'edit') {

          }
          // 新增操作
          if (type === 'add') {
            if (!item.children) {
              that.$set(item, 'children', []);
            }
            item.children.push({
              id: new Date().getTime(), // 用时间戳标识唯一id,具体情况可以根据自己定一个唯一标识
              name: '新增节点' + new Date().getTime(),
              isExpend: false
            });
            // 当没有子节点的时候新增一个节点后展开节点
            if (item.children.length > 0) {
              item.isExpend = true;
            }
          }
        } else {
          if (item.children) {
            this.getNode(item.children, id, type);
          }
        }
      });
    },
    expendNode (v) {
      this.getNode(this.treeData, v.id, 'expend');
    },
    nodeClick (v) {
      console.log(v);
    },
    delNode (v) {
      this.getNode(this.treeData, v.id, 'del');
    },
    addNode (v) {
      this.getNode(this.treeData, v.id, 'add');
      alert('新增成功!');
    },
    editNode (v) {
      console.log(v);
    }
  },
  created () {
  },
  watch: {
    $route (to) {
      console.log(to);
      if (to.path.includes('login')) {
        this.hidden = false;
      } else {
        this.hidden = true;
      }
    }
  }
};
</script>

<style lang="less">
@import "style/common/reset";
@import "style/common/base";
</style>
<style lang="less" scoped>
#app {
  position: absolute;
  height: 100%;
  width: 100%;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 120px;
  overflow-y: auto;
  background: url("static/images/back.png");
  background-size: 100%;
  background-image: linear-gradient(#030303, #170403);
  .loading {
    position: fixed;
    z-index: 999999;
    width: 100%;
    height: 100%;
    text-align: center;
    background: rgba(0, 0, 0, 0.75);
    img {
      position: relative;
      top: 50%;
      transform: translateY(-50%);
      height: 150px;
    }
  }
}
</style>

结果如下:

vue封装可实现选中、新增、修改、删除功能的树组件

编辑部分我没有具体去一一实现,原理都是一样的传参进行修改,还有一天就要上班了,最近冠状病毒太厉害了,回去的时候200多个确诊,现在已经达到35000多个了,搞得人心慌慌,真有点像生化危机的感觉,希望病毒尽快消失,生活尽快回复正常。

结束语: 努力,努力,再努力! ( ̄_ ̄ )

相关标签: Vuejs