<template>
  <a-card :class="[$style.treewrap, $style.tree]">
    <div :class="$style.header">
      <a-input-search v-model="searchStr" placeholder="" allow-clear @change="onSearchChange" />
      <a-button
        v-if="createCategory"
        type="link"
        icon="plus"
        size="large"
        title="新建"
        @click="create(null)"
      />
    </div>
    <a-spin :spinning="spinning" :class="$style.spin" />
    <a-tree
      v-if="!spinning && treeData.length > 0"
      block-node
      :tree-data="treeData"
      :replace-fields="replaceFields"
      :selected-keys="selectedKeys"
      :expanded-keys.sync="expandedKeys"
      :load-data="onLoadData"
      @select="treeSelect"
    >
      <template v-slot:title="{ id, text, permission }">
        <div :class="$style.node">
          <div :title="text" :class="$style.text">
            <span v-if="text.indexOf(searchValue) > -1">
              {{ text.substr(0, text.indexOf(searchValue))
              }}<span :class="$style.active">{{ searchValue }}</span
              >{{ text.substr(text.indexOf(searchValue) + searchValue.length) }}</span
            ><span v-else>{{ text }}</span>
          </div>
          <div v-if="permission" :class="$style.action">
            <a-icon v-if="permission.update" type="edit" title="编辑" @click.stop="edit(id)" />
            <a-icon
              v-if="permission.delete"
              type="delete"
              title="删除"
              :loading="deleteLoading"
              @click.stop="deleteCategory(id)"
            />
          </div>
        </div>
      </template>
    </a-tree>
    <a-empty v-else-if="!spinning" />
    <!-- 分类维护 -->
    <sd-detail-modal
      v-if="showModal"
      ref="category"
      :record-id="categoryId"
      :page-id="pageId"
      :modal-props="{ width: 600 }"
      @saved="categorySaved"
    >
      <slot slot-scope="scope" v-bind="scope" />
    </sd-detail-modal>
  </a-card>
</template>

<script>
import { Modal } from 'ant-design-vue'
import PageService from '@/common/services/page-service'
import components from './_import-components/sd-category-tree-import'

