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

栏目: IT技术 · 发布时间: 5年前

内容简介:本次介绍的内容,稍稍复杂了一点,用VUE实现树形结构。目前这个属性结构还没有编辑功能,仅仅是展示。明天再开一篇文章,介绍如何增加编辑功能,标题都想好了。先看今天的展示效果:构建树必须用到递归,使用slot这种直观明了的方式,已经行不通了。只能通过属性参数,传递一个树形的数据结构给组件,传入的数据结构大致是这个样子:

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

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

构建树必须用到递归,使用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目录,放两个文件:

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

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

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

<template>
  <div>
    <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="inputValue.selected ? 'selected' :''"

  >
    <div 
      @click="click"  
      @contextmenu.prevent = 'onContextMenu'
    >
      <div  @click="iconClick">
        <i v-show="icon" :class="icon"></i>
      </div>
      {{inputValue.title}}
    </div>
    <div v-show="showChild">
      <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

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learn Python the Hard Way

Learn Python the Hard Way

Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99

Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试