<script setup lang="ts">
/**
 * ElTablePlus 组件
 * 基于 Element Plus 的增强表格组件，支持列配置、本地存储和分页功能
 */
import {
  computed,
  ref,
  useAttrs,
  useSlots,
  cloneVNode,
  Fragment,
  type VNode,
} from 'vue'
import { useRoute } from 'vue-router'

// 组件配置选项
defineOptions({
  name: 'ElTablePlus',
  inheritAttrs: false,
})

/**
 * Props 定义
 */
const props = withDefaults(
  defineProps<{
    // 表格数据源
    data: any[]
    // 行唯一标识字段
    rowKey?: string
    // 多表格区分 key（用于本地存储）
    tableKey?: string
    // 完全自定义缓存 key，优先级最高
    columnStorageKey?: string
    // 是否开启列配置功能
    columnConfig?: boolean
    // 是否持久化列配置到本地存储
    columnStorage?: boolean
    // 是否显示分页
    pagination?: boolean
    // 总记录数
    total?: number
    // 当前页码
    page?: number
    // 每页条数
    pageSize?: number
    // 可选每页条数列表
    pageSizes?: number[]
    // 分页组件布局
    layout?: string
  }>(),
  {
    columnConfig: true,
    columnStorage: true,
    pagination: true,
    page: 1,
    pageSize: 10,
    pageSizes: () => [10, 20, 50, 100],
    layout: 'total, sizes, prev, pager, next, jumper',
  },
)

/**
 * 事件定义
 */
const emit = defineEmits<{
  // 更新页码
  'update:page': [value: number]
  // 更新每页条数
  'update:pageSize': [value: number]
  // 分页变化
  'pagination-change': [value: { page: number; pageSize: number }]
}>()

/**
 * 依赖注入
 */
const route = useRoute()
const attrs = useAttrs()
const slots = useSlots()

/**
 * 响应式状态
 */
// 列配置弹窗可见性
const dialogVisible = ref(false)

// 列选项类型定义
type ColumnOption = {
  key: string      // 列唯一标识
  label: string    // 列显示名称
  visible: boolean // 是否可见
  disabled: boolean // 是否禁用选择
  vnode: VNode     // 列的 VNode 节点
}

// 列选项列表
const columnOptions = ref<ColumnOption[]>([])

// 弹窗中的临时列选项（确认后才应用）
const pendingColumnOptions = ref<ColumnOption[]>([])

/**
 * 计算属性
 */

// 本地存储的 key（根据路由路径和表格 key 生成）
const storageKey = computed(() => {
  // 优先使用自定义的存储 key
  if (props.columnStorageKey) {
    return props.columnStorageKey
  }
  // 如果指定了表格 key，添加到存储 key 中
  if (props.tableKey) {
    return `el-table-plus:${route.path}:${props.tableKey}`
  }
  // 默认使用路由路径作为存储 key
  return `el-table-plus:${route.path}`
})

// 当前页码（双向绑定）
const currentPage = computed({
  get: () => props.page,
  set: value => {
    emit('update:page', value)
    emit('pagination-change', {
      page: value,
      pageSize: props.pageSize,
    })
  },
})

// 当前每页条数（双向绑定）
const currentPageSize = computed({
  get: () => props.pageSize,
  set: value => {
    emit('update:pageSize', value)
    emit('update:page', 1)
    emit('pagination-change', {
      page: 1,
      pageSize: value,
    })
  },
})

// 是否全选（弹窗中的临时状态）
const isAllSelected = computed({
  get: () => {
    const selectable = pendingColumnOptions.value.filter(col => !col.disabled)
    return selectable.length > 0 && selectable.every(col => col.visible)
  },
  set: value => {
    pendingColumnOptions.value.forEach(col => {
      if (!col.disabled) {
        col.visible = value
      }
    })
  },
})

/**
 * 方法定义
 */

/**
 * 获取列的唯一标识
 * @param vnode - 列的 VNode 节点
 * @param index - 索引位置
 * @returns 列的唯一标识
 */
