1.封装

根据官网配置项封装了下el-tree 方便维护和复用,有用的话点赞收藏叭~

<template>
  <div class="my-tree">
    <el-input
      v-if="hasSearch"
      v-model="filterText"
      class="search-input"
      placeholder="输入关键字进行过滤">
    </el-input>
    <slot></slot>
    <el-tree 
      ref="myTree"
      :icon-class="iconClass" 
      :filter-node-method="filterNodeMethod"
      :default-checked-keys="defaultCheckedKeys" 
      :check-strictly="checkStrictly" 
      :show-checkbox="showCheckbox" 
      :default-expanded-keys="defaultExpandedKeys"
      :highlight-current="highlightCurrent"
      :check-on-click-node="checkOnClickNode"
      :default-expand-all="defaultExpandAll"
      :expand-on-click-node="expandOnClickNode"
      :node-key="nodeKey"
      :data="treeData"
      :props="defaultProps" 
      :empty-text="emptyText"
      :render-content="renderContent"
      @node-click="nodeClick"
      @check="check"
      @check-change="checkChange"
      @current-change="currentChange"
      @node-expand="nodeExpand"
      @node-collapse="nodeCollapse">
    </el-tree>
  </div>
</template>

<script>
export default {
  name: 'MyTree',
  props: {
    nodeKey: {
      type: String,
      default: 'id'
    },
    // 树形控件展示数据
    treeData: {
      type: Array,
      default: function () {
        return []
      }
    },
    // 默认展开的节点的 key 的数组	
    defaultExpandedKeys: {
      type: Array,
      default: function () {
        return []
      }
    },
    // props配置项
    defaultProps: {
      type: Object,
      default: function () {
        return {
          label: 'label',
          children: 'children',
          disabled: function (data, node) {
            return data
          }
        }
      }
    },
    // 内容为空时的文案
    emptyText: {
      type: String,
      default: '暂无数据'
    },
    // 是否高亮当前选中
    highlightCurrent: {
      type: Boolean,
      default: false
    },
    // 是否默认展开所有节点
    defaultExpandAll: {
      type: Boolean,
      default: false
    },
    // 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。
    expandOnClickNode: {
      type: Boolean,
      default: true
    },
    // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
    checkOnClickNode: {
      type: Boolean,
      default: false
    },
    // 节点是否可被选择
    showCheckbox: {
      type: Boolean,
      default: false
    },
    // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法
    checkStrictly: {
      type: Boolean,
      default: false
    },
    // 默认勾选的节点的 key 的数组
    defaultCheckedKeys: {
      type: Array,
      default: function () {
        return []
      }
    },
    // 是否需要搜索功能
    hasSearch: {
      type: Boolean,
      default: false
    },
    // 自定义树节点的图标
    iconClass: {
      type: String,
      default: ''
    },
    // 自定义节点内容
    renderContent: {
      type: Function,
      default: function (h, { node, data, store }) {
        return (
          <span class="custom-tree-node">
            <span>{node.label}</span>
          </span>)
      }
    }
  },
  data() {
    return {
      filterText: '' // 关键字过滤值
    }
  },
  watch: {
    filterText(val) {
      this.$refs.myTree.filter(val)
    }
  },
  methods: {
    // 节点被点击时的回调
    nodeClick(data, node, self) {
      this.$emit('nodeClick', { data, node, self })
    },
    // 当复选框被点击的时候触发
    check(data, checkObj) {
      this.$emit('check', { data, checkObj })
    },
    // 节点选中状态发生变化时的回调
    checkChange(data, isChecked, childIsCheked) {
      // 传递给 data 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
      this.$emit('checkChange', { data, isChecked, childIsCheked })
    },
    // 当前选中节点变化时触发的事件
    currentChange(data, node) {
      this.$emit('currentChange', { data, node })
    },
    // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
    filterNodeMethod(value, data, node) {
      if (!value) return true
      return data[this.defaultProps.label].indexOf(value) !== -1
    },
    // 节点被展开时触发的事件
    nodeExpand(data, node, el) {
      this.$emit('nodeExpand', { data, node, el })
    },
    // 节点被关闭时触发的事件
    nodeCollapse(data, node, el) {
      this.$emit('nodeCollapse', { data, node, el })
    },
    // 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
    getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
      return this.$refs.myTree.getCheckedNodes(leafOnly, includeHalfChecked)
    },
    // 	设置目前勾选的节点,使用此方法必须设置 node-key 属性
    setCheckedNodes(nodes) {
      this.$refs.myTree.setCheckedNodes(nodes)
    },
    // 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
    getCheckedKeys(leafOnly = false, includeHalfChecked = false) {
      return this.$refs.myTree.getCheckedKeys(leafOnly, includeHalfChecked)
    },
    // 通过 keys 设置目前勾选的节点,使用此方法必须设置 node-key 属性
    setCheckedKeys(keys, leafOnly = false) {
      this.$refs.myTree.setCheckedKeys(keys, leafOnly)
    },
    // 通过 key / data 设置某个节点的勾选状态,使用此方法必须设置 node-key 属性
    setChecked(keyordata, checked, deep = false) {
      this.$refs.myTree.setChecked(keyordata, checked, deep)
    },
    // 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
    getHalfCheckedNodes() {
      return this.$refs.myTree.getHalfCheckedNodes()
    },
    // 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
    getHalfCheckedKeys() {
      return this.$refs.myTree.getHalfCheckedKeys()
    },
    // 获取当前被选中节点的 key,使用此方法必须设置 node-key 属性,若没有节点被选中则返回 null
    getCurrentKey() {
      return this.$refs.myTree.getCurrentKey()
    },
    // 获取当前被选中节点的 data,若没有节点被选中则返回 null
    getCurrentNode() {
      return this.$refs.myTree.getCurrentNode()
    },
    // 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
    setCurrentKey(key) {
      this.$refs.myTree.setCurrentKey(key)
    },
    // 通过 node 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
    setCurrentNode(node) {
      this.$refs.myTree.setCurrentNode(node)
    },
    // 设置 node-key 属性	(node) 待被选节点的 node getNode	根据 data 或者 key 拿到 Tree 组件中的 node
    getNode(data) {
      return this.$refs.myTree.getNode(data)
    },
    // 删除 Tree 中的一个节点,使用此方法必须设置 node-key 属性
    remove(data) {
      this.$refs.myTree.remove(data)
    },
    // 为 Tree 中的一个节点追加一个子节点
    append(data, parentNode) {
      this.$refs.myTree.append(data, parentNode)
    },
    // 递归拉平数据
    recursion(arr) {
      return [].concat(...arr.map(item => {
        if (item[this.defaultProps['children']]) {
          let arr = [].concat(item, ...this.recursion(item[this.defaultProps['children']]))
          delete item[this.defaultProps['children']]
          return arr
        }
        return [].concat(item)
      }
      ))
    },
    // 获取拉平的treeData数据
    getFlatData() {
      let cloneData = JSON.parse(JSON.stringify(this.treeData))
      return this.recursion(cloneData)
    }
  }
}
</script>

