Vue studio management background (7): tree structure, file tree, node tree share a set of code nodetree

Time:2021-5-30

The content of this introduction is a little more complicated, using Vue to realize the tree structure. At present, this attribute structure has no editing function, just display. I’ll open another article tomorrow to introduce how to add editing function. I’ve thought about the title. Let’s look at the effect of today’s exhibition

Vue studio management background (7): tree structure, file tree, node tree share a set of code nodetree

Recursion must be used to build a tree. Using slot as an intuitive way is no longer feasible. You can only pass a tree data structure to a component through attribute parameters. The incoming data structure looks like this:

[
        {
          Title: "page"
          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: "style"
          selected:false,
          opened:false,
          isFolder:true,
          children:[
            {
              title:'style.css',
              selected:false,
              opened:false,
              icon:"far fa-file-code",
            },
          ],
        },
]

Each node embeds child nodes through children. It should be noted that we hope that this tree can be edited, and its nodes can be added, deleted, and edited. Therefore, we need two-way binding of data, which can not be passed to the component through the ordinary attribute props, but through the V-model.
In the rxeditor project, there are only two places where the tree structure is used. The components to be made can meet these two requirements, because it is relatively simple to build a general class library. One of the two places is used to display and edit the file directory structure, and the other is node tree, which is pure display and has no editing function. Only leaf nodes in the file tree can be selected, and all nodes in the node tree can be selected. They are all single choice, and there is no need to select.
Give this control an atmospheric name, nodetree. Let’s see how to use nodetree first.
First call:

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

Second call:

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

Openicon is the icon when the node is expanded, closeicon is the icon when the node is closed, and leaficon is the icon when there is no child node. If these icons are not set, they will have default values, which are the appearance of folders and files. In order to increase the scalability, the tree data structure can also place icons. The icons in the data structure have high priority and can override the control settings. Understand a principle, want to make what look like, see their own project requirements. The foldercanbeselected parameter refers to whether a node (such as a folder) with child nodes can be selected.

Create a new tree directory under SRC directory and put two files in it

Vue studio management background (7): tree structure, file tree, node tree share a set of code nodetree

Nodetree is a tree control, and treenode is the node inside the tree control. The name is a little better, but it’s my favorite way of naming.

Code for nodetree.vue (omitting 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)
    }, // recursively sets the selection state
 resetSelected(selectedNode, node){
      node.selected = (node === selectedNode) if(node.children){
        node.children.forEach(child=>{ this.resetSelected(selectedNode, child)
        })
      }
    }
  },
} </script>

The logic of this code is very simple, that is, to receive external parameters and call treenode cyclically. To customize the V-model, you need to use the attribute (props) value, and calculate the attribute inputvalue to modify the value. For the specific principle, please refer to the Vue official document.
Special attention should be paid to the nodeselected event, which is generated in the child node and sent layer by layer to the parent node in a bubbling way, and finally to the nodetree component. The nodetree component is then distributed to the outer calling component through the $emit method.
The control implemented this time is single selection and exclusive. You need to call the resetselected method recursively to eliminate the selected state of other nodes.

The code of treenode component is as follows (omit CSS, please go to GitHub to get it if necessary)

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

When the parent component calls, it transfers the data of the whole node into the control through v-mode. The component calls itself recursively to form a tree structure. Three states: opened, closed and selected are stored in the model data, so that the node state can be controlled by modifying the model outside the control.
After the introduction of this function, please go to GitHub to get the corresponding historical version:
https://github.com/vularsoft/studio-ui

This work adoptsCC agreementReprint must indicate the author and the link of this article