<template>
  <div class="base-table-layer" ref="baseTableLayerRef">
    <div class="operation-box" ref="operationBoxRef">
      <div class="operation-filter" v-show="search">
        <el-form inline>
          <div ref="searchDomRef" class="searchStyle">
            <div class="searchStyle-item">
              <slot name="search" v-if="search" />
            </div>
            <div class="btnDom">
              <el-form-item style="margin-right: 0">
                <el-button type="primary" @click="searchFn(true)">搜索</el-button>
                <el-button plain type="primary" @click="resetFn">重置</el-button>
              </el-form-item>
            </div>
          </div>
        </el-form>
      </div>
    </div>
    <div class="l-main-box">
      <div class="operation-control" v-if="control">
        <p class="operation-control-t">操作</p>
        <div>
          <slot name="prevBtn" />
          <el-button type="primary" size="default" @click="$emit('addFn')" v-if="addBtn">{{ addBtnText }}</el-button>
          <slot name="nextBtn" />
          <el-dropdown trigger="click" @command="handleCommand" v-if="moreOperation">
            <el-button size="default" plain style="margin-left: 12px">
              更多操作
              <el-icon class="el-icon--right"><ArrowDown /></el-icon>
            </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item v-if="batchDelBtn" command="batchDelFn">{{ batchDelBtnText }}</el-dropdown-item>
                <slot name="operation" />
                <el-dropdown-item v-if="importBtn" command="importFn">导入数据</el-dropdown-item>
                <el-dropdown-item v-if="exportBtn" command="exportFn">导出数据</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </div>
      <div class="table-box">
        <el-table
          ref="tableRef"
          v-loading="loading"
          stripe
          border
          tooltip-effect="dark"
          :height="tableHeight < 50 ? 300 : tableHeight"
          :data="tableData"
          :row-key="rowKey"
          :tree-props="treeProps"
          :show-summary="showSummary"
          :summary-method="summarize"
          @selection-change="handleSelectionChange"
        >
          <el-table-column v-if="many" type="selection" align="center" width="50" />
          <el-table-column v-if="index" type="index" label="序号" align="center" width="50">
            <template #default="scope">
              <span>{{ (searchParams.current - 1) * searchParams.size + scope.$index + 1 }}</span>
            </template>
          </el-table-column>
          <el-table-column
            v-for="item in tableHeader"
            :key="item.prop"
            :label="item.label"
            :prop="item.prop"
            :fixed="item.fixed || false"
            :width="item.width"
            :min-width="item.minWidth"
            :align="item.align"
            show-overflow-tooltip
            :sortable="item.sortable || false"
            header-align="center"
          >
            <template #default="scope">
              <span v-if="item.type === 'render'">
                <ExSlot :render="item.render" :row="scope.row" :index="scope.$index" :column="item" />
              </span>
              <span v-else>{{ scope.row[item.prop] || item.defaultValue || '-' }}</span>
            </template>
          </el-table-column>
          <el-table-column
            v-if="tableOperate && tableOperate.length > 0"
            label="操作"
            header-align="center"
            align="center"
            :fixed="fixedFn"
            :width="tableOperate.length === 1 ? '80' : tableOperate.length * 60"
          >
            <template #default="scope">
              <el-tooltip
                effect="dark"
                placement="top"
                v-for="btn in tableOperate"
                :key="btn.label"
                :content="btn.label"
                :enterable="false"
              >
                <el-button
                  v-show="btnShowFn(btn, scope.row)"
                  size="small"
                  :type="btn.type"
                  :icon="btn.icon"
                  :disabled="btnDisableFn(btn, scope.row)"
                  @click="btn.func(scope.row, scope.$index)"
                />
              </el-tooltip>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
    <div class="pagination-box" ref="paginationBoxRef" v-show="isPage">
      <el-pagination
        layout="total, prev, pager, sizes, next"
        :total="Number(searchParams.total)"
        :page-size="Number(searchParams.size)"
        :page-sizes="[10, 50, 100, 200]"
        :current-page="Number(searchParams.current)"
        @current-change="currentChangeFn"
        @size-change="sizeChangeFn"
        @prev-click="prevFn"
        @next-click="nextFn"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, nextTick, h } from 'vue'
import { isArray } from '@/utils/validate'

interface TableHeaderItem {
  label: string
  prop: string
  align?: string
  minWidth?: number | string
  width?: number | string
  fixed?: string | boolean
  sortable?: boolean | string
  type?: string
  render?: (h: any, params: any) => any
  defaultValue?: string
}

interface TableOperateItem {
  label: string
  type?: string
  icon?: string
  func: (row: any, index?: number) => void
  show?: (row: any) => boolean
  disabled?: (row: any) => boolean
}

interface SearchParams {
  current: number
  size: number
  total: number
  [key: string]: any
}

const props = withDefaults(defineProps<{
  many?: boolean
  index?: boolean
  search?: boolean
  control?: boolean
  moreOperation?: boolean
  addBtn?: boolean
  batchDelBtn?: boolean
  batchDelBtnText?: string
  importBtn?: boolean
  exportBtn?: boolean
  rowKey?: string
  treeProps?: Record<string, any>
  showSummary?: boolean
  tableData?: any[]
  tableHeader?: TableHeaderItem[]
  tableOperate?: TableOperateItem[]
  isPage?: boolean
  searchParams: SearchParams
  cutHeight?: number
  addBtnText?: string
  operateFixed?: string
  loading?: boolean
}>(), {
  many: true,
  index: true,
  search: true,
  control: true,
  moreOperation: true,
  addBtn: true,
  batchDelBtn: true,
  batchDelBtnText: '批量删除',
  importBtn: false,
  exportBtn: false,
  rowKey: '',
  treeProps: () => ({}),
  showSummary: false,
  tableData: () => [],
  tableHeader: () => [],
  tableOperate: () => [],
  isPage: true,
  cutHeight: 0,
  addBtnText: '新增',
  operateFixed: '',
  loading: false
})

