本次介绍的内容,稍稍复杂了一点,用VUE实现树形结构。目前这个属性结构还没有编辑功能,仅仅是展示。明天再开一篇文章,介绍如何增加编辑功能,标题都想好了。先看今天的展示效果:

构建树必须用到递归,使用slot这种直观明了的方式,已经行不通了。只能通过属性参数,传递一个树形的数据结构给组件,传入的数据结构大致是这个样子:

[
{
title:‘页面 ’
selected:false,
opened:false,
isFolder:true,
children:[
{
title:'index.html',
selected:false,
opened:false,
icon:"far fa-file-code",
},
{
title:'product.html',
selected:false,
opened:false,
icon:"far fa-file-code",
},
],
},
{
title:‘样式’
selected:false,
opened:false,
isFolder:true,
children:[
{
title:'style.css',
selected:false,
opened:false,
icon:"far fa-file-code",
},
],
},
]

每个节点通过children嵌套子节点。需要注意的是,我们希望这颗树是可以被编辑的,可以增加、删除、编辑其节点,所以需要数据的双向绑定,不能通过普通属性props传递给组件,而是通过v-model传递。
RXEditor项目中,只有两个地方用到了树形结构,要制作的组件满足这两处需求就可以,因为不是构建一个通用类库,就可以相对简单些。这两处地方一处用于展示并编辑文件目录结构,一处是节点树,纯显示,没有编辑功能。文件树只有叶子节点可以被选中,节点树所有节点都可以被选中。都是单选,无复选需求。
给这个控件取个大气的名字,叫NodeTree吧,先看如何使用NodeTree。
第一处调用:

<NodeTree v-model="files"
:openIcon="'fas fa-folder-open'"
:closeIcon="'fas fa-folder'" >
</NodeTree>

第二处调用:

<NodeTree v-model="nodes"
:openIcon="'fas fa-caret-down'"
:closeIcon="'fas fa-caret-right'"
:leafIcon="''"
:folderCanbeSelected = 'true'>
</NodeTree>

通过v-model传递树形数据结构,openIcon是节点展开时的图标,closeIcion是节点闭合时的图标,leafIcon是没有子节点时的图标。这些图标如果不设置,会有缺省值,是文件夹跟文件的样子。为了增加可扩展性,树形数据结构也可以放置图标,数据结构里的图标设置优先级高,可以覆盖控件的设置。明白个原理,想做成什么样子,看自己的项目需求。folderCanbeSelected 参数是指含有子节点的节点(比如文件夹)是否可以被选中。

在src目录下新建tree目录,放两个文件:

NodeTree是树形控件,TreeNode是树形控件内部的节点,名字稍微优点绕,但是是我喜欢的命名方式。

NodeTree.vue的代码(省略CSS):

<template>
<div class="node-tree">
<TreeNode v-for = "(node, i) in inputValue"
:key = "i"
v-model = "inputValue[i]"
:openIcon = "openIcon"
:closeIcon = "closeIcon"
:leafIcon = "leafIcon"
:folderCanbeSelected = "folderCanbeSelected"
@nodeSelected = "nodeSelected"
></TreeNode>
</div>
</template> <script>
import TreeNode from "./TreeNode.vue" export default {
name: 'FileTree',
props: {
value: { default: []},
openIcon:{ default: 'fas fa-folder-open'},
closeIcon:{ default: 'fas fa-folder'},
leafIcon:{ default: 'fas fa-file' },
folderCanbeSelected:{ default:false }
},
components:{
TreeNode
},
data() {
return {
};
}, computed:{
inputValue: {
get:function() {
return this.value;
},
set:function(val) {
this.$emit('input', val);
},
},
}, methods: {
nodeSelected(selectedNode){
this.inputValue.forEach(child=>{
this.resetSelected(selectedNode, child)
})
this.$emit('nodeSelected', selectedNode)
}, //递归充置选择状态
resetSelected(selectedNode, node){
node.selected = (node === selectedNode)
if(node.children){
node.children.forEach(child=>{
this.resetSelected(selectedNode, child)
})
}
}
},
}
</script>

这个代码逻辑很简单,就是接收外面参数,循环调用TreeNode。要自定义v-model的话,需要用到属性(props)value,计算属性inputValue用于修改value,具体原理,可以参考VUE官方文档。
需要特殊注意的是nodeSelected事件,这个事件在子节点产生,通过冒泡的方式层层往父节点发送,最后到达NodeTree组件。NodeTree组件再通过$emit方法,分发到外层调用组件。
这次实现的控件是单选,排他的,需要递归调用resetSelected方法消除其它节点的选中状态。

