<template>
  <div
    :class="[
      $style.wrapper,
      {
        [$style.single]: single,
      },
    ]"
  >
    <a-spin ref="scroll" :spinning="!dataLoaded" :class="$style.spin">
      <a-dropdown
        v-if="dataLoaded"
        v-model="showMenu"
        :get-popup-container="() => this.$refs.scroll.$el"
        :trigger="['contextmenu']"
        :disabled="single"
        @visibleChange="menuVisibleChange"
      >
        <div>
          <a-tree
            v-if="treeData"
            :load-data="onLoadData"
            :tree-data="treeDataComputed"
            checkable
            check-strictly
            :checked-keys="[...selectedKeys, ...targetKeys]"
            :default-expand-parent="true"
            :default-expanded-keys="expandedKeys"
            :selected-keys="[selectedNode && selectedNode.eventKey]"
            @click="treeClick"
            @click.native="treeClickNative"
            @dblclick="treeDblClick"
            @rightClick="treeRightClick"
            @contextmenu.native.capture="treeRightClickNative"
            @check="
              (_, props) => {
                onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect)
              }
            "
            @select="onSelect"
          />
        </div>
        <a-menu slot="overlay" @click="menuClick">
          <a-menu-item key="1" :disabled="!selectedNodeHasSelectableChild">
            <a-icon type="check" />选中下一级节点
          </a-menu-item>
          <a-menu-item key="2" :disabled="!selectedNodeHasSelectableChild">
            <a-icon type="close" />取消下一级节点
          </a-menu-item>
        </a-menu>
      </a-dropdown>
      <!-- 空span撑开加载条 -->
      <span></span>
    </a-spin>
    <a-empty v-if="dataLoaded && treeData.length === 0" :image="simpleImage" />
  </div>
</template>

<script>
import debounce from 'lodash.debounce'
import cloneDeep from 'lodash.clonedeep'
import auditPermissionTreeService from '../audit-permission-tree-service'
import components from './_import-components/audit-picker-source-tree-import'
import pickerSourceMixin from './picker-source-mixin'
import AuditPickerLabel from './audit-picker-label'

function addDisableProp(data, targetKeys = []) {
  data.forEach((item) => {
    item.disabled = targetKeys.includes(item.key)
    if (item.children) {
      addDisableProp(item.children, targetKeys)
    }
  })
  return data
}

function isChecked(selectedKeys, eventKey) {
  return selectedKeys.indexOf(eventKey) !== -1
}

/**
 * SdValuePicker的数据源：树结构
 * @displayName SdPickerSourceTree 数据源-树结构
 */
