<template>
  <a-card :class="[{ [$style.treewrap]: !hideLeftBtn }, { [$style.collapse]: fold }]">
    <a-input-search
      v-if="treeData.length > 0 && !hideSearch"
      placeholder="请输入"
      allow-clear
      @change="onSearchChange"
    />
    <a-spin :spinning="spinning" :class="$style.spin" />
    <a-empty v-if="empty" />
    <a-tree
      v-if="treeData.length > 0"
      show-icon
      :expanded-keys.sync="expandedKeys"
      :auto-expand-parent="autoExpandParent"
      :tree-data="treeData"
      :replace-fields="replacefields"
      :selected-keys.sync="selectedkey"
      :load-data="loadData"
      @select="onSelect"
      @expand="onExpand"
    >
      <template v-slot:title="{ text, props }">
        <span v-if="text.indexOf(searchValue) > -1">
          {{ text.substr(0, text.indexOf(searchValue))
          }}<span :class="$style.searchColor">{{ searchValue }}</span
          >{{ text.substr(text.indexOf(searchValue) + searchValue.length) }}</span
        ><span v-else>{{ text }}</span
        ><span v-if="props.count > 0 && optionCount !== undefined"
          >（{{ props[optionCount] }}）</span
        >
      </template>
    </a-tree>
    <a-button v-if="!hideLeftBtn" type="primary" :class="$style.fold" @click="foldClick">
      <a-icon :type="icontype" />
    </a-button>
  </a-card>
</template>