TreeNode组件的代码如下(省略CSS,如需要,请到GIthub获取):

<template>
<div class="tree-node" :class="inputValue.selected ? 'selected' :''" >
<div class="node-title"
@click="click"
@contextmenu.prevent = 'onContextMenu'
>
<div class="node-icon" @click="iconClick">
<i v-show="icon" :class="icon"></i>
</div>
{{inputValue.title}}
</div>
<div v-show="showChild" class="children-nodes">
<TreeNode v-for="(child, i) in inputValue.children"
:openIcon = "openIcon"
:closeIcon = "closeIcon"
:leafIcon = "leafIcon"
:key="i"
:folderCanbeSelected = "folderCanbeSelected"
v-model="inputValue.children[i]"
@nodeSelected = "nodeSelected"
></TreeNode>
</div>
</div>
</template> <script>
export default {
name: 'TreeNode',
props: {
value: { default: {}},
openIcon:{ default: 'fas fa-folder-open'},
closeIcon:{ default: 'fas fa-folder'},
leafIcon:{ default: 'fas fa-file' },
folderCanbeSelected:{default: false},
},
data() {
return {
}
}, computed:{
inputValue: {
get:function() {
return this.value;
},
set:function(val) {
this.$emit('input', val);
},
}, icon(){
if(this.hasChildren){
return this.inputValue.opened ? this.openIcon : this.closeIcon
}
return this.inputValue.icon !== undefined ? this.inputValue.icon : this.leafIcon
}, showChild(){
return this.hasChildren && this.inputValue.opened
}, hasChildren(){
return this.inputValue.children
&&this.inputValue.children.length > 0
},
}, methods: {
click(){
if((this.hasChildren && this.folderCanbeSelected) || !this.hasChildren){
this.inputValue.selected = true
this.$emit('nodeSelected', this.inputValue)
}
else {
this.inputValue.opened = !this.inputValue.opened
}
}, iconClick(event){
if(this.hasChildren && this.folderCanbeSelected){
event.stopPropagation()
this.inputValue.opened = !this.inputValue.opened
}
}, nodeSelected(node){
this.$emit('nodeSelected', node)
}, onContextMenu(event){
console.log(event)
}
}, }
</script>

父组件调用时通过v-mode,把整个节点的数据传入该控件。该组件递归调用自身,从而形成树形结构。三个状态:opened(展开),closed(闭合),selected(选中)存于model数据中,这样在控件外部,通过修改model,也可以控制节点状态。

本功能介绍完毕,代码请自行到github获取相应历史版本:
https://github.com/vularsoft/studio-ui

 

