# useTableColumnControl Hook 使用文档

## 一、简介

`useTableColumnControl` 是一个用于统一管理表格列显示/隐藏和排序功能的 Vue 3 Composition API Hook。它封装了列配置的加载、保存、同步等逻辑，让开发者只需几行代码即可实现完整的表格列控制功能。

### 主要功能
- ✅ 列显示/隐藏控制
- ✅ 列排序管理
- ✅ 配置自动保存到本地存储
- ✅ 配置同步到后端服务器
- ✅ 自动加载已保存的配置
- ✅ 类型安全支持

---

## 二、安装和引入

### 文件位置
```
src/hooks/tableColumn.ts
```

### 引入方式
```typescript
import { useTableColumnControl, type TableColumn } from '/@/hooks/tableColumn'
```

---

## 三、基础使用

### 1. 引入公共样式（推荐）

首先在 `<style>` 中引入现代化页面布局样式：

```vue
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>
```

### 2. 定义表格列配置

```typescript
import { User, Calendar, Phone } from '@element-plus/icons-vue'

const tableColumns = [
  { prop: 'name', label: '姓名', icon: User },
  { prop: 'age', label: '年龄', icon: Calendar },
  { prop: 'phone', label: '电话', icon: Phone }
]
```

### 3. 使用 Hook

```typescript
const {
  visibleColumns,
  visibleColumnsSorted,
  checkColumnVisible,
  handleColumnChange,
  handleColumnOrderChange
} = useTableColumnControl(tableColumns)
```

### 4. 在模板中使用

```vue
<template>
  <div class="modern-page-container">
    <div class="page-wrapper">
      <!-- 搜索表单卡片 -->
      <el-card v-show="showSearch" class="search-card" shadow="never">
        <template #header>
          <div class="card-header">
            <span class="card-title">
              <el-icon class="title-icon"><Search /></el-icon>
              筛选条件
            </span>
          </div>
        </template>
        <el-form class="search-form">
          <!-- 搜索表单项 -->
        </el-form>
      </el-card>

      <!-- 内容卡片 -->
      <el-card class="content-card" shadow="never">
        <template #header>
          <div class="card-header">
            <span class="card-title">
              <el-icon class="title-icon"><Document /></el-icon>
              数据列表
            </span>
            <div class="header-actions">
              <!-- TableColumnControl 组件 -->
              <TableColumnControl
                :columns="tableColumns"
                v-model="visibleColumns"
                @change="handleColumnChange"
                @order-change="handleColumnOrderChange"
              />
            </div>
          </div>
        </template>

        <!-- 表格 -->
        <el-table :data="dataList" stripe class="modern-table">
          <el-table-column type="index" label="序号" width="70">
            <template #default="{ $index }">
              {{ $index + 1 + ((pagination?.current || 1) - 1) * (pagination?.size || 10) }}
            </template>
          </el-table-column>
          
          <!-- 动态渲染列 -->
          <template v-for="col in visibleColumnsSorted" :key="col.prop">
            <el-table-column
              v-if="checkColumnVisible(col.prop || '')"
              :prop="col.prop"
              :label="col.label"
              :width="col.width"
              :min-width="col.minWidth"
            >
              <template #header>
                <el-icon v-if="col.icon">
                  <component :is="col.icon" />
                </el-icon>
                <span style="margin-left: 4px">{{ col.label }}</span>
              </template>
            </el-table-column>
          </template>
        </el-table>

        <!-- 分页 -->
        <div class="pagination-wrapper">
          <pagination v-bind="pagination" />
        </div>
      </el-card>
    </div>
  </div>
</template>
```

---

## 四、完整示例

```vue
<template>
  <div>
    <right-toolbar>
      <TableColumnControl
        ref="columnControlRef"
        :columns="tableColumns"
        v-model="visibleColumns"
        trigger-type="default"
        trigger-circle
        @change="handleColumnChange"
        @order-change="handleColumnOrderChange"
      >
        <template #trigger>
          <el-tooltip content="列设置" placement="top">
            <el-button circle>
              <el-icon><Menu /></el-icon>
            </el-button>
          </el-tooltip>
        </template>
      </TableColumnControl>
    </right-toolbar>

    <el-table :data="state.dataList" stripe>
      <el-table-column type="index" label="序号" width="70">
        <template #default="{ $index }">
          {{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
        </template>
      </el-table-column>

      <template v-for="col in visibleColumnsSorted" :key="col.prop">
        <el-table-column
          v-if="checkColumnVisible(col.prop || '')"
          :prop="col.prop"
          :label="col.label"
          :width="col.width"
          :min-width="col.minWidth"
          show-overflow-tooltip
        >
          <template #header>
            <el-icon v-if="col.icon">
              <component :is="col.icon" />
            </el-icon>
            <span style="margin-left: 4px">{{ col.label }}</span>
          </template>
        </el-table-column>
      </template>

      <el-table-column label="操作" width="180" fixed="right">
        <!-- 操作列内容 -->
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useTableColumnControl } from '/@/hooks/tableColumn'
import { User, Calendar, Phone, Menu } from '@element-plus/icons-vue'
import TableColumnControl from '/@/components/TableColumnControl/index.vue'

// 定义表格列
const tableColumns = [
  { prop: 'name', label: '姓名', icon: User },
  { prop: 'age', label: '年龄', icon: Calendar },
  { prop: 'phone', label: '电话', icon: Phone }
]

// 使用 Hook
const {
  visibleColumns,
  visibleColumnsSorted,
  checkColumnVisible,
  handleColumnChange,
  handleColumnOrderChange
} = useTableColumnControl(tableColumns)

const columnControlRef = ref()
const state = ref({
  dataList: [],
  pagination: { current: 1, size: 10 }
})
</script>
```

---

## 五、API 参考

### TableColumn 类型定义

```typescript
interface TableColumn {
  prop?: string              // 列属性名
  label?: string            // 列标题
  icon?: any                // 列图标组件
  width?: number | string   // 列宽度
  minWidth?: number | string // 最小宽度
  showOverflowTooltip?: boolean // 是否显示溢出提示
  align?: 'left' | 'center' | 'right' // 对齐方式
  alwaysShow?: boolean      // 是否始终显示（不参与列控制）
  fixed?: boolean | 'left' | 'right' // 是否固定列
  [key: string]: any       // 其他自定义属性
}
```

### Hook 返回值

| 属性/方法 | 类型 | 说明 |
|----------|------|------|
| `visibleColumns` | `Ref<string[]>` | 当前可见的列 key 数组 |
| `columnOrder` | `Ref<string[]>` | 列排序顺序数组 |
| `visibleColumnsSorted` | `ComputedRef<TableColumn[]>` | 排序后的可见列配置数组 |
| `checkColumnVisible` | `(prop: string) => boolean` | 检查列是否可见 |
| `handleColumnChange` | `(value: string[]) => void` | 处理列显示变化 |
| `handleColumnOrderChange` | `(order: string[]) => void` | 处理列排序变化 |
| `loadSavedConfig` | `() => void` | 手动加载保存的配置 |

### Hook 选项参数

```typescript
interface Options {
  autoLoad?: boolean        // 是否自动加载配置，默认 true
  storageKey?: string       // 自定义存储 key，默认使用路由路径
}
```

---

## 六、高级用法

### 1. 自定义存储 Key

如果不想使用默认的路由路径作为存储 key，可以自定义：

```typescript
const {
  visibleColumns,
  visibleColumnsSorted,
  checkColumnVisible,
  handleColumnChange,
  handleColumnOrderChange
} = useTableColumnControl(tableColumns, {
  storageKey: 'custom-table-columns-key'
})
```

### 2. 禁用自动加载

如果需要在特定时机手动加载配置：

```typescript
const {
  visibleColumns,
  visibleColumnsSorted,
  checkColumnVisible,
  handleColumnChange,
  handleColumnOrderChange,
  loadSavedConfig
} = useTableColumnControl(tableColumns, {
  autoLoad: false
})

// 在需要的时候手动加载
onMounted(() => {
  loadSavedConfig()
})
```

### 3. 固定列和始终显示的列

某些列（如操作列）不应该参与列控制：

```typescript
const tableColumns = [
  { prop: 'name', label: '姓名', icon: User },
  { prop: 'age', label: '年龄', icon: Calendar },
  { 
    prop: 'action', 
    label: '操作', 
    alwaysShow: true,  // 始终显示
    fixed: 'right'     // 固定在右侧
  }
]
```

### 4. 特殊列模板

如果需要为某些列添加特殊模板：

```vue
<template v-for="col in visibleColumnsSorted" :key="col.prop">
  <el-table-column
    v-if="checkColumnVisible(col.prop || '')"
    :prop="col.prop"
    :label="col.label"
  >
    <!-- 状态列特殊模板 -->
    <template v-if="col.prop === 'status'" #default="scope">
      <el-tag :type="scope.row.status === '1' ? 'success' : 'warning'">
        {{ scope.row.status }}
      </el-tag>
    </template>
    
    <!-- 其他列默认显示 -->
    <template v-else #default="scope">
      {{ scope.row[col.prop] }}
    </template>
  </el-table-column>
</template>
```

---

## 七、注意事项

### 1. 列 Key 的唯一性

确保每列的 `prop` 或 `label` 是唯一的，因为 Hook 使用它们作为标识：

```typescript
// ✅ 正确
const tableColumns = [
  { prop: 'name', label: '姓名' },
  { prop: 'age', label: '年龄' }
]

// ❌ 错误：两个列都没有 prop
const tableColumns = [
  { label: '姓名' },
  { label: '年龄' }
]
```

### 2. 响应式更新

`tableColumns` 应该是响应式的，如果需要在运行时动态修改列配置：

```typescript
const tableColumns = ref([
  { prop: 'name', label: '姓名' },
  { prop: 'age', label: '年龄' }
])

// 动态添加列
tableColumns.value.push({ prop: 'phone', label: '电话' })
```

### 3. 配置存储位置

- **本地存储**：使用 `localStorage`，key 为 `user-table-configs-all`
- **后端存储**：通过 API 同步，key 为 `user-table-configs`
- **页面存储**：每个页面的配置通过路由路径生成唯一 key

### 4. 配置加载时机

Hook 默认在组件挂载时自动加载配置。如果需要在数据加载后重新加载：

```typescript
const { loadSavedConfig } = useTableColumnControl(tableColumns)

watch(() => someData.value, () => {
  // 数据变化后重新加载配置
  loadSavedConfig()
})
```

---

## 八、扩展方式

### 1. 添加列宽保存功能

如果需要保存列宽，可以扩展 Hook：

```typescript
// 扩展 Hook
function useTableColumnControlWithWidth(tableColumns: TableColumn[]) {
  const baseHook = useTableColumnControl(tableColumns)
  const columnWidths = ref<Record<string, number>>({})

  const handleColumnWidthChange = (prop: string, width: number) => {
    columnWidths.value[prop] = width
    // 保存到本地存储
    const storageKey = getStorageKey()
    const config = getTableConfigFromLocal(storageKey) || {}
    config.columnWidths = columnWidths.value
    saveTableConfigToLocal(storageKey, config)
  }

  return {
    ...baseHook,
    columnWidths,
    handleColumnWidthChange
  }
}
```

### 2. 添加列固定功能

如果需要保存列的固定状态：

```typescript
function useTableColumnControlWithFixed(tableColumns: TableColumn[]) {
  const baseHook = useTableColumnControl(tableColumns)
  const fixedColumns = ref<string[]>([])

  const toggleColumnFixed = (prop: string) => {
    const index = fixedColumns.value.indexOf(prop)
    if (index > -1) {
      fixedColumns.value.splice(index, 1)
    } else {
      fixedColumns.value.push(prop)
    }
    // 保存配置
  }

  return {
    ...baseHook,
    fixedColumns,
    toggleColumnFixed
  }
}
```

### 3. 添加列分组功能

如果需要按分组管理列：

```typescript
function useTableColumnControlWithGroup(
  tableColumns: TableColumn[],
  groups: Record<string, string[]>
) {
  const baseHook = useTableColumnControl(tableColumns)
  
  const getColumnsByGroup = (groupName: string) => {
    return tableColumns.filter(col => 
      groups[groupName]?.includes(col.prop || '')
    )
  }

  return {
    ...baseHook,
    getColumnsByGroup
  }
}
```

### 4. 自定义存储策略

如果需要使用不同的存储策略（如 IndexedDB）：

```typescript
function useTableColumnControlWithCustomStorage(
  tableColumns: TableColumn[],
  storageAdapter: {
    get: (key: string) => Promise<any>
    set: (key: string, value: any) => Promise<void>
  }
) {
  // 实现自定义存储逻辑
}
```

---

## 九、常见问题

### Q1: 为什么配置没有保存？

**A:** 检查以下几点：
1. 确保 `handleColumnChange` 和 `handleColumnOrderChange` 已正确绑定到组件
2. 检查浏览器控制台是否有错误
3. 确认 API 调用是否成功（检查网络请求）

### Q2: 为什么某些列无法隐藏？

**A:** 检查列配置中是否设置了 `alwaysShow: true` 或 `fixed` 属性：

```typescript
// 这些列无法隐藏
{ prop: 'action', label: '操作', alwaysShow: true }
{ prop: 'id', label: 'ID', fixed: 'left' }
```

### Q3: 如何重置列配置？

**A:** 可以手动清除本地存储或调用 API 删除配置：

```typescript
// 清除本地存储
localStorage.removeItem('user-table-configs-all')

// 或通过 API 删除
import { deleteUserTableConfig } from '/@/api/admin/usertable'
deleteUserTableConfig(storageKey)
```

### Q4: 如何在多个页面共享列配置？

**A:** 使用相同的 `storageKey`：

```typescript
const { visibleColumns } = useTableColumnControl(tableColumns, {
  storageKey: 'shared-table-columns'
})
```

---

## 十、最佳实践

### 1. 列配置管理

将列配置提取到单独的文件中，便于维护：

```typescript
// src/views/xxx/columns.ts
import { User, Calendar } from '@element-plus/icons-vue'

export const tableColumns = [
  { prop: 'name', label: '姓名', icon: User },
  { prop: 'age', label: '年龄', icon: Calendar }
]
```

### 2. 类型定义

为列配置添加类型约束：

```typescript
import type { TableColumn } from '/@/hooks/tableColumn'

const tableColumns: TableColumn[] = [
  { prop: 'name', label: '姓名', icon: User }
]
```

### 3. 统一命名

保持列 `prop` 与后端字段名一致，便于数据处理。

### 4. 性能优化

对于大量列的表格，考虑使用虚拟滚动或分页加载列配置。

---

## 十一、迁移指南

### 从旧实现迁移到 Hook

**旧代码（~90 行）：**
```typescript
const visibleColumns = ref<string[]>([])
const columnOrder = ref<string[]>([])
const loadSavedConfig = () => { /* ... */ }
const handleColumnChange = () => { /* ... */ }
// ... 更多代码
```

**新代码（~5 行）：**
```typescript
import { useTableColumnControl } from '/@/hooks/tableColumn'

const {
  visibleColumns,
  visibleColumnsSorted,
  checkColumnVisible,
  handleColumnChange,
  handleColumnOrderChange
} = useTableColumnControl(tableColumns)
```

### 迁移步骤

1. 引入 Hook
2. 删除旧的列控制逻辑（`loadSavedConfig`、`handleColumnChange` 等）
3. 使用 Hook 返回值替换原有变量
4. 测试功能是否正常

---

## 十二、更新日志

### v1.0.0 (2024-01-XX)
- ✅ 初始版本发布
- ✅ 支持列显示/隐藏控制
- ✅ 支持列排序管理
- ✅ 支持本地存储和服务器同步
- ✅ 支持自动加载配置

---

## 十三、相关资源

- **Hook 源码**：`src/hooks/tableColumn.ts`
- **API 接口**：`src/api/admin/usertable.ts`
- **组件源码**：`src/components/TableColumnControl/index.vue`
- **示例页面**：`src/views/stuwork/classmasterresume/index.vue`

---

## 十四、贡献指南

如果发现 bug 或有改进建议，请：
1. 提交 Issue 描述问题
2. 提交 Pull Request 提供修复方案
3. 更新本文档说明变更

---

**最后更新：2024-01-XX**

