精华内容
下载资源
问答
  • 主要为大家详细介绍了Vue组件tree实现树形菜单,小巧实用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 新项目使用了ant-design-vue组件库.该组件库完全根基数据双向绑定的模式实现.只有表单组件提供少量的方法.所以,在使用ant-design-vue时,一定要从改变数据的角度去切换UI显示效果.然而,在树形控件a-tree的使用上,单...
  • tree组件可实现节点拖拽、双击编辑、右键操作等等
  • 主要为大家详细介绍了Vue.js组件tree实现无限级树形菜单代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要为大家详细介绍了Vue2组件tree实现无限级树形菜单,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 主要介绍了vue el-tree 默认展开第一个节点的实现代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
  • Vue.D3.tree Vue组件可基于D3.js布局显示图形。 Tree Live演示https://david-desmaisons.github.io/Vue.D3.tree/tree用法 从'vued3tree'导入{tree}导出默认{组件:{树},data(){返回{树:{名称:“父亲”,子代:...
  • Vue实现一个Tree组件

    千次阅读 2020-12-26 23:20:39
    Tree组件在实际应用非常广泛,例如省市县地域的展现.一般一些包含从属关系的数据都可以使用Tree组件来进行展示,下面通过一个实战的demo来深入学一下实现一个Tree组件所要了解的原理和实现细节.本文实现的功能包含...

    前言

    Tree组件在实际应用中非常广泛,例如省市县地域的展现.一般一些包含从属关系的数据都可以使用Tree组件来进行展示,下面通过一个实战的demo来深入学一下实现一个Tree组件所要了解的原理和实现细节.本文实现的功能包含以下三点.

    • 实现一个基础版可以显示嵌套数据的Tree组件
    • 点击Tree组件的某一级标签它的下一级数据支持异步加载
    • Tree组件的节点支持拖拽

    最终Demo的效果图如下.

    基础版的Tree

    实现一个基础版的Tree组件十分简单,原理就是掌握组件嵌套的使用方法.

    外部调用

    首先设置外部调用Tree组件的模板如下.Tree组件只需要传入一个data属性,就可以将data数据渲染成相应的树形结构.

    <template>
      <Tree
        :data="data"
      />
    </template>
    <script>
    import Tree from "../../components/Tree/index.vue";
    export default {
      data() {
        return {
          data: [
            {
              label: "一级",
              children: [
                {
                  label: "二级1",
                  children: [
                    {
                      label: "三级1",
                    },
                  ],
                }
              ],
            }
          ],
        };
      },
      components: {
        Tree,
      }
     }
     </script>
    

    Tree组件的实现

    Tree组件包含两个文件,一个是index.vue,另一个文件是Tree.vue.

    index.vue

    index.vue内容如下,它是作为Tree组件与外部衔接的桥梁,很多扩展功能都可以在这个中间层进行处理,而Tree.vue只需要处理数据展现相关的逻辑.

    <template>
      <div class="tree">
        <template v-for="(item, index) in data_source">
          <Tree :item="item" :key="index" class="first-layer" />
        </template>
      </div>
    </template>
    <script>
    import Tree from "./Tree";
    import { deepClone } from "../util/tool.js"; //深度克隆函数
    export default {
      props: {
        data: Object | Array,
      },
      data() {
        return {
          tree_data: deepClone(this.data),
        };
      },
      computed: {
        data_source() {
          if (Array.isArray(this.tree_data)) {
            return this.tree_data;
          } else {
            return [this.tree_data];
          }
        },
      },
      components: {
        Tree,
      }
     }
     </script>
    

    上方代码将外部传入的data数据做了深度克隆赋值给tree_data.如果组件内部存在一些改变数据的功能,那么就可以直接操作tree_data,而不会影响外界传入的数据.

    为了让Tree组件支持数组渲染,也支持对象渲染,新增了一个计算属性data_source将数据源最终都转化成数组,再遍历渲染Tree组件;

    Tree.vue

    Tree.vue文件里面包含具体渲染树形结构数据的具体代码.它的模板内容分为两部分,一个是渲染label对应的标题内容,另外一个渲染子级.

    在组件中设置一个状态is_open用来控制它的下一级是处于打开还是关闭状态.

    getClassName通过is_open可以渲染出相应的类名来显示三角形的图标是向下还是向右显示.

    Tree.vue中设置一个name属性Tree,接下来就可以在模板中嵌套调用自己了.通过遍历item.children,将每一层级的数据复制给Tree组件,就达到了渲染树形结构数据的目的.

    <template>
      <div class="tree">
        <div
          class="box"
          @click="toggle()"
        >
          <div :class="['lt', getClassName]"></div>
          <div class="label lt">{{ item.label }}</div>
        </div>
        <div class="drop-list" v-show="is_open">
          <template v-for="(child, index) in item.children">
            <Tree :item="child" :key="index" />
          </template>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Tree",
      props: {
        item: Object
      },
      data() {
        return {
          is_open: false, //是否打开下一级
        };
      },
      computed: {
        getClassName(){
            return this.is_open ? "down" : "right";
        }
      },
      methods:{
         toggle() {
           this.is_open = !this.is_open;
        },
      }
    };
    </script>
    

    渲染结果如下:

    异步加载

    上面的基础版Tree组件只支持基本的数据渲染,外部需要先把数据准备好,再扔给它就能将对应的数据渲染出来.

    假设数据是保留在服务器端的,我希望点击label的时候它会去请求服务器,在等待期间会将Tree组件点击的那一级显示loading...的字样,等到数据完全返回再渲染子级.如下图所示.

    Tree组件本身是无法知晓去请求何处的数据,因此请求获取数据的逻辑属于自定义内容,需要用户自己编写.最好这部分逻辑封装成一个函数传给Tree组件.当Tree组件的label被点击时,Tree组件检测到需要异步请求就会直接调用传递过来的函数,请求成功后再将数据添加到自己的tree_data数据源上,让页面重新渲染.将步骤拆分如下.

    • 外部定义数据加载函数传递给Tree组件.
    • Tree组件的label点击时,触发数据加载函数,并将状态更新为loading...,等待响应结果.
    • 响应数据返回后再更新整个tree_data触发界面重新渲染.

    外部定义数据加载函数

    模板template中新增两个属性lazyload.

    lazy传递给子组件指定数据渲染为异步,load属性对应的函数loadNode为获取数据的函数,传递给Tree组件使用.

    <template>
      <Tree
        :data="data"
        :load="loadNode"
        :lazy="true"
      />
    </template>
    

    loadNode函数我们设计时设定会返回两个参数node对象和resolve函数.node对象又包含两个属性layerchildren.

    layer是点击label标签时该标签位于第几级的级数,而children是下一级的数据.如下代码所示data第一级的children有数据,用户点击第一级的label时,第一级的children数据就能通过node对象获取到.

    resolve函数执行会将最终结果传递给Tree组件.下面的代码可以描述为当用户点击第一级的标签时,直接返回data中定义的初始数据,而点击其他层级标签时,会执行定时器里面的异步操作,将resolve包裹的数据传递给Tree组件渲染.

    外部调用文件

     data(){
       return {
         label:"一级数据"
         children:[{
           label:"二级数据"
         }]
       }
     },
     methods: {
        loadNode(node, resolve) {
          const { layer, children } = node;
          if (layer <= 1) {
            resolve(children);
          } else {
            setTimeout(() => {
              resolve([
                {
                  title: `第${layer}层`,
                },
              ]);
            }, 1500);
          }
        },
     }
    

    Tree组件处理加载函数

    Tree.vue文件中新增两个属性loadingloaded,用来指示加载的状态.当loading为true时,模板就会渲染加载中...的字样.

    一旦接受的到的lazy为true时,通过执行外部定义的数据加载函数this.load来获取异步数据.this.load接受两个参数dataresolve函数.

    Tree.vue文件

     props: {
        item: Object 
     },
     data() {
        return {
          is_open: false, //是否打开下一级
          loading: false, //是否加载中
          loaded: false, //是否加载完毕
        };
     },
     methods:{
        toggle(){ //点击label时触发
           if(this.lazy){ //异步请求数据
            if (this.loading) {
              //正在加载中
              return false;
            }
            this.loading = true;
            const resolve = (data) => {
              this.is_open = !this.is_open;
              this.loading = false;
              this.loaded = true;
              this.updateData({ data, layer: this.layer });
            };
            const data = { ...this.item, layer: this.layer.length };
            this.load(data, resolve);//执行数据加载函数
           }else{
             ...
           }
         }	
     }
    

    const data = { ...this.item, layer: this.layer.length };

    this.item存储了当前级的数据.this.layer存储了当前级的索引数组,它的数组长度就是对应的层级数.this.layer详细描述如下.

    假设数据源data如下.用户点击了2-2级标签,this.layer值为[0,1].通过this.layer可以追踪到该级数据处于数据源的索引集合.

      data = [{
        label:"1级",
        children:[{
           label:"2-1级"
        },{
           label:"2-2级"
        }}]
      }]
    

    this.load会将resolve函数作为参数传递进去,一旦异步数据加载完毕resolve函数就会执行.将loaded状态更新为true,loading更新为false.随后执行祖先传递过来的this.updateData函数,并传入异步返回的结果data.this.updateData执行会更新Tree组件的根级数据tree_data,从而重新渲染组件树.

    更新tree_data

    updateData函数获取到子级传递过来的异步响应的数据data和索引数组layer.通过这两个参数就可以将data更新到根节点的数据源上.

    getTarget函数的作用就是根据索引数组找到数组对应的最后一级的对象.例如layer的值为[0,0],而result的值为

    [
      {
        label:"第一级",
        children:[{
          label:"第二级"
        }}]
      }
    ]
    

    getTarget(layer,result)执行的结果就会返回label"第二级"的那个对象.一旦操作这个对象的数据,result的数据就会相应变化.

    index.vue文件

     methods:{
        updateData(data) {
          const { data: list, layer } = data;
    
          let result = [...this.data_source];
    
          const tmp = this.getTarget(layer, result);//根据索引数组和数据源找到那一级的数据
    
          tmp.children = list;
    
          this.tree_data = result;
        }
     }
    

    通过getTarget函数找到那一级的数据tmp,将其children更新为list,并将result重新赋值给tree_data.这样异步请求的数据就加到了数据源上.

    节点拖拽

    节点的拖拽可以借助HTML5中的拖拽API轻松实现.在dom元素上添加draggable="true"时,那么就表明该元素允许拖拽了.

    HTML5中的拖拽API还包含几个事件监听函数,比如dragstart,drop,dragover等.

    • dragstart事件会在鼠标按住某dom元素即将拖拽时触发,它的事件对象e支持调用e.dataTransfer.setData函数来设置参数值.它是绑定在被按住的dom元素上的事件.
    • dragover是在鼠标按住某个dom元素后拖拽过程中触发的函数.
    • drop事件会在鼠标拖拽某个dom元素到另外一个dom元素上方释放时触发.它是绑定在另一个dom元素的监听事件,它的事件对象e可以通过e.dataTransfer.getData函数得到dragstart内部设置的参数值.

    Tree组件的所有节点全部绑定dragstartdrop事件,一旦移动某个节点1到另外一个节点2上时.通过dragstart函数可以捕捉到节点1的所有数据信息,并通过e.dataTransfer.setData存储起来.

    节点2监听到节点1在其上方释放,drop事件就会触发.在drop事件内部,它本身就可以得到当前节点(也就是节点2)的数据信息,另外还可以通过e.dataTransfer.getData获取到节点1的数据信息.

    如果同时得到了节点1和节点2的数据信息时,那相当于清楚知道了在根数据源tree_data上需要将某个数据对象移到另一个数据对象的下面.最终移动dom节点的问题就转化成了操作tree_data的问题.

    绑定拖拽事件

    首先在模板上给每个dom节点设置draggable="true"属性,让所有节点都支持拖拽.同时绑定三个事件函数dragstart,dropdragover.

    Tree.vue

    <template>
    ...
    <div
          class="box"
          @click="toggle()"
          @dragstart="startDrag"
          @drop="dragEnd"
          @dragover="dragOver"
          draggable="true"
    >
    ...
    </template>
    

    startDrag事件中存储数组索引this.layer,由于e.dataTransfer.setData不支持存储引用型数据,因此要使用JSON.stringify转化一下.

    dragOver事件里面必须要调用一下e.preventDefault(),否则dragEnd函数不会触发.

    dragEnd函数得到了两个节点的数据,开始调用祖先的方法dragData更新tree_data,这里的祖先方法dragData是通过provide,inject机制传递给后代使用的,可在最后面全部代码中看到.

     methods: {
        dragOver(e) {
          e.preventDefault();
        },
        startDrag(e) {
          e.dataTransfer.setData("data", JSON.stringify(this.layer));
        },
        dragEnd(e) {
          e.preventDefault();
          const old_layer = JSON.parse(e.dataTransfer.getData("data"));
          this.dragData(old_layer, this.layer, this);
        }
     }   
    

    更新Tree_data

    dragData执行的过程就是将被拖拽节点的数据对象添加到新节点的数据对象的children数组中.

    通过this.getTarget找到了两个节点的数据对象,运行new_obj.children.unshift(old_obj);,将旧数据对象添加到新对象的children数组下面.另外还要将原来位置下的旧数据对象删除,否则旧数据对象就会存在两份.

    想删除原来位置下的旧数据对象就必须找到它的父级数据对象和它排在父级的子代数组下的索引值,找到后就可以将使用splice将原来位置的旧数据对象删掉.最终将修改过的数据赋值给tree_data.

    index.vue文件

     methods: {
        dragData(old_layer, new_layer, elem) {
         
          let result = [...this.data_source];
    
          const old_obj = this.getTarget(old_layer, result);
    
          const new_obj = this.getTarget(new_layer, result);
          
          //找到被拖拽数据对象的父级数据对象
          const old_obj_parent = this.getTarget(
            old_layer.slice(0, old_layer.length - 1),
            result
          );
    
          const index = old_layer[old_layer.length - 1]; //获取倒数第二个索引
    
          if (!new_obj.children) {
            new_obj.children = [];
          }
    
          if (Array.isArray(old_obj_parent)) {
            old_obj_parent.splice(index, 1);
          } else {
            old_obj_parent.children.splice(index, 1); //删掉原来位置的被拖拽数据
          }
    
          new_obj.children.unshift(old_obj); //将被拖拽的数据加到目标处
    
          this.tree_data = result;
    
        }
        ...
     }   
    

    完整代码

    index.vue

    <template>
      <div class="tree">
        <template v-for="(item, index) in data_source">
          <Tree :item="item" :key="index" :layer="[index]" class="first-layer" />
        </template>
      </div>
    </template>
    
    <script>
    import Tree from "./Tree";
    export default {
      props: {
        data: Object | Array,
        label: {
          type: String,
          default: "label",
        },
        children: {
          type: String,
          default: "children",
        },
        lazy: {
          type: Boolean,
          default: false,
        },
        load: {
          type: Function,
          default: () => {},
        },
      },
      provide() {
        return {
          label: this.label,
          children: this.children,
          lazy: this.lazy,
          load: this.load,
          updateData: this.updateData,
          dragData: this.dragData,
        };
      },
      data() {
        return {
          tree_data: this.data,
        };
      },
      computed: {
        data_source() {
          if (Array.isArray(this.tree_data)) {
            return this.tree_data;
          } else {
            return [this.tree_data];
          }
        },
      },
      components: {
        Tree,
      },
      methods: {
        dragData(old_layer, new_layer, elem) {
          //数据拖拽
          const flag = old_layer.every((item, index) => {
            return item === new_layer[index];
          });
          if (flag) {
            //不能将元素拖拽给自己的子元素
            return false;
          }
    
          let result = [...this.data_source];
    
          const old_obj = this.getTarget(old_layer, result);
    
          const new_obj = this.getTarget(new_layer, result);
    
          const old_obj_parent = this.getTarget(
            old_layer.slice(0, old_layer.length - 1),
            result
          );
    
          const index = old_layer[old_layer.length - 1]; //获取倒数第二个索引
    
          if (!new_obj[this.children]) {
            new_obj[this.children] = [];
          }
    
          if (Array.isArray(old_obj_parent)) {
            old_obj_parent.splice(index, 1);
          } else {
            old_obj_parent[this.children].splice(index, 1); //原来位置的被拖拽数据删掉x
          }
    
          new_obj[this.children].unshift(old_obj); //将被拖拽的数据加到目标处
    
          this.tree_data = Array.isArray(this.tree_data) ? result : result[0];
    
          this.$nextTick(() => {
            !elem.is_open && elem.toggle(); //如果是关闭状态拖拽过去打开
          });
        },
        getTarget(layer, result) {
          if (layer.length == 0) {
            return result;
          }
          let data_obj;
          Array.from(Array(layer.length)).reduce((cur, prev, index) => {
            if (!cur) return null;
            if (index == 0) {
              data_obj = cur[layer[index]];
            } else {
              data_obj = cur[this.children][layer[index]];
            }
            return data_obj;
          }, result);
          return data_obj;
        },
        updateData(data) {
          const { data: list, layer } = data;
    
          let result = [...this.data_source];
    
          const tmp = this.getTarget(layer, result);
    
          tmp[this.children] = list;
    
          this.tree_data = Array.isArray(this.tree_data) ? result : result[0];
        },
      },
    };
    </script>
    
    <style lang="scss" scoped>
    .first-layer {
      margin-bottom: 20px;
    }
    </style>
    

    Tree.vue

    <template>
      <div class="tree">
        <div
          class="box"
          @click="toggle()"
          @dragstart="startDrag"
          @drop="dragEnd"
          @dragover="dragOver"
          draggable="true"
        >
          <div :class="['lt', getClassName()]"></div>
          <div class="label lt">{{ item[label] }}</div>
          <div class="lt load" v-if="loading_status">loading...</div>
        </div>
        <div class="drop-list" v-show="show_next">
          <template v-for="(child, index) in item[children]">
            <Tree :item="child" :key="index" :layer="[...layer, index]" />
          </template>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: "Tree",
      props: {
        item: Object,
        layer: Array,
      },
      inject: ["label", "children", "lazy", "load", "updateData", "dragData"],
      data() {
        return {
          is_open: false, //是否打开下一级
          loading: false, //是否加载中
          loaded: false, //是否加载完毕
        };
      },
      computed: {
        show_next() {
          //是否显示下一级
          if (
            this.is_open === true &&
            (this.loaded === true || this.lazy === false)
          ) {
            return true;
          } else {
            return false;
          }
        },
        loading_status() {
          //控制loading...显示图标
          if (!this.lazy) {
            return false;
          } else {
            if (this.loading === true) {
              return true;
            } else {
              return false;
            }
          }
        },
      },
      methods: {
        getClassName() {
          if (this.item[this.children] && this.item[this.children].length > 0) {
            return this.is_open ? "down" : "right";
          } else {
            return "gap";
          }
        },
        dragOver(e) {
          e.preventDefault();
        },
        startDrag(e) {
          e.dataTransfer.setData("data", JSON.stringify(this.layer));
        },
        dragEnd(e) {
          e.preventDefault();
          const old_layer = JSON.parse(e.dataTransfer.getData("data"));
          this.dragData(old_layer, this.layer, this);
        },
        toggle() {
          if (this.lazy) {
            if (this.loaded) {
              //已经加载完毕
              this.is_open = !this.is_open;
              return false;
            }
            if (this.loading) {
              //正在加载中
              return false;
            }
            this.loading = true;
            const resolve = (data) => {
              this.is_open = !this.is_open;
              this.loading = false;
              this.loaded = true;
              this.updateData({ data, layer: this.layer });
            };
            const data = { ...this.item, layer: this.layer.length };
            this.load(data, resolve);
          } else {
            this.is_open = !this.is_open;
          }
        },
      },
    };
    </script>
    <style lang="scss" scoped>
    .lt {
      float: left;
    }
    .load {
      font-size: 12px;
      margin-left: 5px;
      margin-top: 4px;
    }
    .gap {
      margin-left: 10px;
      width: 1px;
      height: 1px;
    }
    .box::before {
      width: 0;
      height: 0;
      content: "";
      display: block;
      clear: both;
      cursor: pointer;
    }
    @mixin triangle() {
      border-color: #57af1a #fff #fff #fff;
      border-style: solid;
      border-width: 4px 4px 0 4px;
      height: 0;
      width: 0;
    }
    .label {
      font-size: 14px;
      margin-left: 5px;
    }
    .down {
      @include triangle();
      margin-top: 8px;
    }
    .right {
      @include triangle();
      transform: rotate(-90deg);
      margin-top: 8px;
    }
    .drop-list {
      margin-left: 10px;
    }
    </style>
    

    外部调用Tree组件(测试文件)

    <template>
      <Tree
        :data="data"
        label="title"
        children="childrens"
        :load="loadNode"
        :lazy="true"
      />
    </template>
    
    <script>
    import Tree from "../../components/Tree/index.vue";
    export default {
      data() {
        return {
          data: [
            {
              title: "一级",
              childrens: [
                {
                  title: "二级1",
                  childrens: [
                    {
                      title: "三级1",
                    },
                  ],
                },
                {
                  title: "二级2",
                  childrens: [
                    {
                      title: "三级2",
                    },
                  ],
                },
              ],
            },
            {
              title: "一级2",
              childrens: [
                {
                  title: "二级2",
                },
              ],
            },
          ],
        };
      },
      components: {
        Tree,
      },
      methods: {
        loadNode(node, resolve) {
          const { layer, childrens } = node;
          if (childrens && childrens.length > 0) {
            resolve(childrens);
          } else {
            setTimeout(() => {
              resolve([
                {
                  title: `第${layer}层`,
                },
              ]);
            }, 1500);
          }
        },
      },
    };
    </script>
    <style>
    </style>
    
    展开全文
  • vue-drag-tree-org 内容列表 安装 # use npm npm install vue-drag-tree-org # use yarn yarn add vue-drag-tree-org 使用 # use import VueDragTreeOrg from 'vue-drag-tree-org' Vue.use(VueDragTreeOrg) # or use...
  • 1. 在tree/index.vue中: <template> <div> <ul> <item :model='data'></item> </ul> </div> </template> <script> import Item from './item' ...

    1. 在tree/index.vue中:

    <template>
      <div>
          <ul>
              <item :model='data'></item>
          </ul>
      </div>
    </template>
    
    <script>
    import Item from './item'
    export default {
        components:{
            Item
        },
        data(){
            return{
                data:{
                    title:"一级1",
                    children:[
                        {
                            title:"二级1-1",
                            children:[
                                {
                                    title:"三级1-1-1",
                                    children:[
                                        {
                                            title:"四级1-1-1-1",
                                            children:[
                                                {
                                                    title:"五级1-1-1-1-1"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        },{
                            title:'二级1-2',
                            children:[
                                {
                                    title:"三级1-2-1"
                                }
                            ]
                        }
                    ]
                }
            }
        }
    }
    </script>
    

    2. item.vue组件:

    <template>
      <li>
          <div @click="toggle">
              {{model.title}}
              <span v-if="isFolder">[{{open?'-':'+'}}]</span>
          </div>
          <ul v-show="open" v-if="isFolder">
              <item v-for="(child,index) in model.children" :model='child' :key='index'></item>
          </ul>
      </li>
    </template>
    
    <script>
    export default {
        name:'Item',
        props:{
            model:{
                type:Object,
                required:true
            }
        },
        data(){
            return{
                open:false
            }
        },
        computed:{
            isFolder(){
               return this.model.children && this.model.children.length>0
            }
        },
        methods:{
            toggle(){
                if(this.isFolder) this.open =!this.open
            }
        }
    }
    </script>
    

    3. 在任意组件中使用:

    <template>
      <div class="index">
          <Tree></Tree>
      </div>
    </template>
    
    <script>
        import Tree from "@/components/tree"
        components:{
            Tree
        }
    </script>

     

     

     

    展开全文
  • 主要介绍了不依任何赖第三方,单纯用vue实现Tree 树形控件的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • vue写的树,包括删除节点,全选节点等操作,拿来就可以用的好东西。
  • vue实现tree-table

    千次阅读 2019-05-31 14:12:39
    elment有实现好的tree-table组件可以用,但是自己想自己写写看,写的有点挫,但是功能都实现了,特此记录下,如有不对的地方,欢迎指正. 效果图如下: 可以无限级展开 思路:将后端的数据处理生成树形结构,再通过深度优先...

    elment有实现好的tree-table组件可以用,但是自己想自己写写看,写的有点挫,但是功能都实现了,特此记录下,如有不对的地方,欢迎指正.

    效果图如下:

    可以无限级展开

    思路:将后端的数据处理生成树形结构,再通过深度优先算法,生成categoryList;这样就可以用table,直接v-for显示所有的类别,且对每个分类进行操作就会很方便,就跟table没有什么区别.

    实现代码:

    <template>
        <div class="table-tree">
            <table class="table table-border">
                <thead>
                    <tr>
                        <th>名称</th>
                        <th>编号</th>
                        <th>是否末级分类</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="item in categoryList" 
                        :key="item.id"
                        v-show="item.parentId ==0 || item.isShow"
                        @click="toggle(item)">
                        <td :style="{'padding-left':item.deepLength*20+'px'}">
                            <i :class="[item.isColspan?'el-icon-caret-bottom':'el-icon-caret-right']" v-show="!item.isLeaf"></i>{{item.name}}</td>
                        <td style="text-align:center;">{{item.cateNo}}</td>
                        <td style="text-align:center;">{{item.isLeaf | status}}</td>
                        <td>
                            <a @click.stop="editCate(item)">编辑</a>
                            <a @click.stop="editCate(item)">添加分类</a>
                            <a @click.stop="editCate(item)">删除</a>
                            <a @click.stop="editCate(item)">显示</a>
                            <a @click.stop="editCate(item)">上传图片</a>
                            <a @click.stop="editCate(item)">查看图片</a>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </template>
    
    <script>
    
    export default {
        name:'category-conf',
        data(){
            return{
                categoryList: [],
            }
        },
        methods: {
            toggle: function(item){
    
                let list = this.categoryList;
    
                function getAllChild (node){
                    if(!node.isLeafe){
                        list.forEach((i) => {
                            if(i.parentId == node.id) {
                                i.isShow = false ;
                                i.isColspan = false ;
                                getAllChild(i);
                            }
                        })
                    }        
                }
    
                //若是展开,则展开该级的下一级
                if(!item.isColspan){
                   
                    this.categoryList.forEach((i)=>{
                        if(i.parentId == item.id){
                            i.isShow = true;
                        }
                    })
                    item.isColspan = !item.isColspan
                } else {//若是收起,则收起该级下的所有
            
                    getAllChild(item);
                    this.categoryList = list;
                    item.isColspan = !item.isColspan;
                }
                
            },
        },
        created:function(){
            this.getAllCategory().then((res)=>{ //this.getAllCategory是global.js中的公共方法
                let listOri = res.data.categoryLists;
                let list = listOri.reduce(function(prev, item){
                    item.isShow = false;
                    item.isColspan = false;
                    prev[item.parentId]?prev[item.parentId].push(item):prev[item.parentId] = [item];
                    return prev
                },{});
                
                for (let parentItem in list) {
                    list[parentItem].forEach(function (item) {
                        item.children = list[item.id] ? list[item.id] : [];
                        });
                }
    
                this.cateListTree = list[0];
               
                //深度优先算法
                let cateList=[];
                function deepTraversal(node,deepLength){
                    if(node!=null){
                        if(node.parentId == 0){
                            deepLength = 0;
                        }
                        node.deepLength = deepLength; //计算每个节点的深度
                        cateList.push(node);
                        let childrens=node.children;
                        deepLength ++;
                        for(let i=0;i<childrens.length;i++){
                            deepTraversal(childrens[i], deepLength);
                        }
                        
                    }
                }
                this.cateListTree.forEach(function(item){
                    deepTraversal(item)
                })
                this.categoryList = cateList;
            });
        },
    
    }
    </script>
    
    <style scoped>
    
    </style>
    

     

    展开全文
  • 小颖在上一篇随笔写了两级的tree,下面给大家再分享一下用<ul><li>标签实现省市多级联动。 调用示例: <treeview model='treedata'> [removed] import treeview from './TreeView.vue' export default { ...
  • 主要介绍了vue elementui tree 任意级别拖拽功能代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 根据level值将数据变成tree结构 //result是盛放数据的容器 //判断result数据是否包括有该level数据, if(result.hasOwnProperty(item.level)) { //如果有,就将数据方法入该level下的children数组 item....
    根据level值将数据变成tree结构
    //result是盛放数据的容器
    
     //判断result数据中是否包括有该level数据,
    if(result.hasOwnProperty(item.level)) {
      //如果有,就将数据方法入该level下的children数组中
      item.children.push({
        id:item.level,
        label:item.label
      })
    }else {
      //如果没有,就新建该level和该level的name值
      info = {
        id: item.level,
        label: item.levelName
      };
      result[item.level] = {
        ...info,
        //将数据放入到该children数组中
       children:[
         {
            id:item.level,
            label: item.label
      	}
       ] 
      }    
    }
    
    展开全文
  • vue实现树形tree组件

    2020-03-09 23:42:38
    一.实现效果 ​​​​ 二.json数据格式展示 三.实现效果 ...2.最后一层时无箭头且输出json数据对应value值 四.实现思路 ...四.代码实现 父组件app.vue <template> <div class =...
  • antd + vue3实现tree树增加按钮

    千次阅读 2021-05-12 13:59:31
    部门名称</a-col> 部门负责人</a-col> 操作</a-col> </a-row> </div> <a-tree ref="tree1" :load-data="onLoadData" :tree-data="treeData" :defaultExpandAll="false" :selectedKeys="selectedKeys" blockNode > ...
  • 思考了两天时间,准备仿照ant-design-vue实现一个基于vue的树形控件。主要用到了vue递归组件思想、input的CheckBox类型输入框的使用。 需求 能够将传入的Json数据生成树形目录。 能够初始化选中节点。 能够手动选中...
  • 今天在看不同的后台管理系统框架,看到了iview-vue,在这个框架看到了组织架构图,进而知道了vue-org-tree这个插件,可以实现vue版本的组织架构图。 简易版效果图如下: 我从网上找到一个大神关于vue-org-tree这...
  • vue tree组件的实现

    千次阅读 2019-07-08 15:48:28
    递归组件 一个简单的递归组件如下例所示:...tree.vue <template> <ul class="child-node"> <li class="vue-tree-item" v-for="(item, index) in treeDataList" :key="index"> <div class="...
  • Vue技术栈开发实战-Tree组件实现文件目录-基础实现(41分33秒).mp4 ,在百度网盘上,有兴趣可以自行下载,大小为190.7M,时长41分钟
  • 基于Vue实现拖拽功能

    2020-12-29 11:43:44
    本文实例为大家分享了Vue实现拖拽功能的具体代码,供大家参考,具体内容如下 效果图: HTML代码: 位置 x:{{val.x}} y:{{val.y}} //注意这里要通过指令绑定函数将当前元素的位置数据传出来 JS...
  • " vue-file-tree " : " github:robogeek/vue-file-tree " ... } 然后运行npm install 快速开始 在您的应用程序,添加以下内容: import FileBrowserTree from 'vue-file-tree' ; 这就是Vue组件。 您可以将其...
  • 由于业务开发过程有树形数据,因此需要用到tree树形组件,鉴于项目是基于vue的,前期的ui组件库选型就是element-ui,于是采用了el-tree组件。但在实际使用过程出现了几个意外。 卡顿假死 server端返回全部数据...
  • router/router.js { path: 'folder_tree', name: 'folderTree', component: () => import('@/views/folder-tree/folder-tree.vue') } 定义api请求 api/data.js export const getFolderList =...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,006
精华内容 6,002
关键字:

vue中实现tree

vue 订阅