<style lang="scss" scoped>
  .my-tree {
    .search-input {
      margin-bottom: 10px;
    }
  }
</style>

2.使用

<template>
  <div class="tree-box">
    <my-tree ref="myTree" :tree-data="treeData" icon-class="el-icon-star-on" :default-checked-keys="defaultCheckedKeys" :show-checkbox="true" :default-expanded-keys="defaultExpandedKeys" :highlight-current="true" :check-on-click-node="false" :default-props="defaultProps" @nodeClick="nodeClick" @checkChange="checkChange" @check="check"></my-tree>
    <div class="info-box">
      <p>当前选中:{{ chooseObj.name||'-' }}</p>
      <p>当前勾选:{{ curChecks||'-' }}</p>
    </div>
    <div class="btn-box">
      <el-button type="primary" @click="getCheckedNodes">根据node获取勾选项Id</el-button>
      <el-button type="primary" @click="setCheckedNodes">根据node设置勾选三级 3-2-2</el-button>
      <el-button type="primary" @click="getCheckedKeys">根据key获取勾选项Id</el-button>
      <el-button type="primary" @click="setCheckedKeys">根据key设置勾选三级 3-2-2</el-button>
      <el-button type="primary" @click="setChecked">勾选三级 3-1-1</el-button>
    </div>
  </div>
</template>