VUE实现Studio管理后台(七):树形结构,文件树,节点树共用一套代码NodeTree的更多相关文章

  1. VUE实现Studio管理后台(二):Slot实现选项卡tab切换效果,可自由填装内容

    作为RXEditor的主界面,Studio UI要使用大量的选项卡TAB切换,我梦想的TAB切换是可以自由填充内容的.可惜自己不会实现,只好在网上搜索一下,就跟现在你做的一样,看看有没有好事者实现了类 ...

  2. VUE实现Studio管理后台(十):OptionBox,一个综合属性输入界面,可以级联重置

    为了便于阅读代码,已经把测试数据分离出来,放在了mock目录下: 阅读代码的话,稍微留意一下就好.本次介绍RXEditor界面最重要的部分,属性输入组件,该组件可以显示是否有数据被修改,还可以批量重置 ...

  3. VUE实现Studio管理后台(一):鼠标拖放改变窗口大小

    近期改版RXEditor,把改版过程,用到的技术点,记录下来.昨天完成了静态页面的制作,制作过程并未详细记录,后期已经不愿再补了,有些遗憾.不过工作成果完整保留在github上,地址:https:// ...

  4. VUE实现Studio管理后台(三):支持多语言国际化(vue-i18n)

    RXEditor的第一版本是英文版,有些朋友看起来觉得不习惯,后来因为惰性,不愿意再修改旧代码加入中文版,这次提前就把这个问题解决了,克服惰性最好的方式,就是想到就尽快去做,避免拖延. 本来计划在界面 ...

  5. VUE实现Studio管理后台(九):开关(Switch)控件,输入框input系列

    接下来几篇作文,会介绍用到的输入框系列,今天会介绍组普通的调用方式,因为RXEditor要求复杂的输入功能,后面的例子会用VUE的component动态调用,就没有今天的这么直观了,控件的实现原理都一 ...

  6. VUE实现Studio管理后台(完结):标签式输入、名值对输入、对话框(modal dialog)

    一周的时间,几乎每天都要工作十几个小时,敲代码+写作文,界面原型算是完成了,下一步是写内核的HTML处理引擎,纯JS实现.本次实战展示告一段落,等RXEditor下一个版本完成,再继续分享吧.剩下的功 ...

  7. VUE实现Studio管理后台(十三):按钮点选输入控件,input输入框系列

    按钮点选输入,是一个非常简单的控件,20分钟就能完成的一个控件.先看效果: 根据以前的设定,通过json数据动态生成这两个按钮,示例中这两个按钮对应的json代码: { label:'标题', val ...

  8. vue_shop(基于vue电商管理后台网站)

    vue_shop 目录 vue_shop day01 实现登录功能 项目预开发处理 Login.vue完整代码: 处理步骤: 添加element-ui的表单组件 添加第三方字体: 添加表单验证 导入a ...

  9. vue.js与element-ui实现菜单树形结构

    由于业务需要,要求实现树形菜单,且菜单数据由后台返回,在网上找了几篇文章,看下来总算有了解决办法.借鉴文章链接在最底部. 场景:根据业务要求,需要实现活动的树形菜单,菜单数据由后台返回,最后的效果图如 ...

随机推荐

  1. Regex: positive lookahead 先行断言____ 后行断言(lookbehind)

    先行断言: /a(?=b)/  ,positive lookahead,a的后方必须是b才行 /a(?!b)/   ,negative lookahead,a的后方必须不是b才能匹配 如下图示:  来 ...

  2. Python运维中常用的_脚本

    前言 file是一个类,使用file('file_name', 'r+')这种方式打开文件,返回一个file对象,以写模式打开文件不存在则会被创建.但是更推荐使用内置函数open()来打开一个文件. ...

  3. python Pandas Profiling 一行代码EDA 探索性数据分析

    文章大纲 1. 探索性数据分析 代码样例 效果 解决pandas profile 中文显示的问题 1. 探索性数据分析 数据的筛选.重组.结构化.预处理等都属于探索性数据分析的范畴,探索性数据分析是帮 ...

  4. Tript协议|伯尔尼公约|著作权|立法宗旨|自动保护|著作权集体管理|

    知识产权 国际条约: Tript协议是国际性公约,<与贸易有关的知识产权协定>(英文:Agreement on Trade-Related Aspects of Intellectual ...

  5. docker常用的命令-在自己心情低落的时候,告诫自己不要把负能量带给别人。

    daocker基础命令:https://www.cnblogs.com/xiaowenshu/p/10474746.html 一.Docker常用命令 安装部署好Dokcer后,执行的命令是docke ...

  6. TreeviewEditor.rar

    本工具可以打开.保存指定格式的XML文件. 树形控件的节点可以编辑.删除.增加.使用本工具看方便地创建书或论文的目录大纲,我用这个工具已经写了好几本书了. 动态图1: 动态图2:编辑效果,支持节点拖曳 ...

  7. centos 中文乱码解决办法

    缘由:本人在虚拟机中安装centos 5.3,起初安装时选择了english,后来使用的过程中发现打开网页,会出现中文乱码,无法正常显示.当然,本地文件中的中文更是无法显示. 若是将系统语言langu ...

  8. python学习笔记(4)数据类型-元组

    元组其实和列表一样,不一样的是,元组的值不能改变,一旦创建,就不能再改变了,比如说,要存数据库的连接信息,这个连接信息在程序运行中是不能被改变的,如果变了那数据库连不上了,就程序就完犊子了,这样的就可 ...

  9. LeetCode Day 6

    LeetCode0006 将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" ,指定行数为 3 时,排 ...

  10. 史无前例的KDD 2014大会记

    2014大会记" title="史无前例的KDD 2014大会记"> 作者:蒋朦 微软亚洲研究院实习生 创造多项纪录的KDD 2014 ACM SIGKDD 国际会 ...