export default {
  name: 'SdCategoryTree',
  components,
  props: {
    // 加载树数据
    loadTreeData: {
      type: Function,
      default: undefined,
    },
    formId: {
      type: String,
      default: '',
    },
    pageId: {
      type: String,
      default: '',
    },
    // 新建/编辑分类弹出Modal前
    beforeModalShow: {
      type: Function,
      default: undefined,
    },
    // 删除分类时调用（不使用默认的分类删除）
    deleteCategoryFunction: {
      type: Function,
      default: undefined,
    },
  },
  data() {
    return {
      treeData: [],
      replaceFields: {
        title: 'text',
        key: 'id',
      },
      selectedKeys: [],
      expandedKeys: [],
      spinning: true,
      searchStr: '',
      searchValue: '',
      categoryId: null,
      current: null,
      root: {},
      deleteLoading: false,
    }
  },
  computed: {
    dataList() {
      return this.generateList(this.treeData)
    },
    createCategory() {
      return !!this.current?.permission?.create
    },
    showModal() {
      return this.formId && this.pageId
    },
  },
  created() {
    this.initTreeData(true)
  },
  methods: {
    initTreeData(initd) {
      this.spinning = true
      this.loadTreeData?.()
        .then((treeData) => {
          let p
          if (this.expandedKeys.length > 1) {
            // 指定了展开的路径，则依次获取每一层
            const keys = [...this.expandedKeys]
            keys.reverse() // 数组翻转，从叶子节点往上获取
            const pArray = keys.map((key) => this.loadTreeData(key))
            p = Promise.all(pArray).then((items) => {
              return items.reduce((a, b, i) => {
                if (a.length > 0) {
                  const node = b.find((item) => item.id === keys[i - 1])
                  if (node) {
                    node.children = a
                  }
                }
                return b
              })
            })
          } else {
            p = Promise.resolve(null)
          }
          p.then((child) => {
            if (child) {
              const pNode = treeData.find((item) => item.id === this.expandedKeys[0])
              if (pNode) {
                pNode.children = child
              }
            }
            this.treeData = this.transformData(treeData)
            // 初始加载时默认选中根节点，默认展开根节点
            if (initd) {
              this.root = { ...this.treeData[0] }
              this.expandedKeys = [this.root.id]
              this.selectedKeys = [this.root.id]
              this.current = { ...this.root }
              this.$emit('treeSelect', { ...this.current })
            }
          }).finally(() => {
            this.spinning = false
          })
        })
        .catch((e) => {
          this.spinning = false
          throw e
        })
    },
    transformData(data) {
      return data.map((d) => {
        const { children, leaf, ...rest } = d
        return {
          ...rest,
          isLeaf: leaf,
          children: children && this.transformData(children),
          scopedSlots: { title: 'title' },
        }
      })
    },
    // 处理搜索用的dataList
    generateList(data) {
      let dataList = []
      for (let i = 0; i < data.length; i++) {
        const node = data[i]
        const key = node.id
        const title = node.text
        const props = node.props
        dataList.push({ key, id: key, title: title, props })
        if (node.children) {
          dataList = dataList.concat(this.generateList(node.children))
        }
      }
      return dataList
    },
    // 点击树
    treeSelect(selectedKeys, info) {
      if (selectedKeys?.[0]) {
        this.selectedKeys = selectedKeys
        this.current = { ...info.node.dataRef }
        this.$emit('treeSelect', { ...this.current })
      }
    },
    onLoadData(treeNode) {
      return this.loadTreeData?.(treeNode.dataRef.id).then((treeData) => {
        treeNode.dataRef.children = this.transformData(treeData)
        if (treeNode.dataRef.children.length === 0) {
          treeNode.dataRef.isLeaf = true
        }
        this.treeData = [...this.treeData]
      })
    },
    // 筛选树
    onSearchChange(e) {
      const value = e.target.value
      const expandedKeys = this.dataList
        .map((item) => {
          if (item.title.indexOf(value) > -1) {
            return this.getParentNode(item.key, this.treeData)?.id
          }
          return null
        })
        .filter((item, i, self) => item && self.indexOf(item) === i)
      expandedKeys.unshift(this.root.id) // 顶结点永远展开
      Object.assign(this, {
        expandedKeys,
        searchValue: value,
        autoExpandParent: true,
      })
    },
    getParentNode(key, tree) {
      let parentNode
      for (let i = 0; i < tree.length; i++) {
        const node = tree[i]
        if (node.children) {
          if (node.children.some((item) => item.id === key)) {
            parentNode = node
          } else if (this.getParentNode(key, node.children)) {
            parentNode = this.getParentNode(key, node.children)
          }
        }
      }
      return parentNode
    },
    refresh() {
      this.initTreeData()
    },
    // 新建分类
    create(parent) {
      if (!parent) {
        parent = { ...this.current }
      }
      if (this.showModal) {
        this.beforeModalShow?.({ ...parent }).then(() => {
          this.categoryId = null
          this.$refs.category.show()
        })
      } else {
        this.$emit('createCategory', parent)
      }
    },
    // 保存分类成功
    categorySaved() {
      this.refresh()
    },
    // 编辑分类
    edit(id) {
      const parent = this.getParentNode(id, this.treeData)
      if (this.showModal) {
        this.beforeModalShow?.({ ...parent }).then(() => {
          this.categoryId = id
          this.$refs.category.show()
        })
      } else {
        this.$emit('editCategory', id, parent)
      }
    },
    // 删除分类
    deleteCategory(id) {
      const deleteSuccess = () => {
        // 去除当前分类的选中状态
        this.selectedKeys = this.selectedKeys.filter((key) => key !== id)
        // 展开节点数组去除被删除的节点
        const index = this.expandedKeys.findIndex((key) => key === id)
        if (index !== -1) {
          this.expandedKeys = this.expandedKeys.slice(0, index)
        }
        this.initTreeData()
      }
      if (this.showModal) {
        Modal.confirm({
          title: '您确定删除这项内容吗？',
          content: '删除这条数据后，就无法恢复初始的状态。',
          okText: '删除',
          okType: 'danger',
          onOk: () => {
            this.deleteLoading = true
            PageService.delete({ ids: id }, this.formId)
              .then(() => {
                deleteSuccess()
              })
              .finally(() => {
                this.deleteLoading = false
              })
          },
          onCancel: () => {
            this.deleteLoading = false
          },
        })
      } else {
        if (this.deleteCategoryFunction) {
          this.deleteLoading = true
          this.deleteCategoryFunction(id)
            .then((flag) => {
              if (flag) {
                deleteSuccess()
              }
            })
            .finally(() => {
              this.deleteLoading = false
            })
        }
      }
    },
    getDetailModal() {
      return this.$refs.category
    },
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
.spin {
  width: 100%;
  line-height: 30;
}
.tree {
  :global(.ant-tree-title) {
    display: inline-block;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  :global(.ant-input-search) {
    margin: 8px 0;
    overflow: hidden;
  }
  .active {
    color: $primary-color;
  }
  :global(.ant-card-body) {
    padding: 0;
  }
}
.treewrap {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 20%;
  min-height: 100%;
  margin-right: $padding-lg;
  transition: width 0.2s;
  :global(.ant-tree) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  :global(.ant-card-body) {
    background: $white;
  }
}
.header {
  display: flex;
  align-items: center;
}
.node {
  display: flex;
  justify-content: space-between;
  .text {
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .action {
    display: none;
  }
  &:hover {
    .action {
      position: relative;
      z-index: 1;
      display: inline-block;
      i {
        margin-left: 5px;
      }
    }
  }
}
</style>
