<script setup lang="ts">
import { ElTable, ElCard, TableColumnCtx } from 'element-plus'
import { ColumnConfigType, HandleConfigType } from '@/components/ATable/type'
import { ToolbarConfigType } from '@/components/AToolbar/type'
import { StrAnyObjArr } from '@/typings'
import { getResult } from '@/utils'

const props = withDefaults(
  defineProps<{
    // 表格数据
    data: StrAnyObjArr
    // 是否显示查询条件
    showQuery?: boolean
    // 工具栏配置
    toolbarConfig?: ToolbarConfigType[]
    // 表单配置
    columnConfig: ColumnConfigType[]
    // 树形配置
    treeProps?: {
      hasChildren?: string
      children?: string
    }
    // 主键名称
    rowKey?: string
    // 是否多选
    selection?: boolean
    // 总条数
    pageTotal?: number
    // 每页大小
    pageSize?: number
    // 页数
    pageNum?: number
    // 是否默认展开所有行，当 Table 包含展开行存在或者为树形表格时有效
    expandAll?: boolean
    // 切换展开/折叠时，确认是否展开
    toggleRowExpansion?: (obj: any, expand: boolean) => boolean
    // 合并行列方法
    spanMethod?: (data: {
      row: any
      rowIndex: number
      column: TableColumnCtx<any>
      columnIndex: number
    }) =>
      | number[]
      | {
          rowspan: number
          colspan: number
        }
      | undefined
    card?: boolean
  }>(),
  {
    rowKey: 'id',
    selection: false,
    expandAll: false,
    card: true
  }
)

const tableRef = ref<InstanceType<typeof ElTable>>()
const columnMap = ref<Map<string, boolean>>(new Map<string, boolean>())
props.columnConfig.forEach((item) => {
  columnMap.value.set(item.label as string, item.visible ?? true)
})

const emits = defineEmits([
  'update:showQuery',
  'update:pageNum',
  'update:pageSize',
  'pageNumChange',
  'pageSizeChange',
  'selectionChange'
])

const computedShowQuery = computed({
  get() {
    return props.showQuery
  },
  set(newValue) {
    emits('update:showQuery', newValue)
  }
})

const computedPageNum = computed({
  get() {
    return props.pageNum
  },
  set(newValue) {
    emits('update:pageNum', newValue)
  }
})

const computedPageSize = computed({
  get() {
    return props.pageSize
  },
  set(newValue) {
    emits('update:pageSize', newValue)
  }
})

watch(
  () => props.expandAll,
  (newVal) => {
    nextTick(() => {
      toggleExpandAll(props.data, newVal)
    })
  }
)

watch(
  () => props.data,
  (newVal, oldValue) => {
    if (oldValue === undefined || oldValue.length === 0) {
      nextTick(() => {
        toggleExpandAll(newVal, props.expandAll)
      })
    }
  }
)

// 展开/折叠所有
function toggleExpandAll(data: StrAnyObjArr, status: boolean) {
  const childrenName = props.treeProps?.children ?? 'children'
  data.forEach((item) => {
    const expanded = props.toggleRowExpansion ? props.toggleRowExpansion(item, status) : status
    tableRef.value?.toggleRowExpansion(item, expanded)
    if (expanded && item[childrenName] && item[childrenName].length > 0) {
      toggleExpandAll(item[childrenName], status)
    }
  })
}

// 获取操作栏权限
function getHandlePermissions(config: HandleConfigType[]) {
  // 发现1个按钮无需有权验证，不隐藏操作列
  const list = config.filter((item) => item.permissions)
  if (list.length !== config.length) {
    return undefined
  }

  // 循环按钮权限，将所有权限合并到一个数组中
  const result = []
  for (const item of list) {
    if (item.permissions instanceof Array) {
      item.permissions.forEach((permission) => {
        result.push(permission)
      })
    } else {
      result.push(item.permissions)
    }
  }
  return result
}

// 当前页数change事件
function pageNumChange(pageNum: number) {
  emits('pageNumChange', pageNum)
}

// 每页大小change事件
function pageSizeChange(pageSize: number) {
  emits('pageSizeChange', pageSize)
}

// 选中change事件
function selectionChange(selectionData: StrAnyObjArr) {
  emits('selectionChange', selectionData)
}
</script>