const emit = defineEmits([
  'searchFn', 'resetFn', 'addFn', 'batchDelFn',
  'importFn', 'exportFn', 'selectChange', 'summaries'
])

const baseTableLayerRef = ref<HTMLElement>()
const operationBoxRef = ref<HTMLElement>()
const paginationBoxRef = ref<HTMLElement>()
const tableRef = ref()
const searchDomRef = ref<HTMLElement>()

const tableHeight = ref(300)

const fixedFn = computed(() => {
  return /^(left|right)$/.test(props.operateFixed) ? (props.operateFixed as 'left' | 'right') : undefined
})

// Functional render slot component (Vue 3 style)
function ExSlot(props: any) {
  const params: any = { row: props.row, index: props.index }
  if (props.column) params.column = props.column
  return props.render(h, params)
}

watch(() => props.tableData, (val) => {
  if (val && val.length !== 0) {
    resizeTable()
  }
})

watch(() => props.searchParams.total, (newVal) => {
  if (props.searchParams.current !== 1 && props.searchParams.current > newVal / props.searchParams.size) {
    props.searchParams.current = Math.ceil(newVal / props.searchParams.size)
    searchFn(false)
  }
})

function searchFn(isReset = false) {
  emit('searchFn', isReset)
}

function resetFn() {
  emit('resetFn', true)
}

function handleCommand(command: string) {
  if (!command) return
  if (command === 'batchDelFn') {
    emit('batchDelFn', (tableRef.value as any)?.getSelectionRows?.() || [])
  } else {
    emit(command as any)
  }
}

function sizeChangeFn(size: number) {
  props.searchParams.size = size
  searchFn(false)
}

function currentChangeFn(current: number) {
  props.searchParams.current = current
  searchFn(false)
}

function prevFn(pageNum: number) {
  props.searchParams.current = pageNum
  searchFn(false)
}

function nextFn(pageNum: number) {
  props.searchParams.current = pageNum
  searchFn(false)
}

function btnShowFn(btn: TableOperateItem, row: any): boolean {
  try {
    if (typeof btn.show === 'function') return btn.show(row)
    return true
  } catch { return true }
}

function btnDisableFn(btn: TableOperateItem, row: any): boolean {
  try {
    if (typeof btn.disabled === 'function') return btn.disabled(row)
    return false
  } catch { return false }
}

function resizeTable() {
  setTimeout(() => {
    if (!baseTableLayerRef.value || !operationBoxRef.value || !paginationBoxRef.value) return
    const baseTableHeight = baseTableLayerRef.value.offsetHeight
    const operationHeight = operationBoxRef.value.offsetHeight
    const paginationHeight = paginationBoxRef.value.offsetHeight
    tableHeight.value = baseTableHeight - operationHeight - paginationHeight - props.cutHeight - 46
  }, 50)
}

const summarize = (params: any): string[] => {
  let sum: string[] = []
  emit('summaries', params, (result: string[]) => { sum = result })
  return sum
}

function handleSelectionChange(val: any[]) {
  emit('selectChange', val)
}

function clearSelection() {
  ;(tableRef.value as any)?.clearSelection()
}

onMounted(() => {
  nextTick(() => {
    window.addEventListener('resize', () => resizeTable())
  })
})

defineExpose({
  clearSelection,
  resizeTable
})
</script>

<style scoped lang="less">
.base-table-layer {
  height: 100%;
  display: flex;
  flex-direction: column;

  .operation-box {
    display: flex;
    flex-direction: column;
    padding-bottom: 10px;

    .operation-filter {
      flex: 1;
      background: #fff;
      border-radius: 0;
      padding-bottom: 6px;
      border-bottom: 1px dashed #dfdddd;

      :deep(.el-form) {
        .el-form-item {
          margin: 0;
          margin-right: 20px;
          margin-bottom: 10px;

          .el-form-item__label {
            background: #f5f7fa;
            padding: 0;
            border: 1px solid #dcdfe6;
            border-right: 0;
            border-radius: 4px 0 0 4px;
            box-sizing: border-box;
            height: 40px;
            width: 100px;
            text-align: center;
          }
        }
      }
    }
  }

  .table-box {
    flex: 1;
    box-sizing: border-box;
  }

  .pagination-box {
    display: flex;
    justify-content: flex-end;
    padding-top: 10px;
  }
}

.operation-control {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;

  &-t {
    margin: 0;
    font-weight: bold;
    color: #333;
    display: flex;
    align-items: center;

    &::before {
      content: '';
      display: block;
      width: 6px;
      height: 14px;
      background: @theme-color;
      margin-right: 8px;
      border-radius: 88px;
    }
  }
}

.searchStyle {
  display: flex;
  justify-content: space-between;

  &-item {
    display: flex;
    flex: 1;
    flex-direction: initial;
    flex-wrap: wrap;
  }
}

.btnDom {
  display: flex;
  min-width: 240px;
  justify-content: flex-end;
}

:deep(.el-table th.el-table__cell) {
  background: #fafafa;
  color: #333;
}
</style>
