Browse Source

docs:表格列配置实现文档

sw 4 days ago
parent
commit
d3143cf680
1 changed files with 967 additions and 0 deletions
  1. 967 0
      docs/实现/表格列配置功能.md

+ 967 - 0
docs/实现/表格列配置功能.md

@@ -0,0 +1,967 @@
+# 表格列配置功能 - 实现文档
+
+## 1. 概述
+
+本文档记录了 Worklist 和 History 等页面表格的列配置功能实现,支持通过配置信息动态控制表格显示的列,而不是显示所有35个列。
+
+## 2. 需求背景
+
+### 2.1 业务场景
+
+在 Worklist 和 History 页面,表格默认显示所有35个列,导致:
+- 表格过宽,需要大量横向滚动
+- 用户难以快速找到关键信息
+- 不同用户对列的需求不同
+- 无法根据业务场景灵活调整
+
+### 2.2 核心需求
+
+**主要功能**:
+- ✅ 支持配置哪些列显示、哪些列隐藏
+- ✅ 支持配置列的显示顺序
+- ✅ 支持配置列的宽度
+- ✅ 支持从远程 API 获取配置
+- ✅ API 失败时自动回退到本地默认配置
+- ✅ 不同表格(worklist/history)使用独立配置
+- ✅ 向后兼容:无配置时显示所有列
+
+**技术要求**:
+- 配置可来自本地硬编码或远程 API
+- 远程配置失败时有回退机制
+- 配置通过 props 传递给表格组件
+- 支持多表格独立配置
+
+---
+
+## 3. 架构设计
+
+### 3.1 端口适配器模式(Port-Adapter Pattern)
+
+采用端口适配器模式实现灵活的配置源切换:
+
+```
+┌─────────────────────────────────────────────┐
+│         配置源(可切换)                      │
+│  ┌──────────────┐    ┌──────────────┐       │
+│  │ 本地硬编码    │    │  远程 API    │       │
+│  └──────────────┘    └──────────────┘       │
+└─────────────┬──────────────┬────────────────┘
+              │              │
+              ↓              ↓
+    ┌──────────────────────────────────┐
+    │  IColumnConfigProvider (端口)    │
+    │  - getColumnConfig()            │
+    │  - getAllColumnConfigs()        │
+    │  - isAvailable()                │
+    └──────────────────────────────────┘
+              ↓
+    ┌──────────────────────────────────┐
+    │  ColumnConfigService (领域服务)   │
+    │  - 策略模式                      │
+    │  - 主提供者 + 回退提供者          │
+    │  - 自动切换                      │
+    └──────────────────────────────────┘
+              ↓
+    ┌──────────────────────────────────┐
+    │  父组件 (worklist.tsx)            │
+    │  - 获取配置                      │
+    │  - 通过 props 传递               │
+    └──────────────────────────────────┘
+              ↓
+    ┌──────────────────────────────────┐
+    │  WorklistTable 组件               │
+    │  - 根据配置过滤列                │
+    │  - 根据配置排序列                │
+    │  - 应用列宽                      │
+    └──────────────────────────────────┘
+```
+
+### 3.2 数据流
+
+```
+应用启动
+    ↓
+父组件 useEffect
+    ↓
+columnConfigService.getColumnConfig('worklist')
+    ↓
+    ├─→ 尝试主提供者 (RemoteColumnConfigAdapter)
+    │   ├─→ 成功 → 返回远程配置
+    │   └─→ 失败 ↓
+    │
+    └─→ 回退提供者 (LocalColumnConfigAdapter)
+        └─→ 返回本地配置
+    ↓
+父组件接收配置 → setState
+    ↓
+通过 props 传递给 WorklistTable
+    ↓
+WorklistTable.visibleColumns (useMemo)
+    ├─→ 配置为空 → 显示所有35列
+    └─→ 配置不为空 → 过滤 + 排序 + 应用宽度
+    ↓
+渲染表格
+```
+
+---
+
+## 4. 技术实现
+
+### 4.1 类型定义
+
+**文件**:`src/config/tableColumns/types/columnConfig.ts`
+
+```typescript
+/**
+ * 单个列的配置
+ */
+export interface ColumnConfig {
+  key: string;           // 列标识(对应 dataIndex)
+  visible: boolean;      // 是否显示
+  order: number;         // 显示顺序
+  width?: number;        // 列宽(可选)
+  fixed?: 'left' | 'right'; // 固定列(可选)
+}
+
+/**
+ * 表格名称类型
+ */
+export type TableName = 'worklist' | 'history' | 'archive' | 'output';
+
+/**
+ * 表格列配置
+ */
+export interface TableColumnConfig {
+  tableName: TableName;
+  columns: ColumnConfig[];
+  version?: string;      // 配置版本
+  updatedAt?: string;    // 更新时间
+}
+
+/**
+ * 完整的配置响应
+ */
+export interface ColumnConfigResponse {
+  data: TableColumnConfig[];
+  success: boolean;
+  message?: string;
+}
+```
+
+### 4.2 端口接口
+
+**文件**:`src/config/tableColumns/ports/IColumnConfigProvider.ts`
+
+```typescript
+import { TableColumnConfig, TableName } from '../types/columnConfig';
+
+export interface IColumnConfigProvider {
+  /**
+   * 获取指定表格的列配置
+   */
+  getColumnConfig(tableName: TableName): Promise<TableColumnConfig>;
+
+  /**
+   * 获取所有表格的配置
+   */
+  getAllColumnConfigs(): Promise<TableColumnConfig[]>;
+
+  /**
+   * 检查提供者是否可用
+   */
+  isAvailable(): Promise<boolean>;
+}
+```
+
+### 4.3 本地适配器实现
+
+**文件**:`src/config/tableColumns/adapters/LocalColumnConfigAdapter.ts`
+
+```typescript
+export class LocalColumnConfigAdapter implements IColumnConfigProvider {
+  private readonly defaultConfigs: Map<TableName, TableColumnConfig>;
+
+  constructor() {
+    this.defaultConfigs = new Map([
+      // worklist 的默认列配置
+      ['worklist', {
+        tableName: 'worklist',
+        columns: [
+          { key: 'PatientID', visible: true, order: 1, width: 120 },
+          { key: 'PatientName', visible: true, order: 2, width: 150 },
+          { key: 'StudyID', visible: true, order: 3, width: 120 },
+          { key: 'AccessionNumber', visible: true, order: 4, width: 150 },
+          { key: 'StudyStatus', visible: true, order: 5, width: 100 },
+          { key: 'Modality', visible: true, order: 6, width: 100 },
+          { key: 'StudyStartDatetime', visible: true, order: 7, width: 180 },
+          { key: 'PatientAge', visible: true, order: 8, width: 80 },
+          { key: 'PatientSex', visible: true, order: 9, width: 80 },
+          // 其他列默认隐藏 (visible: false)
+        ],
+        version: '1.0.0',
+      }],
+      // history 的默认列配置
+      ['history', {
+        tableName: 'history',
+        columns: [
+          { key: 'StudyID', visible: true, order: 1, width: 120 },
+          { key: 'PatientName', visible: true, order: 2, width: 150 },
+          { key: 'StudyDescription', visible: true, order: 3, width: 200 },
+          { key: 'StudyStartDatetime', visible: true, order: 4, width: 180 },
+          { key: 'IsExported', visible: true, order: 5, width: 100 },
+          { key: 'StudyStatus', visible: true, order: 6, width: 100 },
+        ],
+        version: '1.0.0',
+      }],
+    ]);
+  }
+
+  async getColumnConfig(tableName: TableName): Promise<TableColumnConfig> {
+    const config = this.defaultConfigs.get(tableName);
+    if (!config) {
+      throw new Error(`No default config found for table: ${tableName}`);
+    }
+    return Promise.resolve(config);
+  }
+
+  async getAllColumnConfigs(): Promise<TableColumnConfig[]> {
+    return Promise.resolve(Array.from(this.defaultConfigs.values()));
+  }
+
+  async isAvailable(): Promise<boolean> {
+    return Promise.resolve(true); // 本地配置总是可用
+  }
+}
+```
+
+**设计特点**:
+- 使用 Map 存储多表格配置
+- 默认显示9个核心列
+- 本地配置总是可用(isAvailable 返回 true)
+
+### 4.4 远程适配器实现
+
+**文件**:`src/config/tableColumns/adapters/RemoteColumnConfigAdapter.ts`
+
+```typescript
+export class RemoteColumnConfigAdapter implements IColumnConfigProvider {
+  private configCache: Map<TableName, TableColumnConfig> = new Map();
+  private cacheExpiry: number = 5 * 60 * 1000; // 5分钟缓存
+  private lastFetchTime: number = 0;
+
+  async getColumnConfig(tableName: TableName): Promise<TableColumnConfig> {
+    // 如果缓存有效,直接返回
+    if (this.isCacheValid() && this.configCache.has(tableName)) {
+      return this.configCache.get(tableName)!;
+    }
+
+    // 否则从API获取
+    await this.fetchAndCacheConfigs();
+    
+    const config = this.configCache.get(tableName);
+    if (!config) {
+      throw new Error(`No config found for table: ${tableName}`);
+    }
+    return config;
+  }
+
+  async getAllColumnConfigs(): Promise<TableColumnConfig[]> {
+    if (!this.isCacheValid()) {
+      await this.fetchAndCacheConfigs();
+    }
+    return Array.from(this.configCache.values());
+  }
+
+  async isAvailable(): Promise<boolean> {
+    try {
+      await this.fetchAndCacheConfigs();
+      return true;
+    } catch (error) {
+      console.error('Remote config provider unavailable:', error);
+      return false;
+    }
+  }
+
+  private async fetchAndCacheConfigs(): Promise<void> {
+    try {
+      const response = await fetchTableColumnConfig();
+      
+      if (response.success && response.data) {
+        this.configCache.clear();
+        response.data.forEach(config => {
+          this.configCache.set(config.tableName, config);
+        });
+        this.lastFetchTime = Date.now();
+      }
+    } catch (error) {
+      throw new Error(`Failed to fetch remote config: ${error}`);
+    }
+  }
+
+  private isCacheValid(): boolean {
+    return Date.now() - this.lastFetchTime < this.cacheExpiry;
+  }
+}
+```
+
+**设计特点**:
+- 实现5分钟缓存机制,减少 API 调用
+- isAvailable 通过尝试获取配置来判断可用性
+- 失败时抛出错误,触发回退机制
+
+### 4.5 API 调用
+
+**文件**:`src/API/tableColumnConfig.ts`
+
+```typescript
+import axios from './interceptor';
+import { ColumnConfigResponse } from '../config/tableColumns/types/columnConfig';
+
+/**
+ * 获取表格列配置
+ */
+export async function fetchTableColumnConfig(): Promise<ColumnConfigResponse> {
+  const response = await axios.get<ColumnConfigResponse>(
+    '/api/config/table-columns'
+  );
+  return response.data;
+}
+```
+
+**API 端点**:`GET /api/config/table-columns`
+
+**响应格式**:
+```json
+{
+  "success": true,
+  "data": [
+    {
+      "tableName": "worklist",
+      "columns": [
+        { "key": "PatientID", "visible": true, "order": 1, "width": 120 },
+        { "key": "PatientName", "visible": true, "order": 2, "width": 150 }
+      ],
+      "version": "1.0.0",
+      "updatedAt": "2025-10-07T10:00:00Z"
+    }
+  ]
+}
+```
+
+### 4.6 领域服务(策略模式 + 回退机制)
+
+**文件**:`src/config/tableColumns/domain/ColumnConfigService.ts`
+
+```typescript
+export class ColumnConfigService {
+  private primaryProvider: IColumnConfigProvider;
+  private fallbackProvider: IColumnConfigProvider;
+
+  constructor(
+    primaryProvider?: IColumnConfigProvider,
+    fallbackProvider?: IColumnConfigProvider
+  ) {
+    // 默认:优先使用远程,回退到本地
+    this.primaryProvider = primaryProvider || new RemoteColumnConfigAdapter();
+    this.fallbackProvider = fallbackProvider || new LocalColumnConfigAdapter();
+  }
+
+  /**
+   * 获取表格列配置
+   * 优先使用主提供者,失败则回退到备用提供者
+   */
+  async getColumnConfig(tableName: TableName): Promise<TableColumnConfig> {
+    try {
+      if (await this.primaryProvider.isAvailable()) {
+        return await this.primaryProvider.getColumnConfig(tableName);
+      }
+    } catch (error) {
+      console.warn('Primary provider failed, using fallback:', error);
+    }
+
+    // 回退到备用提供者
+    return await this.fallbackProvider.getColumnConfig(tableName);
+  }
+
+  /**
+   * 切换提供者
+   */
+  switchProvider(provider: IColumnConfigProvider): void {
+    this.primaryProvider = provider;
+  }
+}
+
+// 导出单例
+export const columnConfigService = new ColumnConfigService();
+```
+
+**设计特点**:
+- 策略模式:可运行时切换提供者
+- 回退机制:主提供者失败自动使用备用
+- 单例导出:全局共享服务实例
+
+### 4.7 导出模块
+
+**文件**:`src/config/tableColumns/index.ts`
+
+```typescript
+// 类型定义
+export * from './types/columnConfig';
+
+// 端口接口
+export * from './ports/IColumnConfigProvider';
+
+// 适配器
+export * from './adapters/LocalColumnConfigAdapter';
+export * from './adapters/RemoteColumnConfigAdapter';
+
+// 领域服务
+export * from './domain/ColumnConfigService';
+export { columnConfigService } from './domain/ColumnConfigService';
+```
+
+---
+
+## 5. 组件改造
+
+### 5.1 WorklistTable 组件
+
+**文件**:`src/pages/patient/components/WorklistTable.tsx`
+
+#### 新增接口定义
+
+```typescript
+interface WorklistTableProps {
+  columnConfig?: ColumnConfig[]; // ⭐ 新增:列配置(可选)
+  worklistData: Task[];
+  filters?: WorkFilter;
+  page?: number;
+  pageSize?: number;
+  selectedIds: string[];
+  handleRowClick: (record: Task) => void;
+  handleRowDoubleClick: (record: Task) => void;
+}
+```
+
+#### 核心逻辑:根据配置过滤和排序列
+
+```typescript
+const WorklistTable: React.FC<WorklistTableProps> = ({
+  columnConfig = [], // 接收配置,默认为空数组
+  worklistData,
+  selectedIds,
+  handleRowClick,
+  handleRowDoubleClick,
+}) => {
+  // 根据传入的配置过滤和排序列
+  const visibleColumns = useMemo(() => {
+    // 如果没有配置,显示所有列(保持当前行为)
+    if (columnConfig.length === 0) {
+      return columnsDef.map(col => ({
+        ...col,
+        width: 150,
+      }));
+    }
+
+    // 根据配置过滤出可见的列
+    return columnsDef
+      .filter(col => {
+        const config = columnConfig.find(c => c.key === col.dataIndex);
+        return config?.visible ?? false;
+      })
+      .map(col => {
+        const config = columnConfig.find(c => c.key === col.dataIndex);
+        return {
+          ...col,
+          width: config?.width ?? 150,
+        };
+      })
+      .sort((a, b) => {
+        const orderA = columnConfig.find(c => c.key === a.dataIndex)?.order ?? 999;
+        const orderB = columnConfig.find(c => c.key === b.dataIndex)?.order ?? 999;
+        return orderA - orderB;
+      });
+  }, [columnConfig]);
+
+  // 列可调整大小的逻辑
+  const [columns, setColumns] = useState<TableColumnsType<DataType>>(visibleColumns);
+
+  useEffect(() => {
+    setColumns(visibleColumns);
+  }, [visibleColumns]);
+
+  // ... 其他代码
+};
+```
+
+**关键点**:
+- 使用 `useMemo` 优化性能,只在配置变化时重新计算
+- 支持向后兼容:配置为空时显示所有列
+- 三步处理:过滤 → 应用宽度 → 排序
+
+### 5.2 父组件集成
+
+**文件**:`src/pages/patient/worklist.tsx`
+
+#### 导入依赖
+
+```typescript
+import { columnConfigService } from '@/config/tableColumns';
+import { ColumnConfig } from '@/config/tableColumns/types/columnConfig';
+```
+
+#### 状态管理
+
+```typescript
+const WorklistPage: React.FC = () => {
+  const [columnConfig, setColumnConfig] = useState<ColumnConfig[]>([]); // 列配置状态
+
+  // 获取列配置
+  useEffect(() => {
+    columnConfigService
+      .getColumnConfig('worklist')
+      .then(config => {
+        setColumnConfig(config.columns);
+      })
+      .catch(error => {
+        console.error('Failed to load worklist column config:', error);
+        // 失败时使用空配置,表格会显示所有列
+        setColumnConfig([]);
+      });
+  }, []);
+
+  return (
+    <WorklistTable
+      columnConfig={columnConfig} // ⭐ 传递配置
+      worklistData={worklistData}
+      selectedIds={selectedIds}
+      handleRowClick={handleRowClick}
+      handleRowDoubleClick={handleRowDoubleClick}
+    />
+  );
+};
+```
+
+---
+
+## 6. 完整的 35 个列定义
+
+```typescript
+const columnsDef = [
+  { title: 'StudyInstanceUID', dataIndex: 'StudyInstanceUID' },
+  { title: 'StudyID', dataIndex: 'StudyID' },
+  { title: 'SpecificCharacterSet', dataIndex: 'SpecificCharacterSet' },
+  { title: 'AccessionNumber', dataIndex: 'AccessionNumber' },
+  { title: 'PatientID', dataIndex: 'PatientID' },
+  { title: 'PatientName', dataIndex: 'PatientName' },
+  { title: 'DisplayPatientName', dataIndex: 'DisplayPatientName' },
+  { title: 'PatientSize', dataIndex: 'PatientSize' },
+  { title: 'PatientAge', dataIndex: 'PatientAge' },
+  { title: 'PatientSex', dataIndex: 'PatientSex' },
+  { title: 'AdmittingTime', dataIndex: 'AdmittingTime' },
+  { title: 'RegSource', dataIndex: 'RegSource' },
+  { title: 'StudyStatus', dataIndex: 'StudyStatus' },
+  { title: 'RequestedProcedureID', dataIndex: 'RequestedProcedureID' },
+  { title: 'PerformedProtocolCodeValue', dataIndex: 'PerformedProtocolCodeValue' },
+  { title: 'PerformedProtocolCodeMeaning', dataIndex: 'PerformedProtocolCodeMeaning' },
+  { title: 'PerformedProcedureStepID', dataIndex: 'PerformedProcedureStepID' },
+  { title: 'StudyDescription', dataIndex: 'StudyDescription' },
+  { title: 'StudyStartDatetime', dataIndex: 'StudyStartDatetime' },
+  { title: 'ScheduledProcedureStepStartDate', dataIndex: 'ScheduledProcedureStepStartDate' },
+  { title: 'StudyLock', dataIndex: 'StudyLock' },
+  { title: 'OperatorID', dataIndex: 'OperatorID' },
+  { title: 'Modality', dataIndex: 'Modality' },
+  { title: 'Views', dataIndex: 'Views' },
+  { title: 'Thickness', dataIndex: 'Thickness' },
+  { title: 'PatientType', dataIndex: 'PatientType' },
+  { title: 'StudyType', dataIndex: 'StudyType' },
+  { title: 'QRCode', dataIndex: 'QRCode' },
+  { title: 'IsExported', dataIndex: 'IsExported' },
+  { title: 'IsEdited', dataIndex: 'IsEdited' },
+  { title: 'WorkRef', dataIndex: 'WorkRef' },
+  { title: 'IsAppended', dataIndex: 'IsAppended' },
+  { title: 'CreationTime', dataIndex: 'CreationTime' },
+  { title: 'MappedStatus', dataIndex: 'MappedStatus' },
+  { title: 'IsDelete', dataIndex: 'IsDelete' },
+];
+```
+
+---
+
+## 7. 测试方案
+
+详见:`docs/测试/表格列配置功能测试方案.md`
+
+### 7.1 核心测试场景
+
+1. **TC-WL-COL-01**: 使用本地配置显示列
+2. **TC-WL-COL-02**: 使用远程API配置显示列
+3. **TC-WL-COL-03**: API失败时回退到本地配置
+4. **TC-WL-COL-04**: 验证特定列的显示/隐藏
+5. **TC-WL-COL-05**: 验证列的显示顺序
+6. **TC-WL-COL-06**: 验证列宽配置
+7. **TC-WL-COL-07**: 无配置时显示所有列(向后兼容)
+
+### 7.2 E2E 测试文件
+
+- `cypress/e2e/patient/worklist/column-config.cy.ts`
+- `cypress/support/mock/handlers/columnConfig.ts`
+- `cypress/support/pageObjects/WorklistPage.ts`(扩展)
+
+---
+
+## 8. 相关文件清单
+
+### 8.1 新增文件(10个)
+
+| 文件路径 | 作用 |
+|---------|------|
+| `src/config/tableColumns/types/columnConfig.ts` | 类型定义 |
+| `src/config/tableColumns/ports/IColumnConfigProvider.ts` | 端口接口 |
+| `src/config/tableColumns/adapters/LocalColumnConfigAdapter.ts` | 本地适配器 |
+| `src/config/tableColumns/adapters/RemoteColumnConfigAdapter.ts` | 远程适配器 |
+| `src/config/tableColumns/domain/ColumnConfigService.ts` | 领域服务 |
+| `src/config/tableColumns/index.ts` | 导出模块 |
+| `src/API/tableColumnConfig.ts` | API调用 |
+| `cypress/support/mock/handlers/columnConfig.ts` | Mock handlers |
+| `cypress/e2e/patient/worklist/column-config.cy.ts` | E2E测试 |
+| `docs/测试/表格列配置功能测试方案.md` | 测试文档 |
+
+### 8.2 修改文件(3个)
+
+| 文件路径 | 修改内容 |
+|---------|---------|
+| `src/pages/patient/components/WorklistTable.tsx` | 添加 columnConfig prop,实现列过滤和排序逻辑 |
+| `src/pages/patient/worklist.tsx` | 获取列配置并传递给 WorklistTable |
+| `cypress/support/pageObjects/WorklistPage.ts` | 添加列相关测试方法 |
+
+---
+
+## 9. History 页面扩展指南
+
+要为 History 页面添加相同功能,只需:
+
+### 步骤 1:修改 HistoryList.tsx
+
+```typescript
+import { columnConfigService } from '@/config/tableColumns';
+import { ColumnConfig } from '@/config/tableColumns/types/columnConfig';
+
+const HistoryList: React.FC = () => {
+  const [columnConfig, setColumnConfig] = useState<ColumnConfig[]>([]);
+
+  useEffect(() => {
+    columnConfigService
+      .getColumnConfig('history')  // ⭐ 使用 'history'
+      .then(config => {
+        setColumnConfig(config.columns);
+      })
+      .catch(error => {
+        console.error('Failed to load history column config:', error);
+        setColumnConfig([]);
+      });
+  }, []);
+
+  return (
+    <WorklistTable  // 或者 HistoryTable
+      columnConfig={columnConfig}
+      // ... 其他 props
+    />
+  );
+};
+```
+
+### 步骤 2:配置默认列
+
+在 `LocalColumnConfigAdapter.ts` 中已经包含 history 的默认配置:
+
+```typescript
+['history', {
+  tableName: 'history',
+  columns: [
+    { key: 'StudyID', visible: true, order: 1, width: 120 },
+    { key: 'PatientName', visible: true, order: 2, width: 150 },
+    { key: 'StudyDescription', visible: true, order: 3, width: 200 },
+    { key: 'StudyStartDatetime', visible: true, order: 4, width: 180 },
+    { key: 'IsExported', visible: true, order: 5, width: 100 },
+    { key: 'StudyStatus', visible: true, order: 6, width: 100 },
+  ],
+  version: '1.0.0',
+}]
+```
+
+---
+
+## 10. 优势与特点
+
+### 10.1 架构优势
+
+1. **灵活切换**:端口适配器模式支持运行时切换配置源
+2. **容错机制**:远程配置失败自动回退到本地
+3. **独立配置**:不同表格使用各自的配置,互不影响
+4. **向后兼容**:无配置时显示所有列,不影响现有功能
+5. **易于扩展**:新增表格只需添加配置,无需修改核心代码
+
+### 10.2 性能优化
+
+1. **缓存机制**:远程配置缓存5分钟,减少 API 调用
+2. **useMemo**:列计算使用 useMemo,避免不必要的重渲染
+3. **按需加载**:只在组件初始化时获取配置
+
+### 10.3 用户体验
+
+1. **精简显示**:只显示关键列,提升可读性
+2. **自定义顺序**:重要列靠前显示
+3. **合适宽度**:每列设置合适宽度,减少调整需求
+4. **回退保障**:配置失败不影响使用
+
+---
+
+## 11. 注意事项
+
+### 11.1 配置格式要求
+
+- `key` 必须与 `columnsDef` 中的 `dataIndex` 完全匹配
+- `order` 应该从 1 开始连续编号
+- `visible: false` 的列不会显示,无论 order 值如何
+
+### 11.2 API 要求
+
+- 响应必须包含 `success` 和 `data` 字段
+- `data` 是数组,每个元素是一个表格的配置
+- 支持一次返回多个表格的配置
+
+### 11.3 向后兼容
+
+- 当 `columnConfig` 为空数组时,显示所有35个列
+- 这确保了在配置缺失或加载失败时,系统仍然可用
+
+### 11.4 缓存策略
+
+- 远程配置缓存5分钟
+- 如需立即更新配置,需要刷新页面或重启应用
+- 可根据业务需求调整 `cacheExpiry` 值
+
+---
+
+## 12. 未来改进建议
+
+### 12.1 用户自定义
+
+允许用户在 UI 中自定义列配置:
+- 拖拽调整列顺序
+- 点击切换列显示/隐藏
+- 调整列宽后保存到配置
+- 重置为默认配置
+
+### 12.2 配置管理界面
+
+提供独立的配置管理页面:
+- 可视化编辑列配置
+- 预览配置效果
+- 导入/导出配置文件
+- 版本控制
+
+### 12.3 更多配置项
+
+扩展配置支持:
+- 列的对齐方式(左对齐/居中/右对齐)
+- 列的数据格式化
+- 列的排序功能开关
+- 列的过滤功能开关
+- 固定列(fixed: 'left' | 'right')
+
+### 12.4 性能优化
+
+- 虚拟滚动:大量数据时只渲染可见行
+- 懒加载:分批加载数据
+- 请求合并:批量获取多个表格配置
+
+### 12.5 多租户支持
+
+- 不同用户/角色使用不同配置
+- 用户级别配置覆盖系统级别配置
+- 配置权限管理
+
+---
+
+## 13. 常见问题(FAQ)
+
+### Q1: 为什么选择端口适配器模式?
+
+**A**: 端口适配器模式提供了以下优势:
+1. **灵活性**:可以轻松切换配置源(本地/远程/数据库等)
+2. **可测试性**:可以注入 Mock 提供者进行单元测试
+3. **解耦**:业务逻辑不依赖具体的配置来源
+4. **扩展性**:新增配置源只需实现接口,无需修改现有代码
+
+### Q2: 为什么使用 props 传递配置而不是 Redux?
+
+**A**: 考虑因素:
+1. **职责分离**:配置获取是父组件的职责,表格组件只负责渲染
+2. **灵活性**:不同父组件可以传递不同的配置
+3. **测试简单**:测试时直接传入 Mock 配置即可
+4. **避免全局状态污染**:配置是组件级别的,不需要全局共享
+
+### Q3: 为什么有本地配置还需要远程配置?
+
+**A**: 两者各有用途:
+- **本地配置**:作为默认值和回退方案,确保系统在任何情况下都可用
+- **远程配置**:支持动态调整、多环境部署、用户自定义等高级功能
+
+### Q4: 如何添加新的表格配置?
+
+**A**: 只需两步:
+1. 在 `LocalColumnConfigAdapter.ts` 的 Map 中添加新表格的默认配置
+2. 在父组件中调用 `columnConfigService.getColumnConfig('新表格名')`
+
+### Q5: 配置失败会影响用户使用吗?
+
+**A**: 不会。系统有三层保障:
+1. 远程配置失败自动回退到本地配置
+2. 本地配置失败(理论上不会发生)返回空数组
+3. 空数组时表格显示所有列(向后兼容)
+
+---
+
+## 14. 参考资料
+
+### 14.1 设计模式
+
+- **端口适配器模式**(Ports and Adapters / Hexagonal Architecture)
+- **策略模式**(Strategy Pattern)
+- **单例模式**(Singleton Pattern)
+
+### 14.2 相关技术
+
+- [React useMemo](https://react.dev/reference/react/useMemo)
+- [TypeScript Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html)
+- [Ant Design Table](https://ant.design/components/table-cn)
+
+### 14.3 项目内部文档
+
+- 测试方案:`docs/测试/表格列配置功能测试方案.md`
+- 其他实现文档:`docs/实现/`
+
+---
+
+## 15. 更新记录
+
+| 日期 | 修改人 | 修改内容 |
+|------|--------|----------|
+| 2025/10/10 | - | 创建文档,完整记录表格列配置功能实现 |
+
+---
+
+## 附录 A:完整代码示例
+
+### A.1 使用示例
+
+```typescript
+// 在任何需要表格配置的页面中
+import { useState, useEffect } from 'react';
+import { columnConfigService } from '@/config/tableColumns';
+import { ColumnConfig } from '@/config/tableColumns/types/columnConfig';
+
+const MyTablePage: React.FC = () => {
+  const [columnConfig, setColumnConfig] = useState<ColumnConfig[]>([]);
+
+  useEffect(() => {
+    columnConfigService
+      .getColumnConfig('worklist') // 或 'history'
+      .then(config => setColumnConfig(config.columns))
+      .catch(error => {
+        console.error('Failed to load column config:', error);
+        setColumnConfig([]); // 使用空配置作为回退
+      });
+  }, []);
+
+  return (
+    <MyTable 
+      columnConfig={columnConfig}
+      // ... 其他 props
+    />
+  );
+};
+```
+
+### A.2 自定义提供者
+
+```typescript
+import { IColumnConfigProvider } from '@/config/tableColumns';
+
+// 创建数据库提供者
+class DatabaseColumnConfigAdapter implements IColumnConfigProvider {
+  async getColumnConfig(tableName: TableName): Promise<TableColumnConfig> {
+    // 从数据库获取配置
+    const config = await database.getTableConfig(tableName);
+    return config;
+  }
+
+  async getAllColumnConfigs(): Promise<TableColumnConfig[]> {
+    return await database.getAllTableConfigs();
+  }
+
+  async isAvailable(): Promise<boolean> {
+    return await database.isConnected();
+  }
+}
+
+// 使用自定义提供者
+const customService = new ColumnConfigService(
+  new DatabaseColumnConfigAdapter(),  // 主提供者
+  new LocalColumnConfigAdapter()      // 回退提供者
+);
+```
+
+---
+
+## 附录 B:配置示例
+
+### B.1 最小配置
+
+```typescript
+{
+  tableName: 'worklist',
+  columns: [
+    { key: 'PatientID', visible: true, order: 1 },
+    { key: 'PatientName', visible: true, order: 2 }
+  ]
+}
+```
+
+### B.2 完整配置
+
+```typescript
+{
+  tableName: 'worklist',
+  columns: [
+    { 
+      key: 'PatientID', 
+      visible: true, 
+      order: 1, 
+      width: 120,
+      fixed: 'left'
+    },
+    { 
+      key: 'PatientName', 
+      visible: true, 
+      order: 2, 
+      width: 150
+    },
+    { 
+      key: 'StudyStatus', 
+      visible: true, 
+      order: 3, 
+      width: 100,
+      fixed: 'right'
+    }
+  ],
+  version: '1.0.0',
+  updatedAt: '2025-10-10T12:00:00Z'
+}
+```
+
+---
+
+**文档结束**