export default {
  name: 'AuditPickerSourceTree',
  components,
  mixins: [pickerSourceMixin],
  props: {
    /**
     * 根节点{code:'200000',name:'西安分公司'}
     */
    rootNode: {
      type: [Object, Array],
      default: undefined,
    },
    /**
     * <p>获取数据的回调，获取根节点时，parentId为undefined</p>
     * <pre>(parentId,parent) => childrenItems</pre>
     */
    loadTreeData: {
      type: Function,
      required: true,
    },
    /**
     * <p>搜索数据的回调</p>
     * <pre>(searchText) => items</pre>
     * @since 8.0.2
     */
    searchTreeData: {
      type: Function,
      default: undefined,
    },
    /**
     * 默认展开指定的树节点
     * @since 8.0.7
     */
    defaultExpandedKeys: {
      type: Array,
      default: undefined,
    },
    moduleId: {
      type: String,
      default: undefined,
    },
    // 特殊树不需要获取节点
    selectchecked: {
      type: Boolean,
      default: false,
    },
  },

  data() {
    return {
      treeData: null,
      searchValueDebounce: '',
      searchedData: [],
      expandedKeys: [],
      showMenu: false,
      selectedNode: undefined,
      dataList: [], // 数组dataList，搜索要用
    }
  },
  computed: {
    listOrTreeData() {
      return this.searchValueDebounce ? this.searchedData : this.treeData
    },
    treeDataComputed() {
      // targetKeys和disabledKeys中的都变成禁用状态
      return addDisableProp(this.listOrTreeData, this.targetKeys.concat(this.disabledKeys))
    },
    selectedNodeHasSelectableChild() {
      return this.selectedNode?.dataRef.children?.some((item) => item.checkable && !item.disabled)
    },
  },
  watch: {
    listOrTreeData(data) {
      // 把treeData中所有selectable的节点，转成一个列表，给父组件作为dataSource
      const result = []
      function flatten(list, that) {
        list = list ?? []
        list.forEach((item) => {
          if (item.checkable) {
            result.push(item.originalValue)
          }

          flatten(item.children, that)
        })
      }
      flatten(data, this)
      this.updateDataSource(result)
    },
    searchValue(text) {
      this.doSearch(text)
    },
  },
  created() {
    // this.initTreeData()
    // 传了searchTreeData函数，就显示搜索框
    // this.updateShowSearch(!!this.searchTreeData)
    this.loadDataForRoot()
    // // 用户输入搜索条件时，延迟500ms
    this.doSearch = debounce(this.doSearch, 500, {
      leading: false,
      trailing: true,
    })
  },
  methods: {
    transformData(data) {
      return data.map((d) => {
        const { children, checkable, selectable, ...rest } = d
        let isLeaf
        if (d.isleaf !== undefined) {
          isLeaf = d.isleaf
        }
        if (d.isLeaf !== undefined) {
          isLeaf = d.isLeaf
        }
        return {
          ...rest,
          checkable: checkable ?? true,
          selectable: selectable ?? false,
          isLeaf: isLeaf ?? true,
          key: d[this.optionValue],
          title: <AuditPickerLabel vnodes={this.render(d, 'left')} />,
          children: children && this.transformData(children),
          originalValue: rest,
          code: rest.code,
        }
      })
    },
    loadDataForRoot() {
      Promise.resolve(this.loadTreeData())
        .then((data) => {
          data[0].isLeat = false
          this.treeData = this.transformData(data)
          if (this.defaultExpandedKeys) {
            this.expandedKeys = this.defaultExpandedKeys
          } else {
            // 没传展开的keys，根节点只有一个的话，展开它
            if (this.treeData.length === 1) {
              this.expandedKeys = [this.treeData[0].key]
            }
          }
        })
        .finally(() => (this.dataLoaded = true))
    },
    loadData(treeNode) {
      return new Promise((resolve) => {
        if (treeNode.dataRef.children) {
          // 有值了直接渲染
          resolve()
          return
        }
        Promise.resolve(
          this.loadTreeData(treeNode.dataRef.oldId, cloneDeep(treeNode.dataRef.originalValue))
        ).then((data) => {
          treeNode.dataRef.children = this.transformData(data)
          if (this.searchValueDebounce) {
            this.searchedData = [...this.searchedData]
          } else {
            this.treeData = [...this.treeData]
          }

          resolve()
        })
      })
    },
    doSearch(text) {
      this.searchValueDebounce = text
      if (!text) return
      this.dataLoaded = false
      this.searchedData = []
      Promise.resolve(this.searchTreeData(text))
        .then((data) => {
          // 返回一个列表，转换成树的格式
          this.searchedData = this.transformData(data)
        })
        .finally(() => (this.dataLoaded = true))
    },
    onChecked(_, e, checkedKeys, itemSelect) {
      const { eventKey } = e.node
      if (this.single) {
        // 单选模式，取消已经选中的，再选中当前的
        if (!isChecked(checkedKeys, eventKey)) {
          itemSelect(checkedKeys[0], false)
          itemSelect(eventKey, true)
        }
      } else {
        itemSelect(eventKey, !isChecked(checkedKeys, eventKey))
      }
    },
    treeClick(evt, treeNode) {
      const nodeData = treeNode.dataRef
      if (nodeData.disabled) return
      // 非叶子节点 + 不可选中，展开节点
      if (!nodeData.isLeaf && !nodeData.checkable && !nodeData.selectable)
        treeNode.vcTree.onNodeExpand(evt, treeNode)
    },
    treeDblClick(evt, treeNode) {
      const nodeData = treeNode.dataRef
      if (nodeData.disabled) return
      if (nodeData.checkable) this.itemDblClick(evt, nodeData.key)
    },
    treeClickNative() {
      // 点击树的其他区域，隐藏菜单
      if (this.single) return
      this.hideContextMemu()
    },
    treeRightClick({ node }) {
      // 记录当前点击的节点
      if (this.single) return
      this.selectedNode = node
    },
    hideContextMemu() {
      this.showMenu = false
      this.menuVisibleChange(false)
    },
    menuVisibleChange(visible) {
      if (!visible) this.selectedNode = undefined
    },
    treeRightClickNative(evt) {
      // 右键如果没有点击到树节点，不触发右键菜单
      if (this.single) return
      if (evt.target.nodeName !== 'SPAN') evt.stopPropagation()
    },
    menuClick(menu) {
      this.selectedNode.dataRef.children?.forEach((item) => {
        if (item.checkable && !item.disabled) this.itemSelect(item.key, menu.key === '1')
      })
      this.hideContextMemu()
    },
    onSelect(selectedKeys, evt) {
      /**
       * 树节点的选中事件
       * @property {Object} treeItem 选中的节点
       * @property {Object} evt 其他信息{selected: bool, selectedNodes, node, event}
       */
      this.$emit('treeItemSelect', { ...evt.node.dataRef.originalValue }, evt)
    },
    // 点击地址树展开时调用
    onLoadData(treeNode) {
      const params = {
        moduleId: this.moduleId,
      }
      return auditPermissionTreeService
        .getCategoryTree(treeNode.dataRef.oldId, params)
        .then((res) => {
          res.data.forEach((item) => {
            item.name = item.text
            item.oldId = item.id
            if (item.props.ORG_ID !== null) {
              item.code = item.props.ORG_ID.toString()
              item.id = item.props.ORG_ID
            } else {
              item.code = item.id.toString()
            }
            item.type = 'Group'
            item.checkable = true
          })
          treeNode.dataRef.children = this.transformData(res.data)
          this.treeData = [...this.treeData]
        })
    },
    // 初始化地址树
    initTreeData(depId) {
      const params = {
        moduleId: this.moduleId,
      }
      let topDepId = null
      auditPermissionTreeService.getParentOrgInfo(params).then((res) => {
        let id = res.data[0].id
        let text = res.data[0].text
        let code = this.defaultTopNodeId
        let oldId = res.data[0].id
        if (res.data[0].props.ORG_ID !== null) {
          code = res.data[0].props.ORG_ID
          id = res.data[0].props.ORG_ID
        }
        const obj = this.rootNode
        topDepId = !depId ? id : depId
        if (obj !== undefined) {
          text = this.rootNode.name
          code = this.rootNode.code
          id = this.rootNode.code
          topDepId = this.rootNode.id
          oldId = this.rootNode.id
        }
        this.defaultTopNodeId = oldId
        this.defaultTopNodeText = text
        this.defaultTreeExpandedKeys = [oldId]
        this.defaultSelectedKeys = [oldId]
        // this.selectedKeys = [id]
        this.expandedKeys = [oldId]
        auditPermissionTreeService.getCategoryTree(topDepId, params).then((res1) => {
          this.spinning = false
          res1.data.forEach((item) => {
            item.name = item.text
            item.oldId = item.id
            if (item.props.ORG_ID !== null) {
              item.code = item.props.ORG_ID
              item.id = item.props.ORG_ID
            } else {
              item.code = item.id
            }
            item.type = 'Group'
          })
          if (res1.data.length) {
            const treeNode = [
              {
                id: id,
                oldId: oldId,
                text: this.defaultTopNodeText,
                name: this.defaultTopNodeText,
                title: this.defaultTopNodeText,
                code: id,
                leaf: false,
                isleaf: false,
                props: {},
                children: res1.data,
                differentDisplay: false,
                expandable: true,
                type: 'Group',
              },
            ]
            this.treeData = this.transformData(treeNode)
            this.dataLoaded = true
          } else {
            const treeNode = [
              {
                id: id,
                oldId: oldId,
                text: this.defaultTopNodeText,
                name: this.defaultTopNodeText,
                code: id,
                leaf: false,
                props: res.data[0].props,
                children: [],
                key: id,
                type: 'Group',
              },
            ]
            this.treeData = this.transformData(treeNode)
            this.expandedKeys = this.defaultTreeExpandedKeys
            this.generateList(this.treeData)
            this.empty = false
            this.dataLoaded = 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)
        }
      }
    },
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
@import './source.scss';

.wrapper {
  :global .ant-tree-title {
    user-select: none;
  }
}
.single {
  :global .ant-tree-checkbox {
    .ant-tree-checkbox-inner {
      border-radius: 100px;
      &::after {
        position: absolute;
        top: 3px;
        left: 3px;
        display: table;
        width: 8px;
        height: 8px;
        content: ' ';
        background-color: $primary-color;
        border: 0;
        border-radius: 8px;
        opacity: 0;
        transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
        transform: scale(0);
      }
    }
    &.ant-tree-checkbox-checked {
      &::after {
        position: absolute;
        top: 16.67%;
        left: 0;
        width: 100%;
        height: 66.67%;
        content: '';
        border: 1px solid #1890ff;
        border-radius: 50%;
        animation: antRadioEffect 0.36s ease-in-out;
        animation-fill-mode: both;
      }
      .ant-tree-checkbox-inner {
        background-color: $white;
        &::after {
          opacity: 1;
          transition: all 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
          transform: scale(1);
        }
      }
    }
  }
}
</style>

<docs>
需要作为 [SdValuePicker](#sdvaluepicker) 的子节点使用，样例请参考 [SdValuePicker](#sdvaluepicker)
</docs>