const getColumnKey = (vnode: VNode, index: number) => {
  const columnProps = vnode.props as Record<string, any> | null

  return (
    columnProps?.columnKey ||
    columnProps?.prop ||
    columnProps?.label ||
    columnProps?.type ||
    `column_${index}`
  )
}

/**
 * 获取列的显示标签
 * @param vnode - 列的 VNode 节点
 * @param index - 索引位置
 * @returns 列的显示标签
 */
const getColumnLabel = (vnode: VNode, index: number) => {
  const columnProps = vnode.props as Record<string, any> | null

  return (
    columnProps?.label ||
    columnProps?.prop ||
    columnProps?.type ||
    `列 ${index + 1}`
  )
}

/**
 * 从本地存储恢复列配置
 */
const restoreColumnConfig = () => {
  // 如果未开启本地存储，直接返回
  if (!props.columnStorage) return

  // 从 localStorage 获取缓存的列配置
  const cache = localStorage.getItem(storageKey.value)
  if (!cache) return

  try {
    // 解析缓存数据
    const cacheColumns = JSON.parse(cache) as Array<{
      key: string
      visible: boolean
    }>

    // 遍历列选项，更新可见性状态
    columnOptions.value.forEach(column => {
      if (column.disabled) return
      const match = cacheColumns.find(item => item.key === column.key)
      if (match) {
        column.visible = match.visible
      }
    })
  } catch {
    // 解析失败时清除缓存
    localStorage.removeItem(storageKey.value)
  }
}

/**
 * 保存列配置到本地存储
 */
const saveColumnConfig = () => {
  // 如果未开启本地存储，直接返回
  if (!props.columnStorage) return

  // 构建需要缓存的数据结构
  const cacheColumns = columnOptions.value.map(column => ({
    key: column.key,
    visible: column.visible,
  }))

  // 保存到 localStorage
  localStorage.setItem(storageKey.value, JSON.stringify(cacheColumns))
}

/**
 * 打开列配置弹窗
 */
const openColumnConfigDialog = () => {
  pendingColumnOptions.value = columnOptions.value.map(col => ({ ...col }))
  dialogVisible.value = true
}

/**
 * 确认列配置
 */
const confirmColumnConfig = () => {
  pendingColumnOptions.value.forEach(pending => {
    const col = columnOptions.value.find(item => item.key === pending.key)
    if (col) {
      col.visible = pending.visible
    }
  })
  saveColumnConfig()
  dialogVisible.value = false
}

/**
 * 取消列配置
 */
const cancelColumnConfig = () => {
  dialogVisible.value = false
}

/**
 * 初始化列配置
 */
const initColumns = () => {
  const rawChildren = slots.default?.() || []

  const flattenVNodes = (vnodes: VNode[]): VNode[] => {
    const result: VNode[] = []
    for (const vnode of vnodes) {
      if (vnode.type === Fragment && Array.isArray(vnode.children)) {
        result.push(...flattenVNodes(vnode.children as VNode[]))
      } else {
        result.push(vnode)
      }
    }
    return result
  }

  const children = flattenVNodes(rawChildren)

  columnOptions.value = children.map((vnode, index) => {
    const columnProps = vnode.props as Record<string, any> | null
    return {
      key: getColumnKey(vnode, index),
      label: getColumnLabel(vnode, index),
      visible: columnProps?.visible !== false,
      disabled: columnProps?.disabled ?? false,
      vnode,
    }
  })

  restoreColumnConfig()
}

/**
 * 获取可见的列（用于渲染）
 */
const visibleColumns = computed(() => {
  return columnOptions.value
    .filter(column => column.visible)
    .map(column => cloneVNode(column.vnode))
})

// 初始化列配置
initColumns()
</script>

