vue目录树组件

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

通常数据类展现内容,大多采用树状结构展现内容。相似效果以下:
javascript

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

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

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; } }}

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

目录树组件java

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

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

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

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

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

这里把事件简化了,只分了两个事件,一个是展开收起,一个是一系列编辑操做,传个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数组,根据数据结构,使用递归构建布局。

over~加班快乐

本文分享自微信公众号 - 前端一块儿学(gh_3ba18d51f982)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索