<template>
  <!--
    page-table — CRUD 表格便捷组合体
    ================================
    这是一个把「顶部按钮栏 + 数据表格 + 底部分页」打包在一起的高层组件。
    适合 80% 的增删改查页面，你只需要传列定义、按钮定义、数据和分页参数即可。

    依赖：element-ui 的 <el-table> <el-button> <el-pagination>
    i18n：由调用方负责翻译，组件直接渲染传入的文本

    @author 前端团队
    @since  2026-05
  -->
  <div
    ref="root"
    class="page-table"
    :class="{ 'page-table--auto': autoHeight || height === 'auto' }"
    v-loading="loading"
  >
    <!-- ==================== 顶部：工具栏按钮区 ==================== -->
    <!--
      toolbarButtons 由 useTableButtons({ toolbar: [...] }) 生成
      内部自动过滤 hasPermission === false 的按钮（无权限不显示）
      按钮被 disabled 只在前端计算（如 needSelection 需要选中行），实际权限由后端校验
    -->
    <div ref="toolbar" class="page-table__toolbar" v-if="toolbarButtons.length || helpUrl">
      <div class="page-table__toolbar-left">
        <el-button
          v-for="btn in visibleToolbarButtons"
          :key="btn.key"
          :type="btn.type"
          :size="btn.size"
          :icon="btn.icon"
          :style="btn.cssStyle"
          :disabled="btn.needSelection && !selectedCount"
          :title="getToolbarDisabledTip(btn)"
          @click="btn.onClick"
        >
          {{ $t(btn.label) }}
        </el-button>
        <!-- 自定义工具栏内容：如打印按钮、列筛选器等 -->
        <slot name="toolbar-extra" />
      </div>
      <!-- 帮助按钮：始终显示在工具栏最右侧，点击新窗口打开帮助文档 -->
      <el-button
        v-if="helpUrl"
        class="page-table__help-btn"
        size="mini"
        icon="el-icon-question"
        @click="openHelp"
      >
        {{ helpText }}
      </el-button>
    </div>

    <!-- ==================== 中部：数据表格 ==================== -->
    <!--
      height 由外部传入或自动计算（autoHeight=true 时）
      v-bind="tableAttrs" / v-on="tableListeners" 透传 el-table 原生属性和事件
    -->
    <div ref="tableWrapper" class="page-table__body">
      <el-table
        ref="table"
        :data="data"
        :height="tableHeight"
        :border="border"
        :row-key="rowKey"
        :header-cell-style="headerCellStyle"
        v-bind="tableAttrs"
        v-on="elTableListeners"
        @selection-change="onSelectionChange"
      >
        <!--
          遍历 columns，有五种列类型：
          1. type='selection'  → 复选框列
          2. type='index'      → 序号列
          3. slot='_actions'   → 操作列（自动渲染 rowButtons）
          4. slot/headerSlot   → 自定义插槽列
          5. 其他              → 普通数据列
        -->
        <template v-for="col in columns">
          <!-- 1. 复选框列 -->
          <el-table-column
            v-if="col.type === 'selection'"
            :key="'sel-' + col.idx"
            type="selection"
            :width="col.width"
            :selectable="selectionSelectable || undefined"
          />
          <!-- 2. 序号列 -->
          <el-table-column
            v-else-if="col.type === 'index'"
            :key="'idx-' + col.idx"
            type="index"
            :width="col.width"
            :label="$t(col.label) || '#'"
          />
          <!-- 3. 操作列：自动渲染 rowButtons（编辑/删除等行内按钮） -->
          <!--
            约定：columns 中 prop='_actions' 的列会被识别为操作列
            rowButtons 由 useTableButtons({ row: [...] }) 生成
            红色按钮通过 color='danger' 控制
          -->
          <el-table-column
            v-else-if="col.slot === '_actions' && rowButtons.length"
            :key="'act-' + col.idx"
            v-bind="colAttrs(col)"
          >
            <template #default="{ row, $index }">
              <template v-for="btn in visibleRowButtons">
                <span
                  v-if="btn.visible(row)"
                  :key="btn.key"
                  :style="btn.cssStyle"
                  :class="{ 'action-btn--danger': btn.color === 'danger' }"
                  class="action-btn"
                  @click="btn.onClick(row, $index)"
                >
                  <i v-if="btn.icon" :class="btn.icon" />
                  {{ $t(btn.label) }}
                </span>
              </template>
            </template>
          </el-table-column>
          <!-- 4. 自定义插槽列：列内容或表头可由父组件自定义 -->
          <!--
            使用方式：
            - col.slot='status'    → 父组件用 <template #col-status="{ row }">
            - col.headerSlot='xyz' → 父组件用 <template #xyz> 自定义表头
          -->
          <el-table-column
            v-else-if="col.slot || col.headerSlot"
            :key="'slot-' + col.idx"
            v-bind="colAttrs(col)"
          >
            <template #header="{ column }">
              <slot v-if="col.headerSlot" :name="col.headerSlot" :data="col" :column="column">
                <span>{{ column.label }}</span>
              </slot>
              <span v-else>{{ column.label }}</span>
            </template>
            <template #default="{ row, $index }">
              <slot :name="'col-' + col.slot" :row="row" :index="$index">
                <span>{{ row[col.prop] }}</span>
              </slot>
            </template>
          </el-table-column>
          <!-- 5. 普通数据列：直接按 prop 取 row 数据 -->
          <el-table-column
            v-else
            :key="'col-' + col.idx"
            v-bind="colAttrs(col)"
          />
        </template>
        <!-- 表格无数据时显示的占位内容 -->
        <template #empty>
          <slot name="empty" />
        </template>
        <!-- 表格最后一行之后追加的内容 -->
        <template #append>
          <slot name="append" />
        </template>
        <slot />
      </el-table>
    </div>

    <!-- ==================== 底部：分页组件 ==================== -->
    <!--
      只传 pagination 对象（{ current, size, total }）才显示分页
      page-change 事件回传 { current, size, total }，父组件更新 pagination 后重新 fetchData
    -->
    <div ref="footer" class="page-table__footer" v-if="pagination">
      <el-pagination
        :current-page="paginationCurrent"
        :page-size="paginationSize"
        :total="paginationTotal"
        :page-sizes="[10, 25, 50, 100, 250, 500]"
        :disabled="loading"
        layout="total, sizes, prev, pager, next, jumper"
        @size-change="onSizeChange"
        @current-change="onCurrentChange"
      />
    </div>

    <!-- 自定义尾部内容 -->
    <slot name="extra" />
  </div>
