精华内容
下载资源
问答
  • I would like to commemorate last year and today, my life is full of ...单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。页面整体布局...

    89d990203c8b7563c66cfce35254b2ba.png

    I would like to commemorate last year and today, my life is full of regret.

    一般数据类展示内容,大多采用树状结构展示内容。类似效果如下:

    左侧是导航分类,可以进行新建,对单项导航分享和删除。单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。

    页面整体布局页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表分为上下两块,顶部是添加按钮,下面是导航列表。

    a23e9f36396accc13ca653dec11baf92.png

    less样式。
    @import "../../theme/variables.less";.main {    position: relative;    height: 100%;    overflow: hidden;    .content {        border: 1px solid #dcdcdc;        position: relative;        height: 100%;        background: #f1f2f7;        display: flex;        border-radius: @borderRadius;        .left {            width: 240px;            background: #fff;            border-right: 1px solid rgba(220, 220, 220, 1);            padding: 15px 10px;            display: flex;            flex-direction: column;            overflow: auto;            .header {                width: 100%;                margin-bottom: 20px;                display: flex;                justify-content: center;                align-items: center;                .btn {                    width: 136px;                    margin: 0 6px;                    :global {                        .icon {                            margin-right: 14px;                        }                        .customIcon {                            display: inline-block;                            transform: rotate(45deg);                        }                    }                }            }            .treeLayout {                flex: 1;                .item {                    width: 100%;                    height: 32px;                    line-height: 32px;                    margin-bottom: 11px;                    position: relative;                    .link {                        display: flex;                        align-items: center;                        font-size: 14px;                        font-family: Microsoft YaHei;                        font-weight: 400;                        color: rgba(51, 51, 51, 1);                        padding-left: 21px;                        cursor: pointer;                        .catalogIcon {                            font-size: 12px;                        }                        .text {                            display: inline-block;                            flex: 1;                            margin-left: 12px;                        }                        .opBtn {                            width: 46px;                            display: flex;                            align-items: center;                            justify-content: space-between;                        }                        .operateIcon {                            display: none;                        }                        &:hover {                            color: #00a4ff;                            .opBtn {                                color: rgba(51, 51, 51, 1);                            }                            .operateIcon {                                display: block;                            }                        }                    }                    .iconBtns {                        position: absolute;                        top: 28px;                        right: 24px;                        width: 112px;                        background: rgba(255, 255, 255, 1);                        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.13);                        border-radius: 4px;                        z-index: 10;                        .icon {                            width: 100%;                            height: 40px;                            border-radius: 2px;                            display: flex;                            align-items: center;                            justify-content: center;                            font-size: 12px;                            font-family: Microsoft YaHei;                            font-weight: 400;                            color: rgba(51, 51, 51, 1);                            cursor: pointer;                            .iconName {                                margin-left: 18px;                            }                            &:hover {                                background: #e7e8e9;                            }                        }                    }                }                .itemActive {                    .link {                        color: #00a4ff;                        .opBtn {                            color: rgba(51, 51, 51, 1);                        }                        .operateIcon {                            display: block;                        }                    }                }            }        }        .right {            flex: 1;            width: 100%;            overflow: hidden;        }    }}

    这里的导航列表,新增导航,和删除都是调用相关接口。

    目录树组件

    页面右侧就是树状结构列表,通过路由跳转携带catalogId参数,接口查询出列表数据,后端返回的数据就是有层级的树状结构。

    我认为的写一个组件,单指这里的目录树组件,组件中只需要构造出页面布局,任何的交互逻辑都不涉及,只将相关事件抛出即可。这就需要先明确好数据结构,来写样式布局了。

    数据结构,有id,name,父级id,子节点数组,类型catalogType:1是目录,2是场景,3是外链场景 ... 如下:

    82b49f94b610cb4dae36f9c3fad8b692.png

    树状结构会涉及到递归,这里为了处理方便,组件中分为两层。组件目录结构如下:

    bf32d31797fcf720c867de5e5471bfb0.png

    index就是对外暴露的窗口,主要目录树的布局样式是在DomNode中。先明确一下布局,目录树中单个一行,需要一个展开收起的图标,当前行类型的图标,这里业务上分三种类型,就需要以此判断显示不同图标。每项最后还会有四个操作按钮。

    c0647e21b5d641dc51347267a66408e7.png

    这里把事件简化了,只分了两个事件,一个是展开收起,一个是一系列编辑操作,传个type参数作为区分。

    tabNode(node: ITree) {    this.$emit("tabNode", node);},// 操作doNode(node: ITree, type: string, index: number) {    this.$emit("doNode", node, type, index);},

    index文件中引用DomNode,相关的接收的参数和抛出去的事件,和DomNode一致。

    // index布局<div class="treeLayout">    <DomNode        v-for="(item, index) in trees"        :key="index"        :node="item"        @tabNode="tabNode"        @doNode="doNode"        :index="index"    >
    // 接收的参数props: { trees: { type: Array as () => ITree[], default: [], }, activeId: { type: String, default: "", },},页面右侧实现引用catalogTree组件。
      :trees="treeList"  @tabNode="tabNode"  @doNode="doNode"></catalog-tree>
    前文已经提过,目录数据是后端返回的,那么treeList就是后端返回值res.data。但操作tabNode和doNode这两个方法,需要将treeList数组转换成map对象。因为需要自定义添加一些字段,这些字段只作为前端交互操作逻辑使用,所以后端返回值中不会携带。需要给每一项数据添加isOpen字段,用来判断展开收起状态。level字段,用来实现上移下移操作。先来构造这个catalogMap,定义个方法setCatalogMap,需要的参数有存放结果的treeMap,原数据treeList数组。setCatalogMap,很简单的一个递归。

    44f869ea9476bc8541e86aeab5490a0e.png

    拿到map对象,就可以实现tabNode和doNode这两个方法。
    // 切换状态tabNode(node: ITree) {    if (node.isOpen) {        this.treeMap[node.catalogId].isOpen = false;    } else {        this.treeMap[node.catalogId].isOpen = true;    }},// 编辑等一系列操作,按照类型区分doNode(node: ITree, type: string, index: number) {    switch (type) {        case "up":            // 上移            this.doUp(node, index);            break;        case "down":            // 下移            this.doDown(node, index);            break;        case "edit":            // 编辑            this.doEdit(node.catalogId);            break;        case "delete":            // 删除            this.doDelete(node);            break;    }},
    有认真看的话,会发现,并没有在哪里定义isOpen属性,怎么就在tabNode方法中使用了。因为我还没有写。拿到map对象,循环做个判断,用来保持isOpen状态。
    Object.keys(treeMap).forEach((key) => {    const item = treeMap[key];    if (this.treeMap[key]) {        item.isOpen = this.treeMap[key].isOpen;    } else {        item.isOpen = true;    }});
    doNode中的四个方法,编辑和删除就是调个接口,主要是上移下移操作,前端实现数据的排序,最后将最新的数据返回给后端保存,doSaveSort方法调接口保存。上代码,好好琢磨琢磨。
    doUp(node: ICatalogModel, index: number) {    if (index === 0) {        return;    }    const parentId: string = node.catalogParent as string;    const parentItem: ICatalogModel = this.treeMap[parentId];    let dataList: ICatalogModel[] = [];    // 如果为空则是顶级    if (parentItem) {        if (parentItem.catalogTreeVoList) {            dataList = parentItem.catalogTreeVoList;        }    } else {        dataList = this.treeList;    }    const item = dataList[index];    dataList.splice(index, 1);    dataList.splice(index - 1, 0, item);    this.doSaveSort(dataList);},doDown(node: ICatalogModel, index: number) {    const parentId: string = node.catalogParent as string;    const parentItem: ICatalogModel = this.treeMap[parentId];    // 如果为空则是顶级    let dataList: ICatalogModel[] = [];    if (parentItem) {        if (parentItem.catalogTreeVoList) {            // 最后一个不能下移            if (parentItem.catalogTreeVoList.length === (index + 1)) {                return;            } else {                dataList = parentItem.catalogTreeVoList;            }        }    } else {        // 一级最后一个不能下移        if ( this.treeList.length === (index + 1)) {            return;        }        dataList = this.treeList;    }    const item = dataList[index];    dataList.splice(index, 1);    dataList.splice(index + 1, 0, item);    this.doSaveSort(dataList);},
    总结树状结构列表,首先需要明确数据结构,必备的字段id,name,父级id,children数组,根据数据结构,使用递归构建布局。
    over~加班快乐
    展开全文
  • 单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。 页面整体布局 页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表...

    一般数据类展示内容,大多采用树状结构展示内容。类似效果如下:

    关注微信公众号,查看效果

    在这里插入图片描述

    左侧是导航分类,可以进行新建,对单项导航分享和删除。单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。

    页面整体布局

    页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表分为上下两块,顶部是添加按钮,下面是导航列表。

    less样式。

    import "../../theme/variables.less";
    .main {    position: relative;    height: 100%;    overflow: hidden;    .content {        border: 1px solid #dcdcdc;        position: relative;        height: 100%;        background: #f1f2f7;        display: flex;        border-radius: @borderRadius;        .left {            width: 240px;            background: #fff;            border-right: 1px solid rgba(220, 220, 220, 1);            padding: 15px 10px;            display: flex;            flex-direction: column;            overflow: auto;            .header {                width: 100%;                margin-bottom: 20px;                display: flex;                justify-content: center;                align-items: center;                .btn {                    width: 136px;                    margin: 0 6px;                    :global {                        .icon {                            margin-right: 14px;                        }                        .customIcon {                            display: inline-block;                            transform: rotate(45deg);                        }                    }                }            }            .treeLayout {                flex: 1;                .item {                    width: 100%;                    height: 32px;                    line-height: 32px;                    margin-bottom: 11px;                    position: relative;                    .link {                        display: flex;                        align-items: center;                        font-size: 14px;                        font-family: Microsoft YaHei;                        font-weight: 400;                        color: rgba(51, 51, 51, 1);                        padding-left: 21px;                        cursor: pointer;                        .catalogIcon {                            font-size: 12px;                        }                        .text {                            display: inline-block;                            flex: 1;                            margin-left: 12px;                        }                        .opBtn {                            width: 46px;                            display: flex;                            align-items: center;                            justify-content: space-between;                        }                        .operateIcon {                            display: none;                        }                        &:hover {                            color: #00a4ff;                            .opBtn {                                color: rgba(51, 51, 51, 1);                            }                            .operateIcon {                                display: block;                            }                        }                    }                    .iconBtns {                        position: absolute;                        top: 28px;                        right: 24px;                        width: 112px;                        background: rgba(255, 255, 255, 1);                        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.13);                        border-radius: 4px;                        z-index: 10;                        .icon {                            width: 100%;                            height: 40px;                            border-radius: 2px;                            display: flex;                            align-items: center;                            justify-content: center;                            font-size: 12px;                            font-family: Microsoft YaHei;                            font-weight: 400;                            color: rgba(51, 51, 51, 1);                            cursor: pointer;                            .iconName {                                margin-left: 18px;                            }                            &:hover {                                background: #e7e8e9;                            }                        }                    }                }                .itemActive {                    .link {                        color: #00a4ff;                        .opBtn {                            color: rgba(51, 51, 51, 1);                        }                        .operateIcon {                            display: block;                        }                    }                }            }        }        .right {            flex: 1;            width: 100%;            overflow: hidden;        }    }}

    这里的导航列表,新增导航,和删除都是调用相关接口。

    目录树组件

    页面右侧就是树状结构列表,通过路由跳转携带catalogId参数,接口查询出列表数据,后端返回的数据就是有层级的树状结构。

    我认为的写一个组件,单指这里的目录树组件,组件中只需要构造出页面布局,任何的交互逻辑都不涉及,只将相关事件抛出即可。这就需要先明确好数据结构,来写样式布局了。

    数据结构,有id,name,父级id,子节点数组,类型catalogType:1是目录,2是场景,3是外链场景 ... 如下:

    树状结构会涉及到递归,这里为了处理方便,组件中分为两层。组件目录结构如下:

    index就是对外暴露的窗口,主要目录树的布局样式是在DomNode中。先明确一下布局,目录树中单个一行,需要一个展开收起的图标,当前行类型的图标,这里业务上分三种类型,就需要以此判断显示不同图标。每项最后还会有四个操作按钮。

    这里把事件简化了,只分了两个事件,一个是展开收起,一个是一系列编辑操作,传个type参数作为区分。

    tabNode(node: ITree) {    this.$emit("tabNode", node);},
    // 操作doNode(node: ITree, type: string, index: number) {    this.$emit("doNode", node, type, index);},

    index文件中引用DomNode,相关的接收的参数和抛出去的事件,和DomNode一致。

    // index布局<div class="treeLayout">    <DomNode        v-for="(item, index) in trees"        :key="index"        :node="item"        @tabNode="tabNode"        @doNode="doNode"        :index="index"    ></DomNode></div>
    // 接收的参数props: {    trees: {        type: Array as () => ITree[],        default: [],    },    activeId: {        type: String,        default: "",    },},

    页面右侧实现

    引用catalogTree组件。

    <catalog-tree  :trees="treeList"  @tabNode="tabNode"  @doNode="doNode"></catalog-tree>

    前文已经提过,目录数据是后端返回的,那么treeList就是后端返回值res.data。但操作tabNode和doNode这两个方法,需要将treeList数组转换成map对象。

    因为需要自定义添加一些字段,这些字段只作为前端交互操作逻辑使用,所以后端返回值中不会携带。

    需要给每一项数据添加isOpen字段,用来判断展开收起状态。level字段,用来实现上移下移操作。

    先来构造这个catalogMap,定义个方法setCatalogMap,需要的参数有存放结果的treeMap,原数据treeList数组。

    setCatalogMap,很简单的一个递归。

    拿到map对象,就可以实现tabNode和doNode这两个方法。

    // 切换状态tabNode(node: ITree) {    if (node.isOpen) {        this.treeMap[node.catalogId].isOpen = false;    } else {        this.treeMap[node.catalogId].isOpen = true;    }},
    // 编辑等一系列操作,按照类型区分doNode(node: ITree, type: string, index: number) {    switch (type) {        case "up":            // 上移            this.doUp(node, index);            break;        case "down":            // 下移            this.doDown(node, index);            break;        case "edit":            // 编辑            this.doEdit(node.catalogId);            break;        case "delete":            // 删除            this.doDelete(node);            break;    }},

    有认真看的话,会发现,并没有在哪里定义isOpen属性,怎么就在tabNode方法中使用了。

    因为我还没有写。

    拿到map对象,循环做个判断,用来保持isOpen状态。

    Object.keys(treeMap).forEach((key) => {    const item = treeMap[key];    if (this.treeMap[key]) {        item.isOpen = this.treeMap[key].isOpen;    } else {        item.isOpen = true;    }});

    doNode中的四个方法,编辑和删除就是调个接口,主要是上移下移操作,前端实现数据的排序,最后将最新的数据返回给后端保存,doSaveSort方法调接口保存。

    上代码,好好琢磨琢磨。

    doUp(node: ICatalogModel, index: number) {    if (index === 0) {        return;    }    const parentId: string = node.catalogParent as string;    const parentItem: ICatalogModel = this.treeMap[parentId];
        let dataList: ICatalogModel[] = [];    // 如果为空则是顶级    if (parentItem) {        if (parentItem.catalogTreeVoList) {            dataList = parentItem.catalogTreeVoList;        }    } else {        dataList = this.treeList;    }    const item = dataList[index];    dataList.splice(index, 1);    dataList.splice(index - 1, 0, item);    this.doSaveSort(dataList);},
    doDown(node: ICatalogModel, index: number) {    const parentId: string = node.catalogParent as string;    const parentItem: ICatalogModel = this.treeMap[parentId];    // 如果为空则是顶级    let dataList: ICatalogModel[] = [];    if (parentItem) {        if (parentItem.catalogTreeVoList) {            // 最后一个不能下移            if (parentItem.catalogTreeVoList.length === (index + 1)) {                return;            } else {                dataList = parentItem.catalogTreeVoList;            }        }    } else {        // 一级最后一个不能下移        if ( this.treeList.length === (index + 1)) {            return;        }        dataList = this.treeList;    }    const item = dataList[index];    dataList.splice(index, 1);    dataList.splice(index + 1, 0, item);    this.doSaveSort(dataList);},

    总结

    树状结构列表,首先需要明确数据结构,必备的字段id,name,父级id,children数组,根据数据结构,使用递归构建布局。

    展开全文
  • vue——目录实现

    2020-03-17 10:27:27
    1、使用el-menu子组件递归 父级菜单menu.vue <template> <div class="main-sidebar" class="showSlide expandSide"> <el-menu class="el-menu-style" @select="" ...

    1、使用el-menu子组件递归

    • 父级菜单menu.vue
    <template>
        <div class="main-sidebar" class="showSlide expandSide">
            <el-menu class="el-menu-style"
                     @select=""
                     @open="handleOpen"
                     @close="handleClose">
              <template v-for="item in fileList">
                <sub-menu :param="item" ></sub-menu>
              </template>
            </el-menu>
        </div>
    </template>
    <script>
      import subMenu from "./subMenu.vue"
      import {fileList} from  '../http/api'
    
    
    
      export default {
        data() {
          return {
            fileList:{},
          }
        },
        computed: {
          onRoutes(){
            return this.$route.path;
          },
          onRouteKeys(){
            return getParentArray(this.$route.path, this.fileList);
          }
        },
        created() {
          this.loadFile();
        },
        methods: {
          handleOpen(key, keyPath) {
    
          },
          handleClose(key, keyPath) {
    
          },
          loadFile(){
            let projectId = this.$route.params.id
            fileList(projectId).then(res => {
              this.fileList = res.data
            }).catch(err => {
                alert("当前用例为空")
            })
          }
        }
      }
    </script>
    <style scoped>
    
      .showSlide {
        animation-duration: .2s;
        animation-name: slideInLeft;
      }
    
      .hideSlide {
        animation-duration: .2s;
        animation-name: slideOutLeft;
      }
    
      .main-sidebar {
        background-color: #ffffff;
        position: fixed;
        top: 50px;
        left: 0;
        bottom: 0;
        height: calc(100vh - 50px);
        width: 44px;
        z-index: 810;
        -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out;
        -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out;
        -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out;
        transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
      }
    
      .expandSide {
        width: 280px;
      }
    
      .el-menu-style,
      .el-menu-style .el-menu{
        background-color: #ffffff;
      }
      .el-menu-style .el-menu-item:hover,
      .el-menu-style .el-submenu__title:hover{
        background-color: #eeeeee !important;
      }
    
      .el-menu-style .el-submenu .el-menu-item {
        height: 45px;
        line-height: 45px;
      }
    
      .el-menu-style .el-menu-item,
      .el-menu-style .el-submenu__title {
        height: 45px;
        line-height: 45px;
      }
    
      .main-sidebar .el-menu--collapse {
        width: 44px;
      }
    
      .main-sidebar .el-menu--collapse>.el-menu-item,
      .main-sidebar .el-menu--collapse>.el-submenu>.el-submenu__title {
        padding-left: 13px !important;
      }
    
      .vue-scrollbar{
        background-color: #ffffff !important;
        height: calc(100vh - 50px)
      }
    
      .main-sidebar .el-scrollbar__bar.is-vertical{
        display: none;
      }
    
      .sidebar{
        min-height: 450px;
      }
    
    
    </style>
    
    
    • 子组件subMenu.vue
    <template>
      <div class="contents">
        <div class="menu-list">
          <el-submenu :index="item.href" v-if="item.children && item.children.length>0" class="el-menu-sub" >
            <template slot="title">
                <div v-if="item.isShow == 2" v-contextmenu:contextmenu-file>
                  <i  class="fa  fa-file"></i>
                  <el-link :underline="false" @click="intoItem(item.id)">
                    <span>{{item.name}}</span>
                  </el-link>
                </div>
                <div v-else v-contextmenu:contextmenu-folder>
                  <i class="fa fa-folder"></i>
                  <span>{{item.name}}</span>
                </div>
            </template>
            <template v-for="child in item.children">
              <sub-menu v-if="child.children && child.children.length>0" :param="child"></sub-menu>
              <el-menu-item :index="child.href" v-else >
                  <div v-if="child.isShow == 1" v-contextmenu:contextmenu-folder>
                     <i class="fa fa-folder"></i>
                    <span>{{item.name}}</span>
                  </div>
                  <div v-else v-contextmenu:contextmenu-file>
                    <i  class="fa fa-file"></i>
                    <el-link :underline="false" @click="intoItem(child.id)">
                      <span>{{child.name}}</span>
                    </el-link>
                  </div>
              </el-menu-item>
            </template>
          </el-submenu>
          <el-menu-item :index="item.href" v-else class="el-menu-each">
              <div v-if="item.isShow == 2" v-contextmenu:contextmenu-file>
                <i  class="fa  fa-file"></i>
                <el-link :underline="false" @click="intoItem(item.id)">
                  <span>{{item.name}}</span>
                </el-link>
              </div>
              <div v-else v-contextmenu:contextmenu-folder>
                <i class="fa fa-folder"></i>
                <span>{{item.name}}</span>
              </div>
          </el-menu-item>
        </div>
      </div>
    </template>
    <script>
      import subMenu from "./subMenu.vue"
    
    
    
      export default {
        name: 'subMenu',
        props: ['param'],
        data() {
          return {
            item: this.param
          }
        },
        components: {
          subMenu,
        }
      }
    </script>
    <style scoped>
      .el-menu .fa {
        margin-right: 10px;
      }
      .box {
        width: 100%;
      }
      .el-dialog{
        display: flex;
        flex-direction: column;
        margin:0 !important;
        position:absolute;
        top:50%;
        left:50%;
        transform:translate(-50%,-50%);
        /*height:600px;*/
        max-height:calc(100% - 30px);
        max-width:calc(100% - 30px);
      }
      .el-dialog .el-dialog__body{
        flex:1;
        overflow: auto;
      }
    </style>
    
    

    2、使用开源组件v-tree

    • v-tree.css
    .ztree * {
      moz-user-select: -moz-none;
      -moz-user-select: none;
      -o-user-select:none;
      -khtml-user-select:none;
      -webkit-user-select:none;
      -ms-user-select:none;
      user-select:none;
    
    
      padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif}
    .ztree {margin:0; padding:5px; color:#333}
    .ztree li{padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0}
    .ztree li ul{ margin:0; padding:0 0 0 18px}
    .ztree li ul.line{ background:url(../img/line_conn.png) 0 0 repeat-y;}
    
    .ztree li a {padding-right:3px; margin:0; cursor:pointer; height:21px; color:#333; background-color: transparent; text-decoration:none; vertical-align:top; display: inline-block}
    .ztree li a:hover {text-decoration:underline}
    .ztree li a.curSelectedNode {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; opacity:0.8;}
    .ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; border:1px #666 solid; opacity:0.8;}
    .ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#aaa; color:white; height:21px; border:1px #666 solid;
      opacity:0.8; filter:alpha(opacity=80)}
    .ztree li a.tmpTargetNode_prev {}
    .ztree li a.tmpTargetNode_next {}
    .ztree li a input.rename {height:14px; width:80px; padding:0; margin:0;
      font-size:12px; border:1px #585956 solid; *border:0px}
    .ztree li span {line-height:21px; margin-right:2px}
    .ztree li span.button {line-height:0; margin:0; padding: 0; width:21px; height:21px; display: inline-block; vertical-align:middle;
      border:0 none; cursor: pointer;outline:none;
      background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
      background-image:url("../img/v-tree.png");}
    
    .ztree li span.button.chk {width:13px; height:13px; margin:0 2px; cursor: auto}
    .ztree li span.button.chk.checkbox_false_full {background-position: -5px -5px;}
    .ztree li span.button.chk.checkbox_false_full_focus {background-position: -5px -26px;}
    .ztree li span.button.chk.checkbox_false_part {background-position: -5px -48px;}
    .ztree li span.button.chk.checkbox_false_part_focus {background-position: -5px -68px;}
    .ztree li span.button.chk.checkbox_false_disable {background-position: -5px -89px;}
    .ztree li span.button.chk.checkbox_true_full {background-position: -26px -5px;}
    .ztree li span.button.chk.checkbox_true_full_focus {background-position: -26px -26px;}
    .ztree li span.button.chk.checkbox_true_part {background-position: -26px -48px;}
    .ztree li span.button.chk.checkbox_true_part_focus {background-position: -26px -68px;}
    .ztree li span.button.chk.checkbox_true_disable {background-position: -26px -89px;}
    .ztree li span.button.chk.radio_false_full {background-position: -47px -5px;}
    .ztree li span.button.chk.radio_false_full_focus {background-position: -47px -26px;}
    .ztree li span.button.chk.radio_false_part {background-position: -47px -47px;}
    .ztree li span.button.chk.radio_false_part_focus {background-position: -47px -68px;}
    .ztree li span.button.chk.radio_false_disable {background-position: -47px -89px;}
    .ztree li span.button.chk.radio_true_full {background-position: -68px -5px;}
    .ztree li span.button.chk.radio_true_full_focus {background-position: -68px -26px;}
    .ztree li span.button.chk.radio_true_part {background-position: -68px -47px;}
    .ztree li span.button.chk.radio_true_part_focus {background-position: -68px -68px;}
    .ztree li span.button.chk.radio_true_disable {background-position: -68px -89px;}
    
    .ztree li span.button.switch {width:21px; height:21px}
    .ztree li span.button.root_open{background-position:-105px -63px}
    .ztree li span.button.root_close{background-position:-126px -63px}
    .ztree li span.button.roots_open{background-position: -105px 0;}
    .ztree li span.button.roots_close{background-position: -126px 0;}
    .ztree li span.button.center_open{background-position: -105px -21px;}
    .ztree li span.button.center_close{background-position: -126px -21px;}
    .ztree li span.button.bottom_open{background-position: -105px -42px;}
    .ztree li span.button.bottom_close{background-position: -126px -42px;}
    .ztree li span.button.noline_open{background-position: -105px -84px;}
    .ztree li span.button.noline_close{background-position: -126px -84px;}
    .ztree li span.button.root_docu{ background:none;}
    .ztree li span.button.roots_docu{background-position: -84px 0;}
    .ztree li span.button.center_docu{background-position: -84px -21px;}
    .ztree li span.button.bottom_docu{background-position: -84px -42px;}
    .ztree li span.button.noline_docu{ background:none;}
    
    .ztree li span.button.ico_open{margin-right:2px; background-position: -147px -21px; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.ico_close{margin-right:2px; background-position: -147px 0; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.ico_docu{margin-right:2px; background-position: -147px -42px; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.edit {margin-left:2px; margin-right: -1px; background-position: -189px -21px; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.edit:hover {
      background-position: -168px -21px;
    }
    .ztree li span.button.remove {margin-left:2px; margin-right: -1px; background-position: -189px -42px; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.remove:hover {
      background-position: -168px -42px;
    }
    .ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position: -189px 0; vertical-align:top; *vertical-align:middle}
    .ztree li span.button.add:hover {
      background-position: -168px 0;
    }
    .ztree li span.button.ico_loading{margin-right:2px;  vertical-align:top; *vertical-align:middle}
    
    ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}
    
    span.tmpzTreeMove_arrow {width:16px; height:21px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
      background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
      background-position:-168px -84px; background-image:url("../img/v-tree.png"); }
    
    ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
    .ztreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}
    
    
    • tree.vue
    <template>
      <div>
        <ul class="ztree">
          <vTree v-for="(node,index) in treeData"
                 :key="index"
                 :checkBoxType="checkBoxType"
                 :allOpen="allOpen"
                 :beforeClick="beforeClick"
                 :checkBox="checkBox"
                 :nodeTrigger="nodeTrigger"
                 :index="index"
                 :tree="node"
                 :first="index===0"
                 :last="treeData.length-1===index"
                 :currentArray="treeData"
                 :parentTree="node.parentTree"
                 :rootData="treeData"
                 :checkBoxCallInit="checkBoxCallInit"
                 :checkBoxCall="checkBoxCall"
                 :clickNode="clickNode"
                 :hiddenLine="hiddenLine"
                 :async="async"
                 :asyncCall="asyncCall"
          />
        </ul>
      </div>
    </template>
    
    <script>
      import vTree from './tree-core'
    
      export default {
        components: {vTree},
        name: "tree",
        props: {
          treeNode: {
            type: Array,
            default: function () {
              return [];
            },
            required: true,
          },
          allOpen: {
            type: Boolean,
            default: null,
            required: false,
          },
          nodeTrigger: {
            type: Boolean,
            default: false,
            required: false,
          },
          checkBox: {
            type: Boolean,
            default: false,
            required: false,
          },
          checkBoxType: {
            type: Boolean,
            default: true,
            required: false,
          },
          beforeClick: {
            type: Function,
            default: null
          },
          clickNode: {
            type: Function,
          },
          asyncCall: {
            type: Function,
          },
          hiddenLine: {
            type: Boolean,
            default: false,
            required: false,
          },
          async: {
            type: Boolean,
            default: false,
            required: false,
          },
        },
        data() {
          return {
            treeData: [],
            line: '',
            first: true,
            allOpens: this.allOpen,
            checkedBoxCallArr: [],
          }
        },
        methods: {
          init() {
            let tempList = JSON.parse(JSON.stringify(this.treeNode));
    
            let initTree = (tree, parent) => {
              for (let index = 0; index < tree.length; index++) {
                let m = tree[index];
                if (!m.hasOwnProperty("id")) {
                  m.id = m.hasOwnProperty("id") ? m.id : null;
                }
                if (!m.hasOwnProperty("open")) {
                  m.open = m.hasOwnProperty("open") ? m.open : false;
                }
                if (!m.hasOwnProperty("checked")) {
                  m.checked = m.hasOwnProperty("checked") ? m.checked : false;
                }
                if (!m.hasOwnProperty("checkBox")) {
                  m.checkBox = m.hasOwnProperty("checkBox") ? m.checkBox : false;
                }
                if (!m.hasOwnProperty("nodeTrigger")) {
                  m.nodeTrigger = m.hasOwnProperty("nodeTrigger") ? m.nodeTrigger : false;
                }
                if (!m.hasOwnProperty("checkBoxType")) {
                  m.checkBoxType = this.checkBoxType
                }
                if (!m.hasOwnProperty("last")) {
                  m.last = m.hasOwnProperty("last") ? m.last : false;
                }
                if (!m.hasOwnProperty("first")) {
                  m.first = m.hasOwnProperty("first") ? m.first : false;
                }
                if (!m.hasOwnProperty("active")) {
                  m.active = m.hasOwnProperty("active") ? m.active : false;
                }
                if (!m.hasOwnProperty("async")) {
                  m.async = this.async;
                }
                if (!m.hasOwnProperty("hiddenLine")) {
                  m.hiddenLine = this.hiddenLine;
                }
                if (!m.hasOwnProperty("parentTree")) {
                  m.parentTree = parent ? parent : null;
                }
    
                m.children = m.children || [];
                if (m.children.length > 0)
                  initTree(m.children, m);
              }
            };
            initTree(tempList, null);
    
            this.treeData = tempList;
            this.line = 'line';
            if (this.first)
              this.$emit('call', this.treeData);
          },
          changeStatus() {
            let changeOpen = (data) => {
              data.forEach(d => {
                d.open = this.allOpen;
                if (d.children) changeOpen(d.children);
              });
            };
            changeOpen(this.treeData);
          },
          checkBoxCallInit(arr) {
            arr.forEach(a => {
              this.checkedBoxCallArr.push(a);
            });
          },
          checkBoxCall(arr, isAdd) {
            if (isAdd)
              arr.forEach(a => {
                this.checkedBoxCallArr.push(a);
              });
            else {
              arr.forEach(a => {
                if (this.checkBoxCall.length === 0)
                  return;
                let key = (a.id ? a.id : null) + a.index + a.name;
    
                this.checkedBoxCallArr.forEach((ss, index) => {
                  if (((ss.id ? ss.id : null) + ss.index + ss.name) === key) {
                    this.checkedBoxCallArr.splice(index, 1);
                  }
                });
              });
            }
            this.$emit('checkBoxCall', this.checkedBoxCallArr);
          },
        },
        created() {
          this.init();
        },
        update() {
        },
        mounted() {
          /*复选框回调*/
          this.$emit('checkBoxCall', this.checkedBoxCallArr);
        },
        watch: {
          allOpen() {
            this.changeStatus()
          }
        }
      }
    </script>
    
    <style scoped>
      @import "../../../../static/css/v-tree.css";
    </style>
    
    
    • tree-core.vue
    <template>
      <li>
          <span v-if="vif()" title=""
                @click="vShow(tree)"
                class="button  switch"
                :class="vTreeFirstSpan()"></span>
        <span v-else title="" class="button switch "
              :class="classes()"></span>
    
        <span v-if="checkBox" @click="selectCheckBox(tree)" class="button chk"
              :class="tree.checked?'checkbox_true_full':'checkbox_false_full'"></span>
    
        <a target="_blank"
           style=""
           @click="selectNode"
           :class="aClass()"
           :title="tree.name">
          <span :title="tree.name" class="button" :class="iconCss()" :style="load()"></span>
          <span class="node_name">{{tree.name}}</span>
        </a>
        <ul :class="line">
          <treeCore
            v-for="(tr,i) in tree.children"
            v-show="tree.open"
            :key="i"
            :checkBoxType="checkBoxType"
            :allOpen="tr.allOpen"
            :first="false"
            :checkBox="checkBox"
            :nodeTrigger="nodeTrigger"
            :index="i"
            :tree="tr"
            :currentArray="tree.children"
            :parentTree="tr.parentTree"
            :rootData="rootData"
            :clickNode="clickNode"
            @checkedBoxV="checkedBoxV"
            :beforeClick="beforeClick"
            :checkBoxCallInit="checkBoxCallInit"
            :checkBoxCall="checkBoxCall"
            :last="tree.children.length-1===i"
            :hiddenLine="tr.hiddenLine"
            :async="async"
            :asyncCall="asyncCall"
    
          />
        </ul>
      </li>
    
    </template>
    
    <script>
    
      export default {
        name: "treeCore",
        props: {
          tree: {
            type: Object,
            required: true,
          },
          allOpen: {
            type: Boolean,
            default: null,
            required: false,
          },
          nodeTrigger: {
            type: Boolean,
            default: false,
            required: false,
          },
          checkBox: {
            type: Boolean,
            default: false,
            required: false,
          },
          checkBoxType: {
            type: Boolean,
            default: true,
            required: false,
          },
          beforeClick: {
            type: Function,
            default: null
          },
          last: {
            type: Boolean,
            default: null,
            required: false,
          },
          first: {
            type: Boolean,
            default: true,
            required: false,
          },
          currentArray: {
            type: Array,
            required: true,
          },
          index: {
            type: Number,
            required: true,
          },
          rootData: {
            type: Array,
            required: true,
          },
          parentTree: {
            type: Object,
            required: false,
          },
          checkBoxCallInit: {
            type: Function,
          },
          checkBoxCall: {
            type: Function,
          },
          clickNode: {
            type: Function,
          },
          asyncCall: {
            type: Function,
          },
          hiddenLine: {
            type: Boolean,
            default: false,
            required: false,
          },
          async: {
            type: Boolean,
            default: false,
            required: false,
          },
        },
        data() {
          return {
            line: '',
            currentTree: this.tree,
            asyncLoading: false,
          }
        },
        methods: {
          coreInit() {
            this.line = this.hiddenLine ? '' : this.currentArray.length - 1 === this.index ? '' : 'line';
            if (this.tree.parentTree && this.tree.checked) {
              /*方案一 Scheme 1*/
              let checkedBoxArr = [];
              checkedBoxArr.push(this.tree);
              let selectParent = (data) => {
                if (!data.checked) {
                  data.checked = this.tree.checked;
                  checkedBoxArr.push(data);
                }
                if (data.parentTree) {
                  selectParent(data.parentTree);
                } else {
                  this.checkBoxCallInit(checkedBoxArr);
                }
              };
              selectParent(this.currentTree);
              /*方案二 Scheme 2*/
              //this.$emit('checkedBoxV')
            }
          },
          checkedBoxV() {
            this.tree.checked = true;
            this.$emit('checkedBoxV')
          },
          pNode(tree) {
            return tree.open;
          },
          vShow(tree) {
            /*需要有异步加载  need add async loading*/
            /*默认异步加载给他按钮 当加载后才能判别时候有子节点 */
            if (this.async && !tree.open && tree.children.length === 0) {
              this.asyncCall.call(this, tree, this.asyncBack);
              this.asyncLoading = true;
            }
            tree.open = !tree.open;
          },
          asyncBack(requestDataArr) {
            if (typeof requestDataArr)
              this.addNode(requestDataArr);
            this.asyncLoading = false;
          },
          selectNode() {
            let isClick = this.beforeClick.call(this, this.tree);
            if (isClick) {
              //清除其它
              let pro = null;
              let clearStyle = (data) => {
                for (let i = 0; i < data.length; i++) {
                  let d = data[i];
                  if (d.active) {
                    pro = d;
                    d.active = !d.active;
                  } else if (d.children) {
                    clearStyle(d.children);
                  }
                }
              };
              clearStyle(this.rootData);
              this.tree.active = true;
              if (this.nodeTrigger) {
                this.tree.open = !this.tree.open;
              }
              this.clickNode.call(this, this.tree, pro);
            }
          },
          addNode(arr) {
            try {
              arr.forEach(a => {
                this.tree.children.push({
                  id: a.id ? a.id : null,
                  name: a.name,
                  open: a.open ? a.open : false,
                  checked: a.checked ? a.checked : false,
                  checkBox: a.checkBox ? a.checkBox : false,
                  nodeTrigger: a.nodeTrigger ? a.nodeTrigger : this.nodeTrigger,
                  checkBoxType: this.checkBoxType,
                  last: false,
                  first: false,
                  active: false,
                  async: this.async,
                  hiddenLine: this.hiddenLine,
                  parentTree: this.tree,
                  children: [],
                });
              });
            } catch (e) {
              console.error('The asynchronous callback parameter must be an array,异步回调参数必须是数组');
            }
          },
          selectCheckBox(tree) {
            tree.checked = !tree.checked;
            let checkedBoxArr = [];
            checkedBoxArr.push(tree);
            if (this.checkBoxType) {
              /*级联*/
              let changeChecked = (data) => {
                data.forEach(d => {
                  checkedBoxArr.push(d);
                  d.checked = tree.checked;
                  if (d.children) changeChecked(d.children);
                });
              };
              changeChecked(tree.children);
    
              let checkChildren = (data) => {
                for (let i = 0; i < data.length; i++) {
                  if (data[i].checked)
                    return true;
                }
              };
              let selectParent = (data) => {
                if (data.children.length > 1) {
                  const childExistsThis = [];
                  data.children.forEach((m, index) => {
                    if (index !== this.index)
                      childExistsThis.push(m);
                  });
                  if (childExistsThis && data.checked && checkChildren(childExistsThis))
                    return;
                }
                checkedBoxArr.push(data);
                data.checked = tree.checked;
                if (data.parentTree) selectParent(data.parentTree);
              };
              if (this.parentTree)
                selectParent(this.parentTree);
            }
    
            this.checkBoxCall(checkedBoxArr, tree.checked);
          }
        },
        created() {
          this.coreInit();
        },
        computed: {
          vif() {
            return function () {
              return this.async ? true : this.tree.children.length > 0;
            }
          },
          vTreeFirstSpan() {
            return function () {
              return this.hiddenLine ? this.tree.open ? 'noline_open treenode_switch' : 'noline_close treenode_switch' :
                this.currentArray.length - 1 === this.index ? this.tree.open ? 'bottom_open' : 'bottom_close'
                  :
                  this.tree.open ? 'roots_open' : 'roots_close';
            }
          },
          classes() {
            return function () {
              return this.hiddenLine ?
                'noline_docu'
                :
                this.first ?
                  'roots_docu'
                  :
                  this.currentArray.length - 1 === this.index ?
                    'bottom_docu'
                    :
                    'center_docu'
            }
          },
          iconCss() {
            return function () {
              return this.tree.children.length > 0 ? this.tree.open ? 'ico_open' : 'ico_close' : 'ico_docu';
            }
          },
          load() {
            return function () {
              return this.asyncLoading ?
                'background:url(../../static/img/loading.gif) 0 0 no-repeat' : ''
            }
          },
          aClass() {
            return function () {
              return this.tree.active ? 'curSelectedNode' : '';
            }
          }
        },
        watch: {
          tree: {
            handler(oldV, newV) {
              if (newV.checked)
                this.tree.checked = newV.checked;
            },
            deep: true
          },
        }
      }
    </script>
    
    <style scoped>
      @import "../../../../static/css/v-tree.css";
    </style>
    
    
    • vtree-demo.vue
    <template>
      <div style="height: auto">
        <div class="main">
          <ul>
            <li>
              <Tree
                :treeNode="treeNode"
                :clickNode="clickNode"
                :beforeClick="beforeClick"
                :hiddenLine="hiddenLine"
                :async="async"
                @call='callAsync'
                :asyncCall="asyncCall"
    
                :nodeTrigger="nodeTrigger"
              />
            </li>
          </ul>
        </div>
    
      </div>
    
    </template>
    
    <script>
      import Tree from "./tree";
    
      export default {
        components: {Tree},
        data() {
          return {
            treeNode: [],
            nodeTrigger: false,
            // 所有节点
            trees: [],
            hiddenLine: false,
            async: true,
          }
        },
        methods: {
          beforeClick(node) {
            //可操作内部
            return true;
          },
          /*回调初始化后的数据 以后修改tree实现对节点的操作即可*/
          call(data) {
            console.log('data', data);
            // this.trees = data;
          },
          callAsync(data) {
            //this.trees = data;
          },
          /*点击节点信息 上个点击节点信息*/
          clickNode(data, oldData) {
            /*if(data.children){
              data.children.push({
                id: (new Date()).getTime(),
                name:'新增节点',
                children:[]
              });
            }else{
            }*/
            // console.log(data);
            //data.open=true;
          },
          /*异步回调函数 data 当前节点数据 call 回调函数*/
          asyncCall(data, call) {
            setTimeout(function () {
              let hm = '' + (new Date()).getTime();
              let addNode = [
                {id: hm, name: "children node" + hm.substr(hm.length - 4, hm.length)},
              ];
              console.log(data.id)
              // call(addNode);
              call([])
            }, 800);
          }
        },
        mounted() {
          // this.allOpen = true;
        },
        created() {
          this.treeNode = [
            {
              id: "1",
              name: "父节点22 - 折叠",
              open: true,
              // 文件夹open = true/false
              // 文件:不显示图像
    
              // 添加右键弹出操作
              children: [
                {id: "1-1", name: "叶子节点221",open: true},
                {id: "1-2", name: "叶子节点222",open: true},
                {id: "1-3", name: "叶子节点223",open: true},
                {id: "1-4", name: "叶子节点224",open: true}
              ]
            }, {
              id: "2",
              name: "父节点23 - 折叠",
              children: [
                {id: "2-1", name: "叶子节点231"},
                {id: "2-2", name: "叶子节点232"},
                {id: "2-3", name: "叶子节点233"},
                {id: "2-4", name: "叶子节点234"}
              ]
            }, {
              open: true,
              id: "3",
              name: "父节点24 - 折叠",
              children: [
                {
                  id: "2-1-1", name: "叶子节点232", open: true,
                  children: [
                    {id: "2-1-3", name: "叶子节点2313"},
                    {id: "2-2-3", name: "叶子节点2323"},
                    {id: "2-3-3", name: "叶子节点2333"},
                    {id: "2-4-3", name: "叶子节点2343"}
                  ]
                },
              ]
            },
          ];
        }
      }
    </script>
    
    <style scoped>
      .main ul li {
        list-style: none;
        float: left;
        width: 25%;
    
      }
    </style>
    
    
    

    3、使用el-tree动态加载

    展开全文
  • vue 形结构利用vue组件自己调用自己,循环遍历生成形结构。 组件调用自己时应绑定 name属性 拓展:给组件命名的三大作用 1.允许组件模板递归地调用自身; 2.指定 name 选项的另一个好处是便于调试。有名字的组件...

    vue 树形结构利用vue组件自己调用自己,循环遍历生成树形结构。
    组件调用自己时应绑定 name属性

    拓展:给组件命名的三大作用

    1.允许组件模板递归地调用自身;

    2.指定 name 选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。另外,当在有 vue-devtools,未命名组件将显示成 ,这很没有语义

    3.可提供给 keep-alive 的 include 和 exclude 作为参数选项;

    第一步:构造数据

    node:{
            name: '/',
            children: [
              {
                name: 'Music',
                children: [
                  {
                    name: '雨一直下.mp3'
                  },
                  {
                    name: '伍佰'
                  }
                ]
              },
              {
                name: 'Video',
                children: [
                  {
                    name: 'vue教学.mp4'
                  },
                  {
                    name: 'treeBowser.mp4'
                  }
                ]
              }
            ]
          },
    

    **

    第二步:创建组件并引入页面

    初步创建组件并渲染一级目录下的文件名称(name)
    这里我引用的是另一组数据,但是数据结构是一样的
    在这里插入图片描述

    **
    子组件

    <template>
      <div class="tree-box">
        <div class="tree-name">
          <span>{{root.name}}</span>
        </div>
      </div>  
    </template>
    

    页面

    <template>
      <div class="body">
        <h1>Vue Tree Browser</h1>
        <tree-two :root="root"/>
      </div>
    </template>
    
    <script>
    import TreeTwo from '@/components/tree/TreeTwo'
    import root from '@/lib/root.js'
    
    export default {
      components:{
        TreeTwo
      },
      data () {
        return {
          node:{
            name: '/',
            children: [
              {
                name: 'Music',
                children: [
                  {
                    name: '雨一直下.mp3'
                  },
                  {
                    name: '伍佰'
                  }
                ]
              },
              {
                name: 'Video',
                children: [
                  {
                    name: 'vue教学.mp4'
                  },
                  {
                    name: 'treeBowser.mp4'
                  }
                ]
              }
            ]
          },
          root,
        }
      },
      methods: {
        wasChildren(node){
          alert(node.name)
        }
      }
    }
    </script>
    
    <style lang="less" scoped>
      .body{
        color: #fff;
        width: 100%;
        background-color: #333;
      }
    </style>
    

    第三步:组件调用自身
    在这里插入图片描述
    来看下渲染效果,所有数据一起渲染看起来并不清爽,加一个子文件缩进,并让文字左对齐
    在这里插入图片描述

    加一个参数:indent 默认为零,并传入回调组件
    在这里插入图片描述
    在这里插入图片描述
    又能么点像了,但是子节点默认是不点开。加一条判断属性,点击开启或关闭。

    在这里插入图片描述
    在这里插入图片描述
    展开关闭做好后给他们加个图标
    在这里插入图片描述
    加个计算属性判断当前root有无子节点
    在这里插入图片描述

    在这里插入图片描述
    assets 这个文件下没有子节点 但是也可以点击。做个小优化,没有子节点不显示,或者换个图标

    在这里插入图片描述
    在这里插入图片描述
    现在展示已经差不多了,当文件进入最后一级节点,点击时加一些触发事件
    第一次做的时候碰到了问题,这里用到的是click方法与onClick事件。
    用子组件的click方法触发父组件的onClick事件
    子组件代码
    在这里插入图片描述
    页面代码
    在这里插入图片描述
    在这里插入图片描述
    这是第一次写的代码。这样写只会触发根目录。因为用的是组件回调。要在回调的组件中也加入onClick事件监听,并触发自己上一级的onClick事件
    在这里插入图片描述
    或者直接在子组件调用click方法
    在这里插入图片描述

    以上内容都是观看大佬的youtube视频。放个原视频链接
    https://www.youtube.com/watch?v=VdSf6SFPiFg&list=PLwXcwDw-S70k-bqhpr3xRoUJP87u5bKGq

    以上内容如有不足请大家多多指正,谢谢。

    展开全文
  • vue 实现目录树 递归组件 菜单展收
  • ="js/vue.js" > script > head > < body >   <!-- 递归模板 --> < template id ="diguiTempTest" > < li > < div :class ="{bold: isFolder}" > {{model.data.menuName}} div > < ul ...
  • vue 目录结构

    2021-01-27 17:04:00
    == 200) { this.$Message.error(res.data.msg) } else { T.nodes = res.data.data T.allNodes = res.data.data } }) }, beforeDestroy() { } } script> <style lang="scss" scoped> style> 组件 <template> ...
  • 需求: 需要的数据规则: 1 // demo data 2 var data = { 3 name: 'My Tree', 4 children: [ 5 { name: 'hello' }, 6 { name: 'wat' }, 7 { 8 name: 'child folder', ... 9 ...
  • 结构示意图├──index.html├──main.js├──router│└──index.js # 路由配置文件├──components # 组件目录│├──App.vue # 根组件│├──Home.vue # 大的框架结构组件│├──TreeView.vue│├──...
  • 日前,在工作中有一个类似chrome书签管理器的需求,在调研了iview的Tree组件,Vue-dragging,Vue-...本文着重实现形结构的展示,拖拽功能的完善后续会完善。 Vue作为组件化的框架,原则上所有的页面元素都由数据...
  • 最近在做一个接口自动化测试平台,需要前端展示当前服务器上的测试用例,自然而然的想到展示成一个文件目录树的形状,这样不管浏览、添加、删除等操作都可以比较方便的实现,也比较方便浏览。( BTW,你是不是觉着我...
  • vue项目目录介绍

    2019-09-23 17:56:58
    Vue项目目录 初始化项目 vue init webpack []projectname] cd [projectname] npm install vue run dev 目录树 +---build | build.js | check-versions.js | dev-client.js | dev-server.js | ...
  • vue 递归组件 (形结构目录

    千次阅读 2019-08-21 17:46:19
    在项目中 有时需要 分级目录 目录 这时就可以用到递归组件了 递归组件 是指就是组件中嵌套组件自身 首先我们需要一组这样的数据 [ { "name": "一级目录", cList: [ { "name": "二级目录" }, { "name": ...
  • vue树形组件

    万次阅读 2017-01-09 22:06:12
    vue-treevue 编写的形菜单,小巧实用,支持vue1.0,vue2.0v1.0 功能: 1.支持多级树目录 2.支持高亮点击的节点 3.支持展开点击节点 4.支持点击收缩节点时收缩所有子目录 5.支持自定义回调函数,点击节点时回调,...
  • <template> <el-tree ref="tree" :data="layers" #绑定数据 ...node-key="name" #key为对象的name属性 ...:default-expand-all="true" #默认全部展开 ...@check-change="checkChange" #节点选中改变事件 ...
  • 组件的应用场景很多,比如一篇文章的目录、一个公司部门组织情况、思维导图等,其实都可以用形结构来描述。本文讲述一下Vue组件的简单实现。组件在线体验地址:http://wintc.top/laboratory/#/tr...
  • 上一篇文章总结了vue树状目录组件,忘记的可以点击传送门回顾一下:vue目录树组件还是这个目录树,改版成表格树状结构。我这里采用的UI库是Element,库自带了一种实现方式:但是这种自带的实现方式,可扩展性很差,...
  • <script src="https://cdn.bootcss.com/vue/2.6.9/vue.min.js"> <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script> // var TreeData = [ // { // name: 'IT互联网', // child: ...
  • 递归组件的应用===》可以通过组件命名来自己使用自己的组件 实例如下 父组件 <div class="content"> <detail-list :list="categoryList"></detail-list> </div>... ...
  • 这个比较简单,因为就是从root开始的,所以直接拿到root节点的子节点数组,添加新节点对象即可。 addMenu() { const newChild = { title: '编辑菜单', key: (new Date()).getTime(), scopedSlots: {title: '...
  • html <!DOCTYPE html> <html> <head>...meta charset="utf-8"/>...导航目录</title> <link rel="stylesheet" type="text/css" href="css/index.css"> <link rel="...

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 325
精华内容 130
关键字:

vue目录树

vue 订阅