<template>
  <div id="XTreeWarp">
    <div class="tree_title" v-if="config.title">
      <a-row style="font-size: medium;line-height: 3rem">
        <a-col span="16" :offset="2">
          <span>{{ config.title }}</span>
        </a-col>
        <a-col span="1" :offset="2" v-if="treeDisplaySwitch">
          <a-icon
            :type="treeDisplayConfig.showTree? 'caret-down':'caret-left'"
            :style="{color: treeDisplayConfig.buttonColor}"
            @click="treeDisplayConfig.showTree=!treeDisplayConfig.showTree"/>
        </a-col>
      </a-row>
    </div>
    <div v-show="!treeDisplaySwitch || treeDisplayConfig.showTree">
      <a-row v-if="config.search" class="row-search">
        <div class="content-search">
          <a-input
            placeholder="请输入搜索关键字"
            v-model="searchInput"
            @change="onChange">
            <a-icon slot="prefix" type="search"/>
          </a-input>
          <span v-if="config.addParent?.isShow" style="margin-left: 10px;">
            <a-icon
              :type="config.addParent?.icon || 'plus-square'"
              :style="{ fontSize: config.addParent?.size || '30px', color: config.addParent?.color }"
              @click="addParent(config.addParent?.func)"
            />
          </span>
        </div>
      </a-row>
      <a-row v-if="config.tabBtn && config.tabBtn.length > 0">
        <div class="content-search" style="padding-top:7px;padding-left: 10px">
          <a-space>
            <a-button
              v-for="(item,index) in config.tabBtn"
              @click="btnFuncClick(index,item.customFunction)"
              :class="{ 'active-button': activeButtonIndex[index] }"
              :key="index">{{ item.name }}
            </a-button>
          </a-space>
        </div>
      </a-row>
      <div
        class="tree_main"
        :style="{
          maxHeight:heightStore[1]
        }">
        <a-spin :spinning="spinning">
          <div class="ve-tree" :style="{height:heightStore[1]}">
            <!-- 不使用虚拟滚动时只需去掉height参数即可 -->
            <vue-easy-tree
              ref="veTree"
              node-key="value"
              :height="heightStore[1]"
              :show-checkbox="config.checkable"
              :data="localTreeData"
              :item-size="40"
              :auto-expand-parent="true"
              @check-change="handleCheckChange"
              :default-expanded-keys="expandedKeys"
              @node-expand="nodeExpand"
              @node-collapse="nodeCollapse"
              @node-click="handleNodeClick"
              :filter-node-method="filterNode"
              :props="props"
            >
              <span class="custom-tree-node" slot-scope="{ node,data }">
                <span :class="{ 'selected': data === selectedNode }" @click="nodeItemClick(data)">
                  <a-tooltip placement="right" :title="node.label">
                    <a-icon
                      v-if="data.icon"
                      :style="{color: data.iconColor, marginRight: '5px'}"
                      :type="data.icon"/>
                    <span
                      v-html="highlightKeyword(node.label, searchInput)"></span>
                    <span v-if="countVisible">{{
                      data.hasOwnProperty('count') ? ' (' + data.count + ')' : ''
                    }}</span>
                  </a-tooltip>
                </span>
                <a-tooltip
                  :title="icon.title"
                  :key="icon.func"
                  v-for="icon in config.btn"
                >
                  <a-icon
                    @click.stop="action(data, icon.func)"
                    v-if="executeStrFunction( icon.customFunction,[data])"
                    :type="icon.icon"
                    :style="{color:icon.color, marginLeft: '8px'}"></a-icon>
                </a-tooltip>
              </span>
            </vue-easy-tree>
          </div>
        </a-spin>
      </div>
    </div>
  </div>
</template>

<script>
// https://github.com/Junior125306/vue-easy-tree
import VueEasyTree from '@afwenming123/vue-easy-tree'
// 样式文件，可以根据需要自定义样式或主题
import '@afwenming123/vue-easy-tree/src/assets/index.min.css'
import { getConfigByNameAsync, runLogic } from '@vue2-client/services/api/common'
import { mapState } from 'vuex'
import { executeStrFunction } from '@vue2-client/utils/runEvalFunction'
import debounce from 'lodash.debounce'

