<template>
  <div class="pro-table">
    <a-form layout="inline" class="search-form">
      <slot name="search" :form="searchForm" />
      <a-form-item>
        <a-space>
          <a-button type="primary" :loading="loading" @click="handleSearch">查询</a-button>
          <a-button @click="handleReset">重置</a-button>
        </a-space>
      </a-form-item>
    </a-form>

    <div class="toolbar">
      <slot name="actions" />
    </div>

    <a-table
      :columns="columns"
      :data-source="list"
      :loading="loading"
      :row-key="rowKey"
      :pagination="pagination"
      :row-selection="tableRowSelection"
      :scroll="scroll"
      @change="handleTableChange"
    >
      <template v-for="name in forwardSlotNames" #[name]="slotProps" :key="name">
        <slot :name="name" v-bind="slotProps" />
      </template>
    </a-table>
  </div>
</template>

<script setup>
import { reactive, ref, computed, onMounted, useSlots } from 'vue'

const props = defineProps({
  columns: { type: Array, required: true },
  fetchData: { type: Function, required: true },
  rowKey: { type: String, default: 'id' },
  initialSearch: { type: Object, default: () => ({}) },
  pageSize: { type: Number, default: 10 },
  // 兼容原有 Boolean 用法(checkbox 多选);新增字符串 'radio' 支持单选场景(如钱包账户列表)
  rowSelection: { type: [Boolean, String], default: true },
  selectedRowKeys: { type: Array, default: () => [] },
  // 透传给内部 a-table 的 scroll 配置。列较多且有列声明了 fixed(如"操作"列固定在最右侧)时,
  // 需要显式传 { x: 'max-content' } 之类的配置,否则 antd vue 会因存在 fixed 列而强制把
  // table-layout 改成 fixed,导致未设置 width 的列被平均压缩宽度、表头文字挤在一起换行成竖排
  // (仅有 fixed 列而不设 scroll.x 时的典型 bug 表现),详见 vc-table Table.js mergedTableLayout。
  scroll: { type: Object, default: undefined }
})

const emit = defineEmits(['update:selectedRowKeys', 'search'])

const slots = useSlots()
const forwardSlotNames = computed(() =>
  Object.keys(slots).filter((name) => name !== 'search' && name !== 'actions')
)

const searchForm = reactive({ ...props.initialSearch })
const list = ref([])
const loading = ref(false)
const pagination = reactive({
  current: 1,
  pageSize: props.pageSize,
  total: 0,
  showSizeChanger: true,
  showTotal: (total) => `共 ${total} 条`
})

const tableRowSelection = computed(() =>
  props.rowSelection
    ? {
        type: props.rowSelection === 'radio' ? 'radio' : 'checkbox',
        selectedRowKeys: props.selectedRowKeys,
        onChange: (keys) => emit('update:selectedRowKeys', keys)
      }
    : undefined
)

async function loadData() {
  emit('update:selectedRowKeys', [])
  loading.value = true
  try {
    const res = await props.fetchData({
      ...searchForm,
      page: pagination.current,
      pageSize: pagination.pageSize
    })
    list.value = res?.list || []
    pagination.total = res?.total || 0
  } finally {
    loading.value = false
  }
}

async function handleSearch() {
  pagination.current = 1
  await loadData()
  emit('search')
}

function handleReset() {
  Object.keys(searchForm).forEach((key) => {
    searchForm[key] = props.initialSearch[key] ?? undefined
  })
  pagination.current = 1
  loadData()
}

function handleTableChange(pag) {
  pagination.current = pag.current
  pagination.pageSize = pag.pageSize
  loadData()
}

onMounted(loadData)

function getRecordByKey(key) {
  return list.value.find((item) => item[props.rowKey] === key)
}

defineExpose({ reload: loadData, searchForm, getRecordByKey })
</script>

<style scoped>
.search-form {
  margin-bottom: 16px;
}
.toolbar {
  margin-bottom: 12px;
}
</style>
