Is VUE hungry? Add, delete and modify functions for tree controls

The general effect is shown in the figure:

1. Provincial and municipal API

The list of provinces and cities is copied on the Internet, and two properties are added

  • isEdit: controls editing status
  • maxexpandId: the maximum current id
export default{
    maxexpandId: 95,
    treelist: [{  
        id: 1,  
        name: "Beijing",  
        ProSort: 1,  
        remark: "municipality directly under the Central Government",
        pid: '',
        isEdit: false,
        children: [{
            id: 35,
            name: "Chaoyang District",
            pid: 1,
            remark: '',
            isEdit: false,
            children: []
        }]
    }{...}]
}

2. El tree component basic

Let's write a hungry component step by step

<template>
    <el-tree ref="expandMenuList" class="expand-tree"
        v-if="isLoadingTree"
        :data="setTree"
        node-key="id"
        highlight-current
        :props="defaultProps"
        :expand-on-click-node="false"
        :render-content="renderContent"
        :default-expanded-keys="defaultExpandKeys"></el-tree>
</template>
<!--
* highlight-current : To highlight a node when clicked
* expand-on-click-node : Only arrows can control the expansion and contraction of the tree
* render-content : Node rendering mode
* default-expanded-keys : Default deployment node
-->

API and node rendering components are introduced at the same time

import TreeRender from '@/components/tree_render'
import api from '@/resource/api'

Then build the foundation

data(){
  return{
    maxexpandId: api.maxexpandId,//New node start id
    non_maxexpandId: api.maxexpandId,//New node start ID (not changed)
    isLoadingTree: false,//Load node tree
    setTree: api.treelist,//Node tree data
    defaultProps: {
      children: 'children',
      label: 'name'
    },
    defaultExpandKeys: [],//Default expanded node list
  }
},

Add a render method

methods: {
    renderContent(h,{node,data,store}){
      let that = this;//Point to vue
      return h(TreeRender,{
        props: {
          DATA: data,//Node data
          NODE: node,//Node content
          STORE: store,//Complete tree content
        },
        on: {//Binding method
          nodeAdd: ((s,d,n) => that.handleAdd(s,d,n)),
          nodeEdit: ((s,d,n) => that.handleEdit(s,d,n)),
          nodeDel: ((s,d,n) => that.handleDelete(s,d,n))
        }
      });
    },
    handleAdd(s,d,n){//Add node
      console.log(s,d,n)
    },
    handleEdit(s,d,n){//Edit node
      console.log(s,d,n)
    },
    handleDelete(s,d,n){//Delete node
      console.log(s,d,n)
    }
}

3.tree_render Component basic

Render components:

<template>
    <span class="tree-expand">
        <span class="tree-label">
            <span>{{DATA.name}}</span>
        </span>
        <span class="tree-btn">
            <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
            <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
            <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
        </span>
    </span>
</template>

Add methods corresponding to several buttons (icon: address of element UI):

export default{
    props: ['NODE', 'DATA', 'STORE'],
    methods: {
      nodeAdd(s,d,n){//newly added
        this.$emit('nodeAdd',s,d,n)
      },
      nodeEdit(s,d,n){//edit
        this.$emit('nodeEdit',s,d,n)
      },
      nodeDel(s,d,n){//delete
        this.$emit('nodeDel',s,d,n)
      }
    }
}

4. Change

We use isEdit to switch the display status of input and span. First, add an input:

<!-- tree_render component -->
<template>
    <span class="tree-expand">
        <span class="tree-label" v-if="DATA.isEdit">
            <el-input class="edit" size="mini"
            :ref="'treeInput'+DATA.id"
            v-model="DATA.name"></el-input>
        </span>
        <template v-else>
            <span class="tree-label">
                <span>{{DATA.name}}</span>
            </span>
            <span class="tree-btn" v-show="!DATA.isEdit">
                <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
                <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
                <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
            </span>
        </template>
    </span>
</template>

When editing, the buttons disappear at the same time, so when will the editing be completed?

After editing, press the Enter key to listen for the enter input of input
Click "auto focus =" when other nodes are out of focus
Click the current node range
When one of the above three points occurs, the data corresponding to the node must be isEdit = false;

  1. enter key
<!-- tree_render component -->
<el-input @keyup.enter.native="nodeEditPass(STORE,DATA,NODE)"></el-input>

Add method:

//tree_render component
methods: {
    nodeEditPass(s,d,n){
        d.isEdit = false;
    }
}
  1. focus or blur
<!-- tree_render component -->
<el-input @blur="nodeEditPass(STORE,DATA,NODE)"></el-input>

Later, it was found that the input can be focused during the first editing, and clicking the second input will not work. The same is true with the autofocus attribute. So we need to use the native input autofocus when clicking Edit icon.
Modification method:

//tree_render component
nodeEdit(s,d,n){//edit
  d.isEdit = true;
  this.$nextTick(() => {
    this.$refs['treeInput'+d.id].$refs.input.focus()
  })
  this.$emit('nodeEdit',s,d,n)
}
  1. Current node Click

Adopt the existing API of El tree node click

<!-- el-tree component -->
<el-tree @node-click="handleNodeClick"></el-tree>

Add methods:

//el-tree component
methods: {
    handleNodeClick(d,n,s){//Click node
      d.isEdit = false;//Discard editing status
    }
}

Here's the problem. If you click this node in the editing state, it will also affect the input, which cannot enter the editing. Therefore, to prevent the input event from bubbling:

<!-- tree_render component -->
<el-input @click.stop.native="nodeEditFocus"></el-input>

Add methods:

//tree_render component
methods: {
    nodeEditFocus(){}
}
  1. v-show instead of v-if

There is a new problem here. When users often edit and modify, the cost of v-if template is higher, so they use v-show instead. The latter does not support template template, so you should adjust the position appropriately:

<template>
    <span class="tree-expand">
        <span class="tree-label" v-show="DATA.isEdit">
            <el-input class="edit" size="mini" autofocus
            v-model="DATA.name"
            :ref="'treeInput'+DATA.id"
            @click.stop.native="nodeEditFocus"
            @blur="nodeEditPass(STORE,DATA,NODE)"
            @keyup.enter.native="nodeEditPass(STORE,DATA,NODE)"></el-input>
        </span>
        <span v-show="!DATA.isEdit">
            <span>{{DATA.name}}</span>
        </span>
        <span class="tree-btn" v-show="!DATA.isEdit">
            <i class="el-icon-plus" @click.stop="nodeAdd(STORE,DATA,NODE)"></i>
            <i class="el-icon-edit" @click.stop="nodeEdit(STORE,DATA,NODE)"></i>
            <i class="el-icon-delete" @click.stop="nodeDel(STORE,DATA,NODE)"></i>
        </span>
    </span>
</template>

5. Increase

New node = > add a piece of data

  1. Expand parent node while adding
  2. Consider unlimited addition
//el-tree component
handleAdd(s,d,n){//Add node
  console.log(s,d,n)
  if(n.level >=6){
    this.$message.error("Only five levels are supported at most!")
    return false;
  }
  //Add data
  d.children.push({
    id: ++this.maxexpandId,
    name: 'New node',
    pid: d.id,
    isEdit: false,
    children: []
  });
  //Expand node
  if(!n.expanded){
    n.expanded = true;
  }
}

Add node font bold = "add a class =" to the node how to judge whether to add?
We have a parameter maxexpandId
Give it to tree_render adds a prop:

//el-tree component
renderContent(h,{node,data,store}){//Load node
  let that = this;
  return h(TreeRender,{
    props: {
      ...
      maxexpandId: that.non_maxexpandId
    },
    on: {...}
  });
}

Judging by id:

//tree_render component
props: ['NODE', 'DATA', 'STORE', 'maxexpandId']
<!-- tree_render component -->
<span v-show="!DATA.isEdit" 
:class="[DATA.id > maxexpandId ? 'tree-new tree-label' : 'tree-label']"
:ref="'treeLabel'+DATA.id">
    <span>{{DATA.name}}</span>
</span>
.tree-expand .tree-label.tree-new{
    font-weight:600;
}

6. Delete

Synonymous with adding: delete node = > delete a piece of data

Add a node and delete it directly
Existing nodes need to be prompted to delete
Existing child nodes cannot be deleted

handleDelete(s,d,n){//Delete node
  console.log(s,d,n)
  let that = this;
  //Children not deleted
  if(d.children && d.children.length !== 0){
    this.$message.error("This node has children and cannot be deleted!")
    return false;
  }else{
    //Delete operation
    let delNode = () => {
      let list = n.parent.data.children || n.parent.data,
      //The node has the same level data, and there are no children in the top-level node
        _index = 99999;//index to delete
      list.map((c,i) => {
        if(d.id == c.id){
          _index = i;
        }
      })
      let k = list.splice(_index,1);
      //console.log(_index,k)
      this.$message.success("Delete succeeded!")
    }
    let isDel = () => {
      that.$confirm("Delete this node?","Tips",{
        confirmButtonText: "confirm",
        cancelButtonText: "cancel",
        type: "warning"
      }).then(() => {
        delNode()//You can delete it here through ajax
      }).catch(() => {
        return false;
      })
    }
    //The newly added node can be deleted directly, otherwise it must be deleted by requesting data
    d.id > this.non_maxexpandId ? delNode() : isDel()
  }
}

7. Expansion

There are also some special needs, such as:

  1. Click to highlight the icon
.expand-tree .is-current>.el-tree-node__content .tree-btn,
.expand-tree .el-tree-node__content:hover .tree-btn{
  display: inline-block;
}
  1. Add top-level node

Add button:

<!-- el-tree component -->
<el-button @click="handleAddTop">Add top-level node</el-button>
//el-tree component
methods: {
  handleAddTop(){
    this.setTree.push({
      id: ++this.maxexpandId,
      name: 'New node',
      pid: '',
      isEdit: false,
      children: []
    })
  }
}
  1. By default, expand the first level of the tree
//el-tree component
mounted(){
  this.initExpand()
},
methods: {
  initExpand(){
    //This is also the purpose of isLoadingTree
    this.setTree.map((a) => {
      this.defaultExpandKeys.push(a.id)
    });
    this.isLoadingTree = true;
  },
}

Keywords: Vue elementUI

Added by notaloser on Mon, 03 Jan 2022 04:41:51 +0200