export default {
  props: {
    configName: {
      type: String,
      required: false,
      default: undefined
    },
    configValue: {
      type: String,
      required: false,
      default: undefined
    },
    env: {
      type: String,
      default: 'prod'
    },
    treeData: {
      type: Array,
      default: () => []
    },
    // 配置所属命名空间
    serviceName: {
      type: String,
      default: undefined
    },
    // 是否显示收起开关
    treeDisplaySwitch: {
      type: Boolean,
      default: false
    },
    // 是否显示子级数量
    countVisible: {
      type: Boolean,
      default: true
    },
    // 收起开关默认值设置
    treeDisplayConfig: {
      type: Object,
      default: () => {
        return {
          showTree: true,
          buttonColor: '#91d5ff'
        }
      }
    }
  },
  data () {
    return {
      props: {
        label: 'label',
        children: 'children'
      },
      localTreeData: this.treeData,
      config: {},
      // 暂存选中的数据
      expandedKeys: [],
      // 是否加载中
      spinning: false,
      // 选中的 key
      checkedKeys: [],
      // 定义 tree 数据
      data_treeData: [],
      // 存储搜索结果的树数据
      filteredTreeData: [],
      // 定义最深一层的 数据
      deepestNodes: [],
      // 定义激活的按钮
      activeButtonIndex: {},
      // 是否全选
      checkall: false,
      // 选了但没有全选
      indeterminate: false,
      // 选择的节点
      selectedNode: null,
      // 暂存查询信息
      searchInfo: {},
      searchInput: '',
      replaceFields: { children: 'children', title: 'label', key: 'value' }
    }
  },
  components: {
    VueEasyTree
  },
  watch: {
    treeData (newVal) {
      this.localTreeData = newVal
    }
  },
  async created () {
    if (this.configName) {
      this.config = await getConfigByNameAsync(this.configName, this.serviceName, this.env === 'dev')
    } else if (this.configValue) {
      this.config = this.configValue
    } else {
      throw new DOMException('组件[XTreePro]需要传入configName或configValue')
    }
    this.replaceFields = this.config.replaceFields
    if (this.config.treeData.indexOf('logic@') >= 0) {
      // 如果数据源是 logic
      await this.getData()
    }
  },
  methods: {
    // 节点展开暂存
    nodeExpand (node) {
      console.log('====需要展开节点', node, this.replaceFields.key)
      if (this.expandedKeys.includes(node[this.replaceFields.key])) {
        this.expandedKeys = this.expandedKeys.filter(item => item !== node[this.replaceFields.key])
      } else {
        this.expandedKeys.push(node[this.replaceFields.key])
      }
    },
    // 节点关闭
    nodeCollapse (node) {
      this.expandedKeys = this.expandedKeys.filter(item => item !== node[this.replaceFields.key])
    },
    highlightKeyword (label, keyword) {
      const maxLen = this.config.deepNodeMaxWidth

      if (!keyword) {
        return maxLen && label.length > maxLen ? label.substring(0, maxLen) + '...' : label
      }

      const regex = new RegExp(`(${keyword})`, 'gi')
      const match = label.match(regex)

      if (!match) {
        return maxLen && label.length > maxLen ? label.substring(0, maxLen) + '...' : label
      }

      if (!maxLen) {
        return label.replace(regex, '<span class="highlight">$1</span>')
      }

      const keywordIndex = label.indexOf(match[0])
      const start = Math.max(0, keywordIndex - Math.floor((maxLen - keyword.length) / 2))
      const end = Math.min(label.length, start + maxLen)

      let result = label.substring(start, end)

      if (start > 0) {
        result = '...' + result
      }
      if (end < label.length) {
        result = result + '...'
      }
      return result.replace(regex, '<span class="highlight">$1</span>')
    },
    executeStrFunction,
    onChange: debounce(function (e) {
      this.$refs.veTree.filter(e.target.value)
    }, 150),
    filterNode (value, data, node) {
      if (!value) return true
      if (data.label.indexOf(value) !== -1) {
        this.nodeExpand(node.data)
        return true
      }
      return false
    },
    nodeItemClick (e) {
      this.selectedNode = e
    },
    handleNodeClick (node) {
      this.nodeItemClick(node)
      this.$emit('itemChecked', node)
    },
    addParent (func) {
      if (func) {
        this.$emit('action', null, null, null, func, null)
      }
    },
    action (record, func) {
      this.$emit('action', record, record[this.replaceFields.key], null, func)
    },
    async btnFuncClick (index) {
      this.activeButtonIndex = Object.assign({}, this.activeButtonIndex, { [index]: !!!this.activeButtonIndex[index] })
      // 组织参数
      // 把activeButtonIndex.value groupType 相同的其他值改成 false
      Object.keys(this.activeButtonIndex).forEach((key) => {
        if (key != index && this.config.tabBtn[key].groupType === this.config.tabBtn[index].groupType) {
          this.activeButtonIndex[key] = false
        }
      })
      const param = {}
      // 循环执行已经激活的自定义方法
      Object.keys(this.activeButtonIndex).forEach((key) => {
        if (this.activeButtonIndex[key]) {
          const item = this.config.tabBtn[key]
          if (item.customFunction) {
            Object.assign(param, executeStrFunction(item.customFunction, [param]))
          }
        }
      })
      // 获取到 param 执行 getData
      await this.getData(param)
    },
    searchInTree (tree, keyword) {
      return tree.reduce((acc, node) => {
        if (node[this.replaceFields.title].includes(keyword)) {
          acc.push(node)
        } else if (node[this.replaceFields.children]) {
          const childResults = this.searchInTree(node[this.replaceFields.children], keyword)
          if (childResults.length > 0) {
            acc.push({
              ...node,
              children: childResults
            })
          }
        }
        return acc
      }, [])
    },
    handleCheckChange (data, data1) {
      // console.log(data, data1)
      // 获取最内层数据的 node
      // const deepestNodes = this.getDeepestNodes([data])
      // 获取选中的数据
      // const checkedKeys = deepestNodes.map(node => node[this.replaceFields.key])
      // console.log(this.$refs.veTree.getCheckedKeys())
      const checkedKeys = this.$refs.veTree.getCheckedKeys()
      const checkedNodes = this.$refs.veTree.getCheckedNodes()
      // console.log(t)
      // console.log(deepestNodes, checkedKeys)
      this.$emit('treeOnChecked', checkedKeys, checkedNodes, checkedKeys)
    },
    refresh () {
      this.getData(this.searchInfo)
    },
    reLoad () {
      this.getData(this.searchInfo)
    },
    async getData (param = {}) {
      this.spinning = true
      await runLogic(this.config.treeData.substring(6), Object.assign(param, {
        selectedId: this.$route.query.selected,
        orgId: this.currUser.orgid,
        userId: this.currUser.id,
        depId: this.currUser.f_department_id
      }), this.serviceName, this.env === 'dev').then(res => {
        this.localTreeData = res
        this.deepestNodes = this.getDeepestNodes(this.localTreeData)
        this.$nextTick(() => {
          if (this.searchInput) {
            this.$refs.veTree.filter(this.searchInput)
          }
        })
      }).catch(e => {
        this.$message.error('获取数据失败：' + e)
      }).finally(() => {
        this.spinning = false
      })
    },
    getDeepestNodes (treeData) {
      const deepestNodes = []
      const that = this

      function traverse (node) {
        if (!node[that.replaceFields.children] || node[that.replaceFields.children].length === 0) {
          deepestNodes.push(node)
        } else {
          node[that.replaceFields.children].forEach(child => traverse(child))
        }
      }

      treeData.forEach(node => traverse(node))
      return deepestNodes
    }
  },
  computed: {
    ...mapState('account', { currUser: 'user' }),
    heightStore () {
      if (this.config.search) {
        if (this.config.tabBtn && this.config.tabBtn.length > 0) {
          return ['7rem', 'calc(100vh - 200px - 7.6rem)']
        } else {
          return ['5rem', 'calc(100vh - 200px - 5.6rem)']
        }
      } else {
        if (this.config.tabBtn && this.config.tabBtn.length > 0) {
          return ['5rem', 'calc(100vh - 200px - 5rem)']
        } else {
          return ['3rem', 'calc(100vh - 200px - 3rem)']
        }
      }
    }
  }
}
</script>
<style lang="less" scoped>
.tree_title {
  background-color: #E0E5EB;
  border-radius: 8px 8px 0 0;
}

.selected {
  font-weight: bold; /* 加粗字体 */
  color: #1890ff;
}

.row-search {
  background-color: #E0E5EB;

  .content-search {
    background-color: rgb(224, 229, 235);
    height: 2.6rem;
    padding: 0 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-top: 1px solid rgba(0, 0, 0, 0.05);
    margin: auto 0;
  }
}

.active-button {
  border-color: @primary-color !important;
  color: @primary-color !important;
}

.ant-btn:focus {
  color: rgba(0, 0, 0, 0.65);
  background-color: #fff;
  border-color: #d9d9d9
}

.tree_main {
  width: 100%;
  display: inline-block;
  overflow-y: auto;
  font-size: 15px;

  .custom-tree-node {
    flex: 1;
    cursor: pointer;
  }

  :deep(.highlight) {
    color: @primary-color !important;
  }

  :deep(.el-tree-node__content) {
    cursor: default;
  }

  :deep(.vue-recycle-scroller__item-wrapper) {
    overflow-x: auto;
  }
}

</style>