<script>
import MyTree from '../../components/MyTree/index.vue'
export default {
  components: { MyTree },
  data() {
    return {
      treeData: [{
        id: 1,
        name: '一级 2',
        child: [{
          id: 3,
          name: '二级 2-1',
          child: [
            {
              id: 4,
              name: '三级 3-1-1',
              child: [{
                id: 8,
                name: '四级 4-1-1'
              }, {
                id: 9,
                name: '四级 4-1-2',
                disabled: true
              }]
            },
            {
              id: 5,
              name: '三级 3-1-2',
              disabled: true,
              child: [{
                id: 10,
                name: '四级 4-1-3'
              }, {
                id: 11,
                name: '四级 4-1-4',
                disabled: true
              }]
            }]
        }, {
          id: 2,
          name: '二级 2-2',
          disabled: true,
          child: [{
            id: 6,
            name: '三级 3-2-1'
          }, {
            id: 7,
            name: '三级 3-2-2',
            disabled: true
          }]
        }]
      }],
      defaultExpandedKeys: [5],
      defaultCheckedKeys: [9],
      defaultProps: {
        label: 'name',
        children: 'child'
      },
      chooseObj: {},
      checkInfo: {
        checkObj: {
          checkedNodes: [{ name: '四级 4-1-2' }]
        }
      }
    }
  },
  computed: {
    curChecks() {
      if (this.checkInfo && this.checkInfo.checkObj) {
        return this.checkInfo.checkObj.checkedNodes.map(item => item.name).join('、')
      } else {
        return ''
      }
    }
  },
  methods: {
    // 节点被点击时获取选中项
    nodeClick(obj) {
      this.chooseObj = obj.data
    },
    // 节点选中状态发生变化时的回调
    checkChange(obj) {
    },
    // 当复选框被点击的时候触发
    check(obj) {
      this.checkInfo = obj
    },
    // 根据node获取当前勾选项
    getCheckedNodes() {
      let checkArr = this.$refs.myTree.getCheckedNodes(true, true)
      this.$message.success(`当前勾选id有:${checkArr.map(item => item.id).join('、')}`)
    },
    // 根据node设置目前勾选的节点
    setCheckedNodes() {
      this.$refs.myTree.setCheckedNodes([{
        id: 7,
        name: '三级 3-2-2',
        disabled: true
      }])
    },
    // 根据key获取当前勾选项
    getCheckedKeys() {
      let checkArr = this.$refs.myTree.getCheckedKeys(true, true)
      this.$message.success(`当前勾选id有:${checkArr.map(item => item).join('、')}`)
    },
    // 根据key设置目前勾选的节点
    setCheckedKeys() {
      this.$refs.myTree.setCheckedKeys([7])
    },
    // 勾选三级 3-1-1
    setChecked() {
      this.$refs.myTree.setChecked(4, !this.$refs.myTree.getNode(4).checked)
    }
  }
}
</script>

<style lang="scss" scoped>
  .tree-box {
    width: 60%;
    margin: 0 auto;
    ::v-deep .el-tree-node__expand-icon {
      font-size: 16px;
    }
  }
</style>

3.重置样式

如若要对不同的一级二级节点设置不同的样式可以参考这样:

/* 一级节点样式 */
  >.el-tree-node>.el-tree-node__content {
    height: 44px;
    font-size: 14px;
    color: #333;
    font-family: PingFang SC-Medium, PingFang SC;
    font-weight: 500;
    background-color: #F5F5F7;
    margin-bottom: 8px;
    padding-left: 12px !important;
  }

  /* 二级节点样式 */
  >.el-tree-node .el-tree-node .el-tree-node__content {
    height: 34px;
    font-size: 13px;
    color: #444;
    margin-bottom: 8px;
    padding-left: 36px !important;

    &:before {
      content: "";
      display: inline-block;
      width: 6px;
      height: 6px;
      background-color: #54B7AD;
      border-radius: 50%;
      margin-right: 8px;
    }
  }

  /* 三级节点样式 */
  >.el-tree-node .el-tree-node .el-tree-node .el-tree-node__content {
    height: 30px;
    font-size: 12px;
    color: #444;
    margin-bottom: 4px;
    padding-left: 50px !important;

    &:before {
      display: none;
    }
  }