<script>
import { Message } from 'ant-design-vue'
import components from './_import-components/km-tree-async-import'
export default {
  name: 'KmTreeAsync',
  components,
  props: {
    loadTreeData: {
      type: Function,
      required: true,
    },
    hideLeftBtn: {
      type: Boolean,
      default: false,
    },
    hideSearch: {
      type: Boolean,
      default: false,
    },

    optionName: {
      type: String,
      default: 'text',
    },
    optionValue: {
      type: String,
      default: 'id',
    },
    optionCount: {
      type: String,
      default: undefined,
    },
  },
  data() {
    return {
      searchValue: '',
      dataList: [], // 数组dataList，搜索要用

      spinning: true,
      empty: false,

      treeData: [],
      expandedKeys: [],
      autoExpandParent: true,
      replacefields: {
        title: 'text',
        key: 'id',
      },

      selectedkey: [], // 选中的节点
      defaultselectedkey: undefined,

      icontype: 'left',
      fold: false,
    }
  },
  created() {
    this.loadDataForRoot()
  },
  methods: {
    transformData(data) {
      return data.map((d) => {
        const { children, checkable, selectable, leaf, ...rest } = d
        return {
          ...rest,
          checkable: checkable ?? false,
          selectable: selectable ?? true,
          isLeaf: leaf ?? true,
          key: d[this.optionValue],
          title: d[this.optionName],
          children: children && this.transformData(children),
          scopedSlots: { title: 'title' },
          originalValue: rest,
        }
      })
    },

    loadDataForRoot() {
      Promise.resolve(this.loadTreeData())
        .then((data) => {
          this.spinning = false
          this.treeData = this.transformData(data)
          // 生成搜索要用的数组
          this.generateList(this.treeData)
          if (this.defaultselectedkey) {
            this.selectedkey = this.defaultselectedkey
          } else {
            // 没传展开的keys，根节点只有一个的话，展开它
            if (this.treeData.length === 1) {
              this.expandedKeys = [this.treeData[0].key]
              this.selectedkey = [this.treeData[0].key]
              this.$emit('treeSelectd', this.treeData[0])
            }
          }
        })
        .catch(() => {
          this.spinning = false
          this.empty = true
          const data = {
            id: -1,
            text: '获取分类失败，请联系管理员',
          }

          this.$emit('treeSelectd', data)
          Message.error({ content: '获取分类失败，请联系管理员' })
        })
    },

    loadData(treeNode) {
      return new Promise((resolve) => {
        if (treeNode.dataRef.children) {
          // 有值了直接渲染
          resolve()
          return
        }
        Promise.resolve(this.loadTreeData(treeNode.dataRef.key)).then((data) => {
          treeNode.dataRef.children = this.transformData(data)
          this.treeData = [...this.treeData]
          // 生成搜索要用的数组
          this.generateList(this.treeData)
          resolve()
        })
      })
    },

    // 刷新树
    refresh() {
      this.defaultselectedkey = this.selectedkey
      this.treeData = []
      this.spinning = true
      this.loadDataForRoot()
    },

    // 搜索相关
    onExpand(expandedKeys) {
      this.expandedKeys = expandedKeys
      this.autoExpandParent = false
    },

    getParentKey(key, tree) {
      let parentKey
      for (let i = 0; i < tree.length; i++) {
        const node = tree[i]
        if (node.children) {
          if (node.children.some((item) => item.id === key)) {
            parentKey = node.id
          } else if (this.getParentKey(key, node.children)) {
            parentKey = this.getParentKey(key, node.children)
          }
        }
      }
      return parentKey
    },

    // 获取当前选中节点的根节点
    getRootKey(key) {
      let rootKey
      const strKey = key.toString()
      if (this.treeData.length === 1) {
        rootKey = this.treeData[0].id
      } else {
        for (let index = 0; index < this.treeData.length; index++) {
          var item = this.treeData[index]
          // 先判断选中的是不是根节点
          if (item.id === strKey) {
            rootKey = key
            break
          } else {
            if (this.getParentKey(strKey, [item])) {
              // 说明选中的是这个根节点下的分类
              rootKey = item.id
              break
            }
          }
        }
      }
      return parseInt(rootKey)
    },

    onSearchChange(e) {
      const value = e.target.value
      const expandedKeys = this.dataList
        .map((item) => {
          if (item.title.indexOf(value) > -1) {
            return this.getParentKey(item.key, this.treeData)
          }
          return null
        })
        .filter((item, i, self) => item && self.indexOf(item) === i)
      Object.assign(this, {
        expandedKeys,
        searchValue: value,
        autoExpandParent: true,
      })
    },

    // 处理搜索用的dataList
    generateList(data) {
      for (let i = 0; i < data.length; i++) {
        const node = data[i]
        const key = node.id
        const title = node.text
        const props = node.props
        this.dataList.push({ key, id: key, title: title, props })
        if (node.children) {
          this.generateList(node.children)
        }
      }
    },

    // 获取被选中的节点
    fnGetNodeItem(data, key) {
      return new Promise((resolve, reject) => {
        for (var i in data) {
          if (data[i].id === key) {
            resolve(data[i])
            break
          } else {
            if (data[i].children) {
              this.fnGetNodeItem(data[i].children, key).then((data) => {
                resolve(data)
              })
            }
          }
        }
      })
    },

    // 树选中调用
    onSelect(selectedKeys, info) {
      this.selectedkey = selectedKeys
      this.$emit('treeSelectd', info)
    },

    // 展示/隐藏树的小箭头点击事件
    foldClick() {
      this.fold = !this.fold
      if (this.fold) {
        this.icontype = 'right'
      } else {
        this.icontype = 'left'
      }
    },
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
.spin {
  width: 100%;
  line-height: 30;
}
.treewrap {
  :global(.ant-input-search) {
    margin: 8px 0;
    overflow: hidden;
  }

  position: relative;
  display: flex;
  flex-direction: column;
  width: 20%;
  min-height: 100%;
  margin-right: $padding-lg;
  transition: width 0.2s;
  .fold {
    position: absolute;
    top: calc(50% - 30px);
    right: -15px;
    z-index: 2;
    width: 15px;
    height: 75px;
    padding: 0;
    border-radius: 0 10px 10px 0;
  }
  :global(.ant-tree) {
    overflow-x: auto;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  :global(.ant-card-body) {
    background: $white;
  }
}

.collapse {
  width: 0;
  :global(.ant-card-body) {
    background: transparent;
    :global(.ant-empty) {
      display: none;
    }
  }
}
.search-color {
  color: $primary-color;
}
</style>
