use jest creat some ut from this component: <template>
  <div>
    <el-tree
      v-if="treeVisible"
      ref="tree"
      v-bind="treeProps"
      v-on="treeListeners"
    />
  </div>
</template>
<script>
const nonAxiosAttrs = [
  'beforeRequest',
  'interceptResponse',
  'response',
  'resField',
  'lazyKey'
]
export default {
  name: 'GdTree',
  data() {
    return {
      treeVisible: true,
      treeProps: {}, // 取树的属性用这个
      treeListeners: {},
      defaultProps: {
        data: [],
        showCheckbox: true,
        nodeKey: 'id',
        props: {
          label: 'label',
          children: 'children',
          isLeaf: 'leaf'
        }
      },
      defaultListener: {}
    }
  },
  watch: {},
  created() {
    // console.log(this)
    this._mergeTreeObjFn()
    this._setTreeCheckboxStatusFn()
    this._mergeTreeEventFn()
    // this._watchElTreeAttr()
    if (this.auto) {
      this._loadData()
    }

    // this.$gdMar.send('gd-tree-loaded')
  },
  mounted() {},
  methods: {
    // 重新load一下数据，比如重新请求一个接口
    loadData() {
      this.treeVisible = false
      this._loadData()
      this.$nextTick(() => {
        this.treeVisible = true
      })
    },
    getRef() {
      return this.$refs.tree
    },
    setData(data) {
      if (!utils.isArr(data)) {
        console.error('setData中的参数data必须为数组')
        return
      }
      // 对数据按照showLevel的设置进行处理
      if (this.showLevel.length > 0) {
        data = this._setDataByShowLevel(data)
      }
      this._traverseTreeToSetAttr(data)
      // 这个函数，目前只用于初始化的数据
      this.treeProps.data = data
    },
    _setDataByShowLevel(data) {
      let treeData = _cloneDeep(data)
      let level = 1
      let queue = []
      let result = []
      // 给每个树节点打上gd-tree-id
      treeData.map((level1, index) => {
        level1['gd-tree-id'] = index
        queue.push(level1)
      })
      let treeLevelData = {}
      let childrenField = this.treeProps.props.children
      let showLevel = this.showLevel.sort((a, b) => (a - b))
      // BFS
      while (queue.length > 0) {
        let length = queue.length
        treeLevelData[level] = []
        for (let i = 0; i < length; i++) {
          let queueTop = queue.shift()
          let children = queueTop[childrenField]
          if (children && children.length > 0) {
            children.map((item, index) => {
              // 需要显示的层级会加上-index，不需要显示的继承父亲的gd-tree-id，方便下面查找，提高性能
              if (showLevel.indexOf(+level + 1) > 0) {
                item['gd-tree-id'] = `${queueTop['gd-tree-id']}-${index}`
              } else {
                item['gd-tree-id'] = queueTop['gd-tree-id']
              }
              queue.push(item)
            })
          }
          queueTop.children = []
          // 清空childrenField
          treeLevelData[level].push(queueTop)
        }
        level++
      }
      // 对showLevel排序
      showLevel.map((level, index) => {
        let levelData = treeLevelData[level]
        if (!isNaN(level) && levelData) {
          if (index === 0) {
            result.push(...levelData)
          } else {
            levelData.map((item, index) => {
              // 根据gdTreeId直接找到要挂靠的父元素
              let gdTreeId = item['gd-tree-id'] + ''
              let levels = gdTreeId.split('-')
              levels.pop()
              let getStr
              levels.map((item, index) => {
                if (index === 0) {
                  getStr = [item]
                } else {
                  getStr.push(childrenField, item)
                }
              })
              let parantItem = _get(result, getStr)
              parantItem.children.push(item)
            })
          }
        }
      })
      return result
    },
    _traverseTreeToSetAttr(treeData) {
      let level = 1
      // BFS
      let queue = []
      for (let level1 of treeData) {
        queue.push(level1)
      }
      while (queue.length > 0) {
        let length = queue.length
        for (let i = 0; i < length; i++) {
          let queueTop = queue.shift()
          let children = queueTop[this.treeProps.props.children]
          if (this.disabledLevel[level]) {
            queueTop['disabled'] = true
          }
          if (children && children.length > 0) {
            children.map(item => {
              queue.push(item)
            })
          }
        }
        level++
      }
    },
    // 发起请求
    _requestAxios(axiosAttrs) {
      let axiosInstance = gb.axios
      let source = this.source
      return new Promise((resolve, reject) => {
        axiosInstance(axiosAttrs).then(axiosRes => {
          let data = source.resField ? _get(axiosRes, source.resField, []) : axiosRes
          let interceptResponse = source.interceptResponse
          let comData = []
          if (interceptResponse && utils.isFunc(interceptResponse)) {
            let isContinue = true
            let doneFn = (type = true) => {
              isContinue = type
            }
            let interceptResponseRes = interceptResponse(doneFn, data, axiosRes)
            interceptResponseRes = interceptResponseRes || data
            if (!isContinue) {
              console.warn('interceptResponse--拦截')
              return
            }
            if (utils.isArr(interceptResponseRes)) {
              comData = interceptResponseRes
            } else {
              console.error('非法，传入el-tree组件的data非array')
              comData = []
            }
          } else {
            if (!utils.isArr(data)) {
              console.error('非法，传入el-tree组件的data非array')
              comData = []
            } else {
              comData = data
            }
          }
          let response = source.response
          if (response && utils.isFunc(response)) {
            source.response(comData, axiosRes)
          }
          resolve(comData, axiosRes)
          this.$emit('loaded')
        }).catch((error) => {
          console.error(error)
        })
      })
    },
    /**
     * @param {object} otherParams - 会合并进请求体的参数
     * @return {Promise}
     */
    _getSourceData(otherParams) {
      let source = this.source
      if (source && utils.isObj(source) && source.url) {
        let axiosAttrs = utils.deepCopy(source)
        Object.keys(axiosAttrs).map(key => {
          if (nonAxiosAttrs.includes(key)) {
            delete axiosAttrs[key]
          }
        })
        if (axiosAttrs.method === 'get') {
          Object.assign(axiosAttrs.params, otherParams)
        } else {
          Object.assign(axiosAttrs.data, otherParams)
        }

        let beforeRequest = source.beforeRequest
        if (beforeRequest && utils.isFunc(beforeRequest)) {
          // 根据请求url获取数据
          let isContinue = true
          let doneFn = (type = true) => {
            isContinue = type
          }
          var beforeResult = beforeRequest(doneFn, axiosAttrs)
          let _axiosAttrs = beforeResult || axiosAttrs
          if (!isContinue) {
            console.warn('beforeRequest--请求拦截')
            return
          } else {
            if (!utils.isObj(_axiosAttrs)) {
              console.error('非法，beforeRequest返回的非axios参数对象')
              return
            }
            return this._requestAxios(_axiosAttrs)
          }
        } else {
          return this._requestAxios(axiosAttrs)
        }
      } else {
        this.$emit('loaded')
        // 从data中获取数据
        return new Promise((resolve, reject) => {
          resolve([])
        })
      }
    },
    _loadData() {
      if (this.source.url && this.treeProps.lazy !== undefined) {
        // 有设置source且设置lazy
        const LAZYKEY = 'id'
        let defaultSourceLazy = (node, resolve) => {
          // 设置了lazyOption.showMaxLevel
          let showMaxLevel = this.lazyOption.showMaxLevel > 0 ? this.lazyOption.showMaxLevel : 0
          if (showMaxLevel && node.level >= showMaxLevel) {
            return
          }
          // 首次不传
          var obj = {}
          if (node.level > 0) {
            obj[this.source.lazyKey || LAZYKEY] = node.data && node.data[this.treeProps.nodeKey]
          } else {
            obj[this.source.lazyKey || LAZYKEY] = ''
          }
          this._getSourceData(obj).then(data => {
            if (node.level === 0) {
              // 首次请求，请求回来的data连接外部传入的data
              this.setData(data)
            } else {
              // 展示最大层级时，就是最末层节点
              if (showMaxLevel && node.level === (showMaxLevel - 1)) {
                data.map(item => {
                  item[this.treeProps.props.isLeaf] = true
                })
              }
              // 根据disabledLevel的配置，看是否禁用节点
              if (this.disabledLevel[node.level + 1]) {
                data.map(item => {
                  item['disabled'] = true
                })
              }
              // 由于懒加载后追加节点，不会同步到this.treeProps.data，所以得特别留意
              resolve(data)
            }
          })
        }
        this.treeProps.load = this.treeProps.load || defaultSourceLazy
      } else if (this.source.url) {
        this._getSourceData().then(data => {
          // 有设置source，首次请求
          this.setData(data)
        })
      } else {
        this.setData(this.treeProps.data)
      }
    },
    _setTreeCheckboxStatusFn() {
      if (this.multiple) {
        return
      } else {
        this.treeProps['checkStrictly'] = true
        this.defaultListener['check'] = (item, checkStatus) => {
          this.$refs.tree.setCheckedKeys([])
          this.$refs.tree.setCheckedKeys([item[this.treeProps['nodeKey']]])
        }
      }
    },
    _camelCaseTransformFn(data) {
      if (utils.isObj(data)) {
        let obj = {}
        Object.keys(data).map((key) => {
          obj[camelCase(key)] = data[key]
        })
        return obj
      }
      if (utils.isStr(data)) {
        return camelCase(data)
      }
      return data
    },
    _mergeTreeObjFn() {
      this.treeProps = Object.assign({}, this.defaultProps, this._camelCaseTransformFn(this.$attrs))
    },
    _mergeTreeEventFn() {
      this.treeListeners = Object.assign({}, this.defaultListener, this.$listeners)
    },
    _watchElTreeAttr() {
      attributes.map(attr => {
        if (this.$attrs[attr]) {
          this.$watch(function() {
            return this.$attrs[attr]
          }, (newVal, oldVal) => {
            this.treeProps[camelCase(attr)] = newVal
          }, {
            deep: true
          })
        }
      })
    }
  }
}
</script>