<template>
  <component :is="card ? ElCard : 'div'" :class="card ? 'parent' : ''">
    <div v-if="!card && toolbarConfig" class="toolbarDiv">
      <a-toolbar
        :config="toolbarConfig"
        v-model:showQuery="computedShowQuery"
        :column-map="columnMap"
        :column-config="columnConfig"
      />
    </div>

    <template #header v-if="card && toolbarConfig">
      <a-toolbar
        :config="toolbarConfig"
        v-model:showQuery="computedShowQuery"
        :column-map="columnMap"
        :column-config="columnConfig"
      />
    </template>

    <el-table
      ref="tableRef"
      class="table-box"
      fit
      stripe
      border
      table-layout="auto"
      :data="data"
      :row-key="rowKey"
      :tree-props="treeProps"
      :default-expand-all="false"
      :span-method="spanMethod"
      @selection-change="selectionChange"
    >
      <el-table-column v-if="selection" width="40" type="selection" align="center" />

      <template v-for="(item, index) in columnConfig" :key="index">
        <template v-if="item.handleConfig">
          <handle-column
            v-if="getResult(item.if ?? true)"
            v-permissions="getHandlePermissions(item.handleConfig)"
            :label="item.label"
            :width="item.width"
            :min-width="item.minWidth"
            :align="item.align"
            :header-align="item.headerAlign"
            :fixed="item.fixed"
            :handle-config="item.handleConfig"
          />
        </template>

        <template v-else-if="item.dict">
          <dict-column
            v-if="getResult(item.if ?? true) && columnMap.get(item.label as string)"
            v-permissions="item.permissions"
            :label="item.label"
            :width="item.width"
            :min-width="item.minWidth"
            :align="item.align"
            :header-align="item.headerAlign"
            :fixed="item.fixed"
            :prop="item.prop"
            :dict="item.dict"
          />
        </template>

        <template v-else-if="item.slot">
          <slot-column
            v-if="getResult(item.if ?? true) && columnMap.get(item.label as string)"
            v-permissions="item.permissions"
            :label="item.label"
            :width="item.width"
            :min-width="item.minWidth"
            :align="item.align"
            :header-align="item.headerAlign"
            :fixed="item.fixed"
            :show-overflow-tooltip="item.showOverflowTooltip"
          >
            <template #default="scope">
              <slot :name="item.slot" :row="scope?.row" :index="scope?.index" />
            </template>
          </slot-column>
        </template>

        <template v-else>
          <text-column
            v-if="getResult(item.if ?? true) && columnMap.get(item.label as string)"
            v-permissions="item.permissions"
            :visible="item.visible"
            :label="item.label"
            :width="item.width"
            :min-width="item.minWidth"
            :align="item.align"
            :header-align="item.headerAlign"
            :fixed="item.fixed"
            :prop="item.prop"
            :formatter="item.formatter"
            :show-overflow-tooltip="item.showOverflowTooltip"
          />
        </template>
      </template>
    </el-table>

    <div v-if="!card && pageTotal" class="paginationDiv">
      <el-pagination
        class="pagination"
        :page-sizes="[10, 20, 40, 50, 100]"
        layout="total, sizes, prev, pager, next, jumper"
        background
        :total="pageTotal"
        v-model:current-page="computedPageNum"
        v-model:page-size="computedPageSize"
        @current-change="pageNumChange"
        @size-change="pageSizeChange"
      />
    </div>

    <template #footer v-if="card && pageTotal">
      <el-pagination
        class="pagination"
        :page-sizes="[10, 20, 40, 50, 100]"
        layout="total, sizes, prev, pager, next, jumper"
        background
        :total="pageTotal"
        v-model:current-page="computedPageNum"
        v-model:page-size="computedPageSize"
        @current-change="pageNumChange"
        @size-change="pageSizeChange"
      />
    </template>
  </component>
</template>

<style scoped lang="scss">
.parent {
  margin-top: 10px;
  margin-bottom: 20px;
}

.toolbarDiv {
  padding: 15px 0 15px 0;
}

.paginationDiv {
  padding-top: 15px;
}

.pagination {
  display: flex;
  justify-content: flex-end;
}
</style>