</template>

<script>
/**
 * PageTable — CRUD 表格便捷组合体
 *
 * 【核心职责】
 *   1. 渲染顶部工具栏（增删改查等操作按钮，自动权限过滤）
 *   2. 渲染数据表格（5 种列类型，自动 i18n 翻译）
 *   3. 渲染底部分页
 *   4. 表格高度自适应（可选）
 *
 * 【典型用法】
 *   <page-table
 *     :columns="columns"
 *     :data="tableData"
 *     :loading="loading"
 *     :toolbar-buttons="toolbarButtons"
 *     :row-buttons="rowButtons"
 *     :pagination="pagination"
 *     auto-height
 *     @page-change="onPageChange"
 *     @selection-change="onSelect"
 *   />
 *
 * 【内部事件流】
 *   toolbarButtons[i].onClick() → 父组件方法 → fetchData → data 更新 → 表格刷新
 *   el-pagination @change → emit('page-change') → 父组件更新 pagination → fetchData
 *   el-table @selection-change → emit('selection-change') → 父组件获取选中行
 *
 * 【与 useTableColumns / useTableButtons 配合】
 *   详见文档：docs/sct-base-table-refactor-design.md
 */
export default {
  name: 'PageTable',
  props: {
    /**
     * 列定义，由 useTableColumns() 生成
     *
     * 普通列：{ prop: 'code', label: '编码', minWidth: 120 }
     * 操作列：{ prop: '_actions', label: '操作', width: 160, fixed: 'right' }
     * 自定义列：{ prop: 'status', label: '状态', slot: true }
     *
     * 组件内部会按 type/slot/prop 自动分类渲染不同的 <el-table-column>
     */
    columns: { type: Array, default: () => [] },

    /**
     * 表格行数据，直接传接口返回的数组
     */
    data: { type: Array, default: () => [] },

    /**
     * 是否显示 loading 遮罩，通常在 fetchData 期间为 true
     */
    loading: { type: Boolean, default: false },

    /**
     * 表格高度，不传则 el-table 按内容撑开
     * 传 'auto' 或同时传 auto-height 启用自动计算（窗口高度 - 搜索区 - 工具栏 - 分页）
     * 传数字/字符串（如 '500px'）则固定高度
     */
    height: { type: [String, Number], default: undefined },

    /**
     * 启用表格高度自适应：根据容器可视区域自动计算高度
     * 启用后 height prop 被忽略，组件内部通过 ResizeObserver 持续计算
     */
    autoHeight: { type: Boolean, default: false },

    /**
     * 是否显示表格边框，默认 true
     */
    border: { type: Boolean, default: true },

    /**
     * 行数据的唯一 key，默认 'id'，用于 el-table 的 row-key
     * 树形表格或需要保留选中状态时必需
     */
    rowKey: { type: String, default: 'id' },

    /**
     * 顶部工具栏按钮列表，由 useTableButtons({ toolbar: [...] }) 生成
     *
     * 每个按钮字段：
     *   key         - 唯一标识（必填）
     *   label       - 显示文本，支持 i18n key（组件自动 $t() 翻译）
     *   icon        - element-ui 图标名（'el-icon-plus' 等）
     *   type        - 按钮样式类型（'primary' | 'success' | 'warning' | 'danger'）
     *   auth        - 权限 key，传给 useTableButtons 的第二个参数判断
     *   cssStyle    - 自定义样式对象
     *   needSelection - true 的话需要表格有选中行才能点击
     *   onClick     - 点击回调（由父组件绑定方法）
     *   hasPermission - 自动注入，由 useTableButtons 计算（不要手动设）
     */
    toolbarButtons: { type: Array, default: () => [] },

    /**
     * 行内操作按钮列表，由 useTableButtons({ row: [...] }) 生成
     *
     * 每个按钮字段：
     *   key / label / icon / auth / onClick — 同 toolbarButtons
     *   color - 'danger' 文字变红，用于删除等危险操作
     *   cssStyle - 默认 { marginRight: '10px', cursor: 'pointer' }
     */
    rowButtons: { type: Array, default: () => [] },

    /**
     * 分页参数，传此 prop 才显示分页组件
     *
     * { current: 1, size: 10, total: 0 }
     *
     * total 通常在 getList 接口返回 res.count
     */
    pagination: { type: Object, default: null },

    /**
     * 额外透传给 el-table 的原生属性，如 { stripe: true, size: 'medium' }
     */
    tableAttrs: { type: Object, default: () => ({}) },

    /**
     * 额外透传给 el-table 的原生事件，如 { 'sort-change': handleSort }
     */
    tableListeners: { type: Object, default: () => ({}) },

    /**
     * 选择列可选规则，透传给 el-table-column 的 selectable。
     * 例：row => row.active === 1
     */
    selectionSelectable: { type: Function, default: null },

    /**
     * 帮助文档的跳转 URL。传了才显示工具栏右侧的问号按钮，点击新窗口打开
     * 例：'/docs/factory-area-help.html'
     */
    helpUrl: { type: String, default: '' },

    /**
     * 帮助按钮的文字，由调用方负责 i18n 翻译
     * 默认 '帮助'
     */
    helpText: { type: String, default: '帮助' }
  },
  data () {
    return {
      tableSelected: [], // 当前选中的行数据
      computedHeight: null // autoHeight 时动态计算的高度
    }
  },
  computed: {
    /**
     * 最终使用的表格高度：
     *   - 用户明确传了 height（非 'auto'）→ 直接用
     *   - autoHeight=true 或 height='auto' → 用动态计算的 computedHeight
     *   - 否则 undefined（el-table 按内容自适应）
     */
    tableHeight () {
      if (this.height && this.height !== 'auto') return this.height
      if (this.autoHeight || this.height === 'auto') return this.computedHeight || undefined
      return undefined
    },

    /**
     * 过滤后的工具栏按钮（排除无权限的）
     */
    visibleToolbarButtons () {
      return this.toolbarButtons.filter(b => b.hasPermission !== false)
    },

    /**
     * 过滤后的行内操作按钮（排除无权限的）
     */
    visibleRowButtons () {
      return this.rowButtons.filter(b => b.hasPermission !== false)
    },

    /**
     * 选中的行数
     */
    selectedCount () {
      return this.tableSelected.length
    },

    paginationCurrent () {
      if (!this.pagination) return 1
      return Number(this.pagination.current || this.pagination.currentPage || 1)
    },

    paginationSize () {
      if (!this.pagination) return 10
      return Number(this.pagination.size || this.pagination.pageSize || 10)
    },

    paginationTotal () {
      if (!this.pagination) return 0
      return Number(this.pagination.total || 0)
    },

    /**
     * 表头样式：浅灰背景 + 黑色加粗文字
     */
    headerCellStyle () {
      return () => 'background-color: #F8F8F8; color: #000000; font-weight: 500;'
    },

    /**
     * 将父组件直接绑定在 page-table 上的 el-table 原生事件透传下去。
     * selection-change 由当前组件统一维护选中行状态，避免父级监听被触发两次。
     */
    elTableListeners () {
      const listeners = {
        ...this.$listeners,
        ...this.tableListeners
      }
      delete listeners['selection-change']
      delete listeners['page-change']
      return listeners
    }
  },
  mounted () {
    if (this.autoHeight || this.height === 'auto') {
      this.startAutoHeight()
    }
  },
  beforeDestroy () {
    this.stopAutoHeight()
  },
  methods: {
    /* ============ 列处理 ============ */

    colAttrs (col) {
      const attrs = { ...col }
      delete attrs.idx
      delete attrs.slot
      delete attrs.headerSlot
      if (attrs.label) {
        attrs.label = this.$t(attrs.label)
      }
      return attrs
    },

    /* ============ 表格事件 ============ */

    onSelectionChange (val) {
      this.tableSelected = val
      this.$emit('selection-change', val)
    },

    clearSelection () {
      if (this.$refs.table && this.$refs.table.clearSelection) {
        this.$refs.table.clearSelection()
      }
      this.tableSelected = []
      this.$emit('selection-change', [])
    },

    /* ============ 帮助按钮 ============ */

    openHelp () {
      if (this.helpUrl) {
        window.open(this.helpUrl, '_blank')
      }
    },

    getToolbarDisabledTip (btn) {
      if (!btn || !btn.needSelection || this.selectedCount) return ''
      return btn.disabledTip ? this.$t(btn.disabledTip) : ''
    },

    /* ============ 分页事件 ============ */

    onSizeChange (size) {
      this.$emit('page-change', {
        current: 1,
        currentPage: 1,
        size,
        pageSize: size,
        total: this.paginationTotal
      })
    },

    onCurrentChange (current) {
      this.$emit('page-change', {
        current,
        currentPage: current,
        size: this.paginationSize,
        pageSize: this.paginationSize,
        total: this.paginationTotal
      })
    },

    /* ============ 自适应高度 ============ */

    /**
     * 启动表格高度自适应
     *
     * 原理：
     *   1. CSS flex 布局让 page-table 占满父容器 100% 高度
     *   2. 工具栏和分页各占自然高度（flex-shrink: 0）
     *   3. tableWrapper 中间区域自动填充剩余空间（flex: 1 + min-height: 0）
     *   4. ResizeObserver 监听 tableWrapper 的实际渲染高度，赋给 el-table
     *
     * 这样 el-table 永远精确等于可用空间，不会产生外层滚动条
     */
    startAutoHeight () {
      this.stopAutoHeight()

      this._resizeObserver = new ResizeObserver(() => {
        this.$nextTick(() => {
          const wrapper = this.$refs.tableWrapper
          if (wrapper) {
            this.computedHeight = wrapper.clientHeight
          }
        })
      })

      if (this.$refs.tableWrapper) {
        this._resizeObserver.observe(this.$refs.tableWrapper)
        this.$nextTick(() => {
          const wrapper = this.$refs.tableWrapper
          if (wrapper) {
            this.computedHeight = wrapper.clientHeight
          }
        })
      }
    },

    stopAutoHeight () {
      if (this._resizeObserver) {
        this._resizeObserver.disconnect()
        this._resizeObserver = null
      }
    }
  }
}
</script>

<style scoped>
.page-table {
  display: flex;
  flex-direction: column;
}
/* 自适应高度模式：占满父容器 100%，禁止自身溢出 */
.page-table--auto {
  height: 100%;
  overflow: hidden;
}
.page-table__toolbar {
  flex-shrink: 0;
  margin-bottom: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
}
.page-table__toolbar-left {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.page-table__help-btn {
  flex-shrink: 0;
  margin-left: auto;
}
.page-table__body {
  flex: 1;
  min-height: 0;
  overflow: hidden;
  width: 100%;
}
.page-table__footer {
  flex-shrink: 0;
  margin-top: 12px;
  display: flex;
  justify-content: flex-end;
}
.action-btn {
  display: inline-block;
  margin: 0 5px;
  cursor: pointer;
  white-space: nowrap;
}
.action-btn--danger {
  color: #F56C6C;
}
</style>