<template>
  <div class="el-table-plus">
    <!-- 工具栏 -->
    <div
      v-if="columnConfig"
      class="el-table-plus__toolbar"
    >
      <div class="el-table-plus__toolbar-left">
        <slot name="toolbar" />
      </div>

      <div class="el-table-plus__toolbar-right">
        <slot name="toolbar-right" />

        <el-button
          size="small"
          @click="openColumnConfigDialog"
        >
          列配置
        </el-button>
      </div>
    </div>

    <!-- 表格主体 -->
    <el-table
      v-bind="attrs"
      :data="data"
      :row-key="rowKey"
    >
      <component
        :is="column"
        v-for="column in visibleColumns"
        :key="column.key"
      />
    </el-table>

    <!-- 分页组件 -->
    <div
      v-if="pagination"
      class="el-table-plus__pagination"
    >
      <el-pagination
        v-model:current-page="currentPage"
        v-model:page-size="currentPageSize"
        :total="total || data.length"
        :page-sizes="pageSizes"
        :layout="layout"
        background
      />
    </div>

    <!-- 列配置弹窗 -->
    <el-dialog
      v-model="dialogVisible"
      title="表格列配置"
      width="500px"
      :close-on-click-modal="false"
    >
      <!-- 弹窗提示文字 -->
      <p class="el-table-plus__dialog-tip">
        请选择需要在表格中显示的数据列
      </p>

      <!-- 列配置内容 -->
      <div class="el-table-plus__column-config">
        <!-- 全选行 -->
        <div class="el-table-plus__column-select-all">
          <el-checkbox
            v-model="isAllSelected"
          >
            全选
          </el-checkbox>
        </div>

        <!-- 分隔线 -->
        <div class="el-table-plus__column-divider"></div>

        <!-- 列选项网格 -->
        <div class="el-table-plus__column-grid">
          <div
            v-for="column in pendingColumnOptions"
            :key="column.key"
            class="el-table-plus__column-grid-item"
          >
            <el-checkbox
              v-model="column.visible"
              :disabled="column.disabled"
            >
              {{ column.label }}
            </el-checkbox>
          </div>
        </div>
      </div>

      <!-- 弹窗底部按钮 -->
      <template #footer>
        <div class="el-table-plus__dialog-footer">
          <el-button @click="cancelColumnConfig">
            取消
          </el-button>

          <el-button
            type="primary"
            @click="confirmColumnConfig"
          >
            确认
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
/**
 * 主容器样式
 */
.el-table-plus {
  width: 100%;
}

/**
 * 工具栏样式
 */
.el-table-plus__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.el-table-plus__toolbar-left,
.el-table-plus__toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

/**
 * 分页组件样式
 */
.el-table-plus__pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}

/**
 * 弹窗提示文字样式
 */
.el-table-plus__dialog-tip {
  font-size: 14px;
  color: #909399;
  margin-bottom: 16px;
  margin-top: 8px;
}

/**
 * 列配置容器样式
 */
.el-table-plus__column-config {
  border: 1px solid #EBEEF5;
  border-radius: 4px;
  padding: 12px;
}

/**
 * 全选行样式
 */
.el-table-plus__column-select-all {
  padding: 8px 0;
}

/**
 * 分隔线样式
 */
.el-table-plus__column-divider {
  height: 1px;
  background-color: #EBEEF5;
  margin: 8px 0;
}

/**
 * 列选项网格布局样式
 */
.el-table-plus__column-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 8px 0;
  padding: 8px 0;
}

/**
 * 网格项样式
 */
.el-table-plus__column-grid-item {
  display: flex;
  align-items: center;
}

/**
 * 弹窗底部按钮容器样式
 */
.el-table-plus__dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
}

/**
 * 弹窗底部取消按钮样式
 */
.el-table-plus__dialog-footer .el-button:first-child {
  background-color: #F5F7FA;
  color: #606266;
  border-color: #D9D9D9;
}

.el-table-plus__dialog-footer .el-button:first-child:hover {
  background-color: #E4E7ED;
  border-color: #C0C4CC;
}

/**
 * 弹窗底部确认按钮样式
 */
.el-table-plus__dialog-footer .el-button--primary {
  background-color: #1890FF;
  border-color: #1890FF;
  color: #FFFFFF;
}

.el-table-plus__dialog-footer .el-button--primary:hover {
  background-color: #40A9FF;
  border-color: #40A9FF;
}
</style>