• vue递归组件:树形控件


    1、递归组件-简单树形控件预览及问题

    在编写树形组件时遇到的问题:

    • 组件如何才能递归调用?
    • 递归组件点击事件如何传递?

    2、树形控件基本结构及样式

    <template>
      <ul class="vue-tree">
        <li class="tree-item">
          <div class="tree-content"><!--节点内容-->
            <div class="expand-arrow"></div><!--展开或收缩节点按钮-->
            <div class="tree-label">小学</div><!--节点文本内容-->
          </div>
          <ul class="sub-tree"><!--子节点-->
            <li class="tree-item expand">
              <div class="tree-content">
                <div class="expand-arrow"></div>
                <div class="tree-label">语文</div>
              </div>
            </li>
            <li class="tree-item">
              <div class="tree-content">
                <div class="expand-arrow"></div>
                <div class="tree-label">数学</div>
              </div>
            </li>
          </ul>
        </li>
      </ul>
    </template>
    
    <style lang="stylus">
    .vue-tree{
      list-style: none;
      padding: 0;
      margin: 0;
      .tree-item{
        cursor: pointer;
        transition: background-color .2s;
        .tree-content{
          position: relative;
          padding-left: 28px;
          &:hover{
            background-color: #f0f7ff;
          }
        }
        .expand-arrow{
          position: absolute;
          top: 0;
          left: 0;
           28px;
          height: 28px;
          cursor: pointer;
          &::after{
            position: absolute;
            top: 50%;
            left: 50%;
            display: block;
            content: ' ';
            border- 5px;
            border-style: solid;
            border-color: transparent;
            border-left-color: #ccc;
            margin: -5px 0 0 -2.5px;
            transition: all .2s;
          }
        }
        &.expand{
          &>.tree-content{
            background-color: #f0f7ff;
            &>.expand-arrow{
              &::after{
                transform: rotate(90deg);
                margin: -2.5px 0 0 -5px;
              }
            }
          }
        }
        .tree-label{
          height: 28px;
          line-height: 28px;
          font-size: 14px;
        }
        .sub-tree{
          display: none;
          list-style: none;
          padding: 0 0 0 28px;
          margin: 0;
        }
        &.expand>.sub-tree{
          display: block;
        }
        &.no-child{
          &>.tree-content{
            &>.expand-arrow{
              display: none;
            }
          }
        }
      }
    }
    </style>

    3、组件目录及数据结构

    目录结构
    vue-tree

    • VueTree.vue 树形控件父组件
    • TreeItem.vue 树形控件递归组件

    树形控件数据结构

    let treeData = [
      {
        text: "一级", // 显示的文字
        expand: false, // 默认是否展开
        children: [ // 子节点
          {
            text: "一级-1",
            expand: false,
          },
          {
            text: "一级-2",
            expand: false,
            children: [
              {
                text: "一级-2-1",
                expand: false,
              },
              {
                text: "一级-2-2",
                expand: false,
              }
            ]
          }
        ]
      }
    ];

    3.1、TreeItem.vue 代码

    <template>
      <li class="tree-item" :class="{expand: isExpand, 'no-child': !treeItemData.children || treeItemData.children.length  === 0}">
        <div class="tree-content" @click="_clickEvent">
          <div class="expand-arrow" @click.stop="expandTree()"></div>
          <div class="tree-label">{{treeItemData.text}}</div>
        </div>
        <ul class="sub-tree" v-if="treeItemData.children && treeItemData.children.length > 0">
          <!--TreeItem组件中调用TreeItem组件-->
          <TreeItem
            v-for="item in treeItemData.children"
            :tree-item-data="item"
            :key="uuid()"
            :tree-click-event="treeClickEvent"></TreeItem>
        </ul>
      </li>
    </template>
    
    <script>
      export default {
        name: "TreeItem",
        props: {
          treeItemData: {
            type: Object,
            default(){
              return {};
            }
          },
          // 节点点击事件
          treeClickEvent: {
            type: Function,
            default() {
              return function () {};
            }
          }
        },
        data(){
          return {
            // 节点是否展开
            isExpand: this.treeItemData.expand || false
          }
        },
        methods: {
          // 展开/收缩
          expandTree(flag){
            if(!this.treeItemData.children || this.treeItemData.children.length === 0){
              return;
            }
            if(typeof flag === 'undefined'){
              flag = !this.isExpand;
            }else{
    
              flag = !!flag;
            }
            this.isExpand = flag;
          },
          // 创建一个唯一id
          uuid(){
            let str = Math.random().toString(32);
            str = str.substr(2);
            return str;
          },
          // 节点点击事件
          _clickEvent(){
            // 如果有传递事件函数,则调用事件函数并传递当前节点数据及组件
            if(this.treeClickEvent && typeof this.treeClickEvent === 'function'){
              this.treeClickEvent(this.treeItemData, this);
            }
          }
        }
      }
    </script>

    3.1.1、解决 组件如何才能递归调用? 问题

    在组件模板内调用自身必须明确定义组件的name属性,并且递归调用时组件名称就是name属性。如在TreeItem.vue组件中组件的name名称为'TreeItem',那么在template中调用时组件名称就必须是<TreeItem>。
    当然也可以全局注册组件,具体可以查看vue官方文档 递归组件

    3.1.2、解决 递归组件点击事件如何传递? 问题

    我这里的解决方案是使用props将事件函数传递进来,在点击节点的时候调用事件函数,并把相应的数据传递进去。
    之前也尝试过使用$emit的形式并把数据传递过去,由于是递归组件,这样一直$emit,到最外层时传递的数据就变了,比如传递是第3层节点的数据,到最后执行时数据就变成第1层节点的数据了

    4、VueTree.vue 组件

    <template>
      <ul class="vue-tree">
        <TreeItem
            v-for="(item, index) in treeData"
            :key="index"
            :treeItemData="item"
            :tree-click-event="treeClickEvent"></TreeItem>
      </ul>
    </template>
    
    <script>
      import TreeItem from "./TreeItem";
      export default {
        name: "VueTreeMenu",
        components: {
          TreeItem
        },
        props: {
         // 树形控件数据
          treeData: {
            type: Array,
            default(){
              return [];
            }
          },
          // 节点点击事件
          treeClickEvent: {
            type: Function,
            default() {
              return function () {};
            }
          }
        }
      }
    </script>
    
    <style lang="stylus">
    .vue-tree{
      list-style: none;
      padding: 0;
      margin: 0;
      .tree-item{
        cursor: pointer;
        transition: background-color .2s;
        .tree-content{
          position: relative;
          padding-left: 28px;
          &:hover{
            background-color: #f0f7ff;
          }
        }
        .expand-arrow{
          position: absolute;
          top: 0;
          left: 0;
           28px;
          height: 28px;
          cursor: pointer;
          &::after{
            position: absolute;
            top: 50%;
            left: 50%;
            display: block;
            content: ' ';
            border- 5px;
            border-style: solid;
            border-color: transparent;
            border-left-color: #ccc;
            margin: -5px 0 0 -2.5px;
            transition: all .2s;
          }
        }
        &.expand{
          &>.tree-content{
            background-color: #f0f7ff;
            &>.expand-arrow{
              &::after{
                transform: rotate(90deg);
                margin: -2.5px 0 0 -5px;
              }
            }
          }
        }
        .tree-label{
          height: 28px;
          line-height: 28px;
          font-size: 14px;
        }
        .sub-tree{
          display: none;
          list-style: none;
          padding: 0 0 0 28px;
          margin: 0;
        }
        &.expand>.sub-tree{
          display: block;
        }
        &.no-child{
          &>.tree-content{
            /*padding-left: 0;*/
            &>.expand-arrow{
              display: none;
            }
          }
        }
      }
    }
    </style>

    广州品牌设计公司https://www.houdianzi.com PPT模板下载大全https://redbox.wode007.com

    5、使用树形组件

     
    <template>
      <div class="app" id="app">
        <VueTree :tree-data="treeData2" :tree-click-event="treeClickEvent"></VueTree>
      </div>
    </template>
    
    <script>
    import VueTree from "./components/vue-tree/VueTree";
    
    export default {
      name: 'app',
      data(){
        return {
          treeData2: [
            {
              text: "一级", // 显示的文字
              expand: false, // 默认是否展开
              children: [
                {
                  text: "二级-1",
                  expand: false,
                },
                {
                  text: "二级-2",
                  expand: false,
                  children: [
                    {
                      text: "三级-1",
                      expand: false,
                    },
                    {
                      text: "三级-2",
                      expand: false,
                      children: [
                        {
                          text: "四级-1",
                          expand: false,
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              text: "一级-2",
              expand: false
            }
          ]
        }
      },
      methods: {
        treeClickEvent(item, treeItem){
          console.log(item);
        }
      },
      components: {
        VueTree
      }
    }
    </script>
  • 相关阅读:
    ISS6 WEB服务器不能访问 grf 报表模板文件的问题
    c# 读取记事本txt文档到DataTable中
    C# 泛型LIST转DataTable
    sql 查出一张表中重复的所有记录数据
    Coolite中GridPanel真实分页(储存过程方式)
    SQL对Xml字段的操作
    反射和特性 自定义转换datatable为强类型集合
    LINQ TO SQL 并发控制
    AS3 加载文件
    使ASP.NET网站Forms验证可以指定多个登录页面
  • 原文地址:https://www.cnblogs.com/qianxiaox/p/14119913.html
Copyright © 2020-2023  润新知