前言

需求 来自于一些实际的场景 

这里 仅仅是一个 样例来实现 这一部分需求 

这部分 处理也相对比较简单, 就直接 展示 代码了 

1. 节点 增删改查 的实现(一种基于模型的实现, 一种基于 el-tree api 的实现)

2. 保存 el-tree 的 选择状态 

3. 保存 el-tree 的 展开状态 

样例代码

如下 增删改查两种实现方式, 注释掉的是基于 el-tree 的 api 的使用 

<template>

  <div>
    <div style="width: 20%; float: left;">
      (\#-_-)\┯━┯
    </div>

    <div style="width: 20%; float: left;">
      <el-tree
        ref="treeRef"
        :data="tree.nodeList"
        show-checkbox
        node-key="id"
        :default-checked-keys="tree.defaultCheckedKeys"
        :default-expanded-keys="tree.defaultExpandedKeys"
        :check-on-click-node="true"
        :props="tree.defaultProps"
        @node-expand="handleNodeExpand"
        @node-collapse="handleNodeCollapse"
      />
    </div>

    <div style="width: 15%; float: left;">
      <span> 新增 </span>
      <el-select v-model="newNode.parentId" filterable placeholder="请选择父节点">
        <el-option v-for="item in treeNodeList" :key="item.id" :label="item.name" :value="item.id"/>
      </el-select>
      <el-input placeholder="please input id" v-model="newNode.id">
        <template slot="prepend">key</template>
      </el-input>
      <el-input placeholder="please input name" v-model="newNode.name">
        <template slot="prepend">name</template>
      </el-input>
      <el-button @click="handleNewNode">提交</el-button>
    </div>

    <div style="width: 15%; float: left;">
      <span> 更新 </span>
      <el-select v-model="updateNode.id" filterable placeholder="请选择节点">
        <el-option v-for="item in treeNodeList" :key="item.id" :label="item.name" :value="item.id"/>
      </el-select>
      <el-input placeholder="please input name" v-model="updateNode.name">
        <template slot="prepend">name</template>
      </el-input>
      <el-button @click="handleUpdateNode">提交</el-button>
    </div>

    <div style="width: 15%; float: left;">
      <span> 移除 </span>
      <el-select v-model="removeNode.id" filterable placeholder="请选择节点">
        <el-option v-for="item in treeNodeList" :key="item.id" :label="item.name" :value="item.id"/>
      </el-select>
      <el-button @click="handleRemoveNode">提交</el-button>
    </div>

  </div>

</template>

<script>

  export default {
    name: 'ElTreeCrud',
    computed: {
      treeNodeList: function () {
        let collector = []
        this.collectNodeRecursed(this.tree.nodeList, collector)
        return collector
      }
    },
    data () {
      return {
        newNode: {
          parentId: 'backend',
          id: '',
          name: '',
        },
        updateNode: {
          id: 'backend',
          name: '',
        },
        removeNode: {
          id: 'backend',
        },
        tree: {
          nodeList: [{
            id: 'backend',
            name: '后端',
            children: [
              {
                id: 'backend1',
                name: '后端1',
                children: []
              }, {
                id: 'backend2',
                name: '后端2',
                children: []
              }
            ]
          }, {
            id: 'frontend',
            name: '前端',
            children: [
              {
                id: 'frontend1',
                name: '前端1',
                children: []
              }, {
                id: 'frontend2',
                name: '前端2',
                children: []
              }
            ]
          }, {
            id: 'preprocess',
            name: '数据',
            children: [
              {
                id: 'preprocess1',
                name: '数据1',
                children: []
              }, {
                id: 'preprocess2',
                name: '数据2',
                children: []
              }
            ]
          }],
          defaultExpandedKeys: [],
          defaultCheckedKeys: ['backend1', 'preprocess1'],
          defaultProps: {
            children: 'children',
            label: 'name'
          }
        }
      }
    },
    mounted () {
      window.addEventListener('beforeunload', e => this.beforeLeaveFunc(e))
      this.tree.defaultExpandedKeys = this.treeNodeList.map(ele => ele.id)

      // check if already stored
      // todo, if defaultCheckedKeys is not blank at data's init, then specified node will always be checked
      let treeNodeCheckedKeysStr = sessionStorage.getItem('treeNodeCheckedKeys')
      if (treeNodeCheckedKeysStr) {
        this.tree.defaultCheckedKeys = JSON.parse(treeNodeCheckedKeysStr)
        // JSON.parse(treeNodeCheckedKeysStr).forEach(ele => this.tree.defaultCheckedKeys.push(ele))
      }

      let expandedKeysStr = sessionStorage.getItem('expandedKeys')
      if (expandedKeysStr) {
        this.tree.defaultExpandedKeys = JSON.parse(expandedKeysStr)
      }
    },
    created () {

    },
    methods: {
      // beforeLeaveFunc
      beforeLeaveFunc () {
        let treeRef = this.$refs.treeRef
        let checkedKeys = treeRef.getCheckedKeys()
        sessionStorage.removeItem('treeNodeCheckedKeys')
        sessionStorage.setItem('treeNodeCheckedKeys_tmp', JSON.stringify(checkedKeys))
        if (checkedKeys.length > 0) {
          sessionStorage.setItem('treeNodeCheckedKeys', JSON.stringify(checkedKeys))
        }

        // let treePropRoot = treeRef.root
        // let expandedKeys = []
        // this.expandedKeys(treePropRoot, expandedKeys)
        // sessionStorage.removeItem('expandedKeys')
        // sessionStorage.setItem('expandedKeys_tmp', JSON.stringify(expandedKeys))
        // if(expandedKeys.length > 0) {
        //   sessionStorage.setItem('expandedKeys', JSON.stringify(expandedKeys))
        // }
      },
      // curd
      handleNewNode () {
        if (!this.newNode.id || !this.newNode.name) {
          this.$message.error('please input id and name')
          return
        }
        let targetNode = this.lookUpNode(this.tree.nodeList, this.newNode.id)
        if (targetNode) {
          this.$message.error('update node\'s with id ' + this.newNode.id + ' already exists')
          return
        }

        let newNode = {
          id: this.newNode.id,
          name: this.newNode.name,
          children: []
        }

        // let treeRef = this.$refs.treeRef
        // treeRef.append(newNode, treeRef.getNode(this.newNode.parentId))
        // this.$message.success(' add new node ' + this.newNode.name + ' at parentNode ' + this.lookUpNode(this.tree.nodeList, this.newNode.parentId).name + ' success')

        let parentNode = this.lookUpNode(this.tree.nodeList, this.newNode.parentId)
        parentNode.children.push(newNode)
      },
      handleUpdateNode () {
        if (!this.updateNode.id || !this.updateNode.name) {
          this.$message.error('please input id and name')
          return
        }
        let targetNode = this.lookUpNode(this.tree.nodeList, this.updateNode.id)
        if (!targetNode) {
          this.$message.error('update node\'s with id ' + this.newNode.id + ' does not exists')
          return
        }

        // let treeRef = this.$refs.treeRef
        // targetNode.name = this.updateNode.name
        // treeRef.updateKeyChildren(this.updateNode.id, targetNode)

        targetNode.name = this.updateNode.name
      },
      handleRemoveNode () {
        // let treeRef = this.$refs.treeRef
        // let targetNode = this.lookUpNode(this.tree.nodeList, this.removeNode.id)
        // if (!targetNode) {
        //   this.$message.error('update node\'s with id ' + this.removeNode.id + ' does not exists')
        //   return
        // }
        // treeRef.remove(targetNode)

        let parentNode = this.lookUpParentNode(this.tree.nodeList, null, this.removeNode.parentId)
        let idxOfNode = parentNode.children.map(ele => ele.id).indexOf(this.removeNode.id)
        parentNode.children.splice(idxOfNode, 1)
      },
      handleNodeExpand (node, nodeProp, treeNode) {
        console.log('handleNodeExpand')
        let treeRef = this.$refs.treeRef
        let treePropRoot = treeRef.root

        let expandedKeys = []
        this.expandedKeys(treePropRoot, expandedKeys)
        sessionStorage.removeItem('expandedKeys')
        sessionStorage.setItem('expandedKeys_tmp', JSON.stringify(expandedKeys))
        if (expandedKeys.length > 0) {
          sessionStorage.setItem('expandedKeys', JSON.stringify(expandedKeys))
        }
      },
      handleNodeCollapse (node, nodeProp, treeNode) {
        // update current node's expand
        nodeProp.expanded = false
        this.handleNodeExpand(node, nodeProp, treeNode)
      },
      // assist methods
      collectNodeRecursed (nodeList, collector) {
        if (!nodeList) {
          return null
        }

        for (let idx in nodeList) {
          let childNode = nodeList[idx]
          collector.push({id: childNode.id, name: childNode.name})

          if (childNode.children) {
            this.collectNodeRecursed(childNode.children, collector)
          }
        }
      },
      lookUpNode (nodeList, id) {
        if (!nodeList) {
          return null
        }

        for (let idx in nodeList) {
          let childNode = nodeList[idx]
          if (id === childNode.id) {
            return childNode
          }

          if (childNode.children) {
            let result = this.lookUpNode(childNode.children, id)
            if (result) {
              return result
            }
          }
        }

        return null
      },
      lookUpParentNode (nodeList, parentNode, id) {
        if (!nodeList) {
          return null
        }

        for (let idx in nodeList) {
          let childNode = nodeList[idx]
          if (id === childNode.id) {
            if (!parentNode) {
              parentNode = {
                children : this.tree.nodeList
              }
            }
            return parentNode
          }

          if (childNode.children) {
            let result = this.lookUpParentNode(childNode.children, childNode, id)
            if (result) {
              return result
            }
          }
        }

        return null
      },
      expandedKeys (nodeFromRef, collector) {
        if (nodeFromRef.expanded) {
          collector.push(nodeFromRef.data.id)
        }
        // 如果不是 root 节点, 并且没有展开, 不继续处理
        if ((nodeFromRef.id !== 0) && (!nodeFromRef.expanded)) {
          return
        }

        if (nodeFromRef.childNodes) {
          for (let idx in nodeFromRef.childNodes) {
            let childNode = nodeFromRef.childNodes[idx]
            this.expandedKeys(childNode, collector)
          }
        }
      }
    }
  }

</script>

<!-- Add 'scoped' attribute to limit CSS to this component only -->
<style scoped>
</style>

选中状态, 展开状态 的持久化和初始化 

刷新页面的时候 保存选中状态 

16 el-tree 保存树的 选择状态, 展开状态_elementui

节点展开/收缩的时候 保存展开状态 

16 el-tree 保存树的 选择状态, 展开状态_stateful_02

初始化的时候 初始化 选中状态, 展开状态 

16 el-tree 保存树的 选择状态, 展开状态_elementui_03

展示效果

增删改查的演示 

16 el-tree 保存树的 选择状态, 展开状态_stateful_04

保存 选中状态, 展开状态 

 

16 el-tree 保存树的 选择状态, 展开状态_1024程序员节_05

我们看一下 sessionStorage 中存放的 expandedKeys, chekcedKeys 的数据, 是没有问题的

16 el-tree 保存树的 选择状态, 展开状态_1024程序员节_06

 刷新之后的状态, 可以看到这里 "后端1" 的状态是没有保存下来的, 这是另外的一个问题 

这个我们下一个文章 再来讲解, 需要 调试到 element 的代码 

16 el-tree 保存树的 选择状态, 展开状态_vue.js_07