# /create-page-component Command 当使用此命令时,创建一个新的页面组件及其相关的业务组件。 ## 目的 在 ERPTurbo_Admin 项目中快速创建一个标准的页面组件,包括: 1. 业务组件(基于 BizContainer) 2. 页面组件 3. 国际化翻译 4. 组件导出配置 ## 前置条件 - 项目使用 UmiJS Max + Ant Design + TypeScript - 已有 BizContainer 核心业务组件 - 已有 API 服务层(通过 OpenAPI 自动生成) ## 使用方法 ``` /create-page-component <页面名称> ``` **参数说明:** - `<页面名称>`: 页面名称,如 `ReceivablesDetail` - ``: API 服务名称,如 `dealerAccountRecord` ## 执行步骤 ### 1. 分析现有代码结构 首先查找类似页面作为参考: - 查看目标页面组件(如 `FarmerPayment.tsx`)的实现模式 - 确定业务组件应该放在哪个目录(如 `components/Dealer/`) - 查找 API 服务定义(如 `services/business/dealerAccountRecord.ts`) - 查看 API 类型定义(`services/business/typings.d.ts` 中的 VO 类型) ### 2. 创建业务组件 在 `packages/app-operation/src/components/<业务目录>/` 下创建 `List.tsx`: **组件结构模板:** ```tsx import { BizContainer, BizValueType, ModeType } from '@/components'; import { business } from '@/services'; import { useIntl } from '@@/exports'; import { ProColumns } from '@ant-design/pro-components'; import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions'; import React from 'react'; interface IComponentNameListProps { ghost?: boolean; // 筛选参数 xxxId?: BusinessAPI.XXXVO['xxxId']; search?: boolean; onValueChange?: () => void; mode?: ModeType; trigger?: () => React.ReactNode; } export default function ComponentNameList(props: IComponentNameListProps) { const { ghost = false, xxxId, search = true, mode = 'page', trigger, onValueChange, } = props; const intl = useIntl(); const intlPrefix = 'componentName'; // 国际化前缀 // 定义表格列 const columns: ProColumns[] = [ { title: intl.formatMessage({ id: intlPrefix + '.column.xxx' }), dataIndex: 'xxx', key: 'xxx', valueType: 'text', // text|money|dateTime|select等 }, // 更多列... ]; const detailColumns: ProDescriptionsItemProps< BusinessAPI.XXXVO, BizValueType >[] = columns as ProDescriptionsItemProps< BusinessAPI.XXXVO, BizValueType >[]; return ( rowKey={'id'} permission={'operation-xxx'} func={business.apiService} method={'apiService'} methodUpper={'ApiService'} intlPrefix={intlPrefix} modeType={mode} onValueChange={onValueChange} container={{ ghost }} status={false} page={{ fieldProps: { bordered: true, ghost, search, params: { ...(xxxId && { xxxId }), }, }, columns, options: () => [], }} create={false} // 根据需要启用 update={false} destroy={false} detail={{ rowId: xxxId, formType: 'drawer', columns: detailColumns, trigger, }} /> ); } ``` **关键点:** - 使用 `BizContainer` 作为容器组件 - 定义 `columns` 用于列表展示 - 定义 `detailColumns` 用于详情展示 - 通过 `params` 传递筛选条件 - 根据需求配置 `create/update/destroy` ### 3. 导出业务组件 在 `packages/app-operation/src/components/<业务目录>/index.ts` 中添加导出: ```typescript export { default as ComponentNameList } from './ComponentNameList'; ``` ### 4. 创建页面组件 在 `packages/app-operation/src/pages/` 下创建 `.tsx`: ```tsx import { ComponentNameList } from '@/components'; export default function Page() { return ; } ``` **关键点:** - 文件名与路由一致 - 导出为 `Page` 函数 - 直接渲染业务组件 ### 5. 添加国际化翻译 在 `packages/app-operation/src/locales/zh-CN.ts` 中添加翻译: ```typescript componentName: { column: { // 列标题 fieldName: '字段名称', // 枚举值 'enum.key': '枚举值显示文本', option: '操作', }, modal: { view: { title: '查看详情', button: '详情', }, }, }, ``` **关键点:** - 使用 `intlPrefix` 作为顶级键 - `column` 中包含所有列的翻译 - 枚举值使用 `'enum.key'` 格式 - 添加 `modal` 相关翻译 ## 常见 BizContainer 配置 ### 如何判断是否有内置 valueType **快速检查方法:** 查看 `packages/app-operation/src/components/Biz/typing.ts` 中的 `BizValueType` 类型定义(第 18-28 行)。 **内置 valueType 列表:** ```typescript export type BizValueType = | 'user' // 用户 | 'customField' // 自定义字段 | 'status' // 状态 | 'remark' // 备注 | 'order' // 采购单 ✅ | 'dealer' // 经销商 ✅ | 'paymentTask' // 付款任务 ✅ | 'company' // 公司 ✅ | 'orderCost' // 订单费用 ✅ | 'supplier'; // 供应商 ✅ ``` **判断流程:** ``` 1. 检查关联对象类型(如 orderVO、dealerVO、orderShipVO) ↓ 2. 去掉 "VO" 后缀,得到类型名(order → order, orderShip → orderShip) ↓ 3. 在 BizValueType 中查找 ↓ 4a. 找到 → 使用对应的 valueType ↓ 4b. 未找到 → 使用嵌套 dataIndex 数组 ``` **示例:** - `orderVO` → 去掉 "VO" → `order` → 在列表中 ✅ → 使用 `valueType: 'order'` - `dealerVO` → 去掉 "VO" → `dealer` → 在列表中 ✅ → 使用 `valueType: 'dealer'` - `orderShipVO` → 去掉 "VO" → `orderShip` → 不在列表 ❌ → 使用 `dataIndex: ['orderShipVO', 'orderSn']` ### valueType 类型 | 类型 | 说明 | dataIndex 格式 | key 格式 | 适用场景 | |------|------|----------------|----------|----------| | `text` | 文本 | `fieldName` | `fieldName` | 普通文本字段 | | `money` | 金额 | `amount` | `amount` | 自动格式化货币 | | `dateTime` | 日期时间 | `createdAt` | `createdAt` | 格式化时间戳 | | `select` | 下拉选择 | `type` | `type` | 配合 valueEnum | | `switch` | 开关 | `status` | `status` | 布尔值显示 | | `order` | 采购单 | `orderVO` | `orderId` | orderVO 对象 | | `dealer` | 经销商 | `dealerVO` | `dealerId` | dealerVO 对象 | | `company` | 公司 | `companyVO` | `companyId` | companyVO 对象 | | `paymentTask` | 付款任务 | `paymentTaskVO` | `paymentTaskId` | paymentTaskVO 对象 | | `orderCost` | 订单费用 | `orderCostVO` | `orderCostId` | orderCostVO 对象 | | `supplier` | 供应商 | `supplierVO` | `supplierId` | supplierVO 对象 | ### 对象关联列配置(xxxVO 模式) 项目中大量使用对象关联模式,当 VO 类型中包含嵌套的 `xxxVO` 对象时,优先使用内置 valueType。 #### 通用规则 **有内置 valueType 的情况:** ```tsx // 1. 检查 BizValueType 是否包含对应类型 // 2. 如果包含,使用 xxxVO + xxxId + xxx 模式 { dataIndex: 'orderVO', // 完整的 VO 字段名 key: 'orderId', // 对应的主键字段 valueType: 'order', // BizValueType 中定义的类型 } ``` **无内置 valueType 的情况:** ```tsx // 1. BizValueType 中没有对应类型 // 2. 使用嵌套 dataIndex 数组直接访问子字段 { dataIndex: ['orderShipVO', 'orderSn'], // 嵌套路径 key: 'orderShipId', // 不需要 valueType,默认为文本 } ``` **示例对照表:** | VO 字段 | 是否有 valueType | dataIndex | key | valueType | 说明 | |---------|-----------------|-----------|-----|-----------|------| | `orderVO?: OrderVO` | ✅ 是 | `orderVO` | `orderId` | `order` | 采购单信息 | | `dealerVO?: DealerVO` | ✅ 是 | `dealerVO` | `dealerId` | `dealer` | 经销商信息 | | `companyVO?: CompanyVO` | ✅ 是 | `companyVO` | `companyId` | `company` | 公司信息 | | `supplierVO?: SupplierVO` | ✅ 是 | `supplierVO` | `supplierId` | `supplier` | 供应商信息 | | `orderShipVO?: OrderShipVO` | ❌ 否 | `['orderShipVO', 'orderSn']` | `orderShipId` | - | 发货单信息 | | `paymentTaskVO?: PaymentTaskVO` | ✅ 是 | `paymentTaskVO` | `paymentTaskId` | `paymentTask` | 付款任务信息 | | `orderCostVO?: OrderCostVO` | ✅ 是 | `orderCostVO` | `orderCostId` | `orderCost` | 订单费用信息 | #### order 列(采购单关联) ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.order' }), dataIndex: 'orderVO', key: 'orderId', valueType: 'order', }, ``` **要点:** - BizContainer 内部会自动处理订单链接和展示 - 支持点击跳转到订单详情页 - 自动显示订单编号或关键信息 **参考文件:** `components/Order/OrderSupplierList.tsx:54-59` #### dealer 列(经销商关联) ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.dealer' }), dataIndex: 'dealerVO', key: 'dealerId', valueType: 'dealer', hidden: !!dealerId, // 可选:如果已传入 dealerId,则隐藏此列 }, ``` **要点:** - 显示经销商名称链接 - 支持点击跳转到经销商详情 - 可根据业务需求使用 `hidden` 属性控制显示 **参考文件:** - `components/Dealer/DealerPaymentAccountList.tsx:44-48` - `components/Order/OrderShipList.tsx:41-45` #### company 列(公司关联) ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.company' }), dataIndex: 'companyVO', key: 'companyId', valueType: 'company', }, ``` **要点:** - 显示公司名称链接 - 支持点击跳转到公司详情 - 常用于展示入账公司、销售公司等 **参考文件:** `components/Order/OrderShipList.tsx:47-51` #### 无内置 valueType 的关联列 对于 BizValueType 中没有定义的类型(如 `orderShipVO`),使用嵌套 dataIndex 数组: ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.orderShip' }), dataIndex: ['orderShipVO', 'orderSn'], // 直接访问嵌套字段 key: 'orderShipId', // 不需要指定 valueType }, ``` **优点:** - ✅ 简洁:无需自定义 render 函数 - ✅ 类型安全:利用 ProTable 的嵌套字段访问 - ✅ 自动处理空值:ProTable 自动处理 undefined 情况 **更多嵌套示例:** ```tsx // 访问嵌套对象的单个字段 { dataIndex: ['orderShipVO', 'orderSn'] } // 发货单编号 { dataIndex: ['dealerVO', 'shortName'] } // 经销商简称 { dataIndex: ['companyVO', 'fullName'] } // 公司全称 { dataIndex: ['orderVO', 'orderVehicle', 'plate'] } // 多层嵌套:订单的车辆车牌 ``` **参考文件:** `components/Dealer/DealerAccountRecordList.tsx:73-77` #### 条件隐藏关联列 当组件已通过 props 传入关联对象的 ID 时,可以隐藏该列避免冗余: ```tsx // 接收 props interface IDealerAccountRecordListProps { dealerId?: BusinessAPI.DealerAccountRecordVO['dealerId']; orderId?: BusinessAPI.DealerAccountRecordVO['orderId']; } // 列配置 { dataIndex: 'dealerVO', key: 'dealerId', valueType: 'dealer', hidden: !!dealerId, // 如果已传入 dealerId,则隐藏此列 }, ``` **使用场景:** - 列表作为详情页的子组件时 - 已知特定经销商/订单的记录列表 - 避免显示冗余的关联信息 #### select 列(枚举类型) 对于有固定选项的枚举字段: ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.targetType' }), dataIndex: 'targetType', key: 'targetType', valueType: 'select', valueEnum: { VALUE_1: { text: '选项1', status: 'Processing' }, VALUE_2: { text: '选项2', status: 'Success' }, }, }, ``` #### money 列(金额格式化) 金额字段会自动格式化,也可以自定义渲染: ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.amount' }), dataIndex: 'amount', key: 'amount', valueType: 'money', search: false, render: (_, record) => ( = 0 ? 'text-green-600' : 'text-red-600'}> {formatCurrency(record.amount)} ), }, ``` ### 权限配置格式 ``` operation-<资源>-<操作> 例如: operation-dealer-account-record-view ``` ### 国际化前缀命名规范 - 页面级别: 使用小写+连字符,如 `dealer-account-record` - 组件级别: 使用驼峰式,如 `dealerAccountRecord` - 保持与 API 服务名称一致 ## 快速参考:关联列配置决策树 ``` 需要显示关联对象(xxxVO)? ↓ 检查 BizValueType 是否包含对应类型 ↓ ┌─────────────┬─────────────────┐ │ 包含 ✅ │ 不包含 ❌ │ ├─────────────┼─────────────────┤ │ 使用 │ 使用嵌套 │ │ valueType │ dataIndex 数组 │ ├─────────────┼─────────────────┤ │ 示例: │ 示例: │ │ dataIndex: │ dataIndex: │ │ 'orderVO' │ ['orderShipVO',│ │ valueType: │ 'orderSn'] │ │ 'order' │ │ └─────────────┴─────────────────┘ ``` ## 常见关联列配置速查表 | 场景 | dataIndex | key | valueType | 备注 | |------|-----------|-----|-----------|------| | **有内置 valueType** | | 采购单 | `orderVO` | `orderId` | `order` | 自动显示订单链接 | | 经销商 | `dealerVO` | `dealerId` | `dealer` | 自动显示经销商链接 | | 公司 | `companyVO` | `companyId` | `company` | 自动显示公司链接 | | 供应商 | `supplierVO` | `supplierId` | `supplier` | 自动显示供应商链接 | | 付款任务 | `paymentTaskVO` | `paymentTaskId` | `paymentTask` | 自动显示任务链接 | | 订单费用 | `orderCostVO` | `orderCostId` | `orderCost` | 自动显示费用链接 | | **无内置 valueType** | | 发货单编号 | `['orderShipVO', 'orderSn']` | `orderShipId` | - | 直接访问嵌套字段 | | 经销商简称 | `['dealerVO', 'shortName']` | `dealerId` | - | 直接访问嵌套字段 | | 车牌号 | `['orderVO', 'orderVehicle', 'plate']` | - | - | 支持多层嵌套 | ## 示例参考 查看以下文件作为参考: - 业务组件: `components/Dealer/DealerAccountRecordList.tsx` - 页面组件: `pages/ReceivablesDetail.tsx` - 组件导出: `components/Dealer/index.ts` - 国际化: `locales/zh-CN.ts` 中的 `dealerAccountRecord` 部分 ### DealerAccountRecordList 组件示例 该组件展示了以下特性: - ✅ **dealer 列**: 展示关联的经销商(第 60-66 行) - 使用 `valueType: 'dealer'` - 条件隐藏:当已传入 `dealerId` 时隐藏此列 - ✅ **order 列**: 展示关联的采购单(第 67-72 行) - 使用 `valueType: 'order'` - ✅ **orderShip 列**: 使用嵌套 dataIndex 访问发货单字段(第 73-77 行) - 使用 `dataIndex: ['orderShipVO', 'orderSn']` - 无需自定义 render 函数 - 无需 valueType(BizValueType 中未定义 orderShip) - ✅ **枚举类型列**: 变动类型使用 Tag 和颜色区分(第 89-103 行) - ✅ **金额格式化**: 使用 formatCurrency 格式化金额(第 110-144 行) - ✅ **动态样式**: 根据正负值显示不同颜色和图标 - ✅ **筛选条件**: 支持 dealerId 和 orderId 筛选(第 182-185 行) ## 验证清单 - [ ] 业务组件已创建在正确的目录 - [ ] 组件已正确导出 - [ ] 页面组件已创建 - [ ] 国际化翻译已添加 - [ ] API 服务和类型定义已确认存在 - [ ] 权限配置正确 - [ ] 组件 props 支持所需的筛选条件 ## 故障排除 ### 问题: 组件找不到 **解决**: 检查 `components/index.ts` 是否正确导出了业务组件目录 ### 问题: 翻译未生效 **解决**: 检查 `intlPrefix` 是否与国际化文件中的键一致 ### 问题: API 类型错误 **解决**: 1. 确认 API 服务文件存在于 `services/business/` 2. 检查类型定义是否存在于 `typings.d.ts` 3. 确保 `business.apiService` 导入正确 ### 问题: 权限报错 **解决**: 确认 `permission` 属性格式为 `operation--` ### 问题: order 列不显示或报错 **原因分析**: 1. TypeScript 类型定义中可能没有 `orderVO` 字段 2. API 返回数据时实际包含 `orderVO` 字段(后端动态扩展) **解决方案**: - 方案1: 如果类型定义中缺少 `orderVO`,可以在组件中扩展类型: ```tsx // 在组件顶部添加类型扩展 declare module '@/services/business' { namespace BusinessAPI { interface DealerAccountRecordVO { orderVO?: OrderVO; } } } ``` - 方案2: 使用类型断言(不推荐,但可用): ```tsx const columns: ProColumns[] = [ // ... ]; ``` - 方案3: 联系后端更新 OpenAPI 规范,确保 `orderVO` 字段在类型定义中 ### 问题: dealer 列不显示经销商名称 **原因**: BizContainer 内部会自动处理,但需要确保 `dealerVO` 字段存在 **解决**: 1. 确认 API 返回数据包含 `dealerVO` 对象 2. 检查 `dataIndex` 使用的是 `dealerVO` 而不是 `dealerId` 3. 检查 `valueType` 是否设置为 `dealer` ### 问题: 自定义关联列(如 orderShip)显示为 [object Object] **原因**: 直接渲染对象而没有提取具体字段 **解决**: ```tsx // ❌ 错误写法 - 直接渲染整个对象 { dataIndex: 'orderShipVO', render: (_, record) => {record.orderShipVO}, } // ❌ 错误写法 - 使用自定义 render 访问字段 { dataIndex: 'orderShipVO', render: (_, record) => record.orderShipVO ? ( {record.orderShipVO.orderSn} ) : null, } // ✅ 正确写法 - 使用嵌套 dataIndex 数组 { dataIndex: ['orderShipVO', 'orderSn'], // 直接访问嵌套字段 key: 'orderShipId', // 不需要 render,ProTable 自动处理 } ``` **优势对比:** - ❌ render 方式:代码冗长,需要手动处理空值 - ✅ 嵌套 dataIndex:简洁清晰,ProTable 自动处理所有情况 ### 问题: 关联列在嵌套组件中重复显示 **原因**: 当列表作为详情页的子组件时,父组件已通过 props 传入 ID **解决**: 使用 `hidden` 属性条件隐藏 ```tsx { dataIndex: 'dealerVO', key: 'dealerId', valueType: 'dealer', hidden: !!dealerId, // 如果已传入 dealerId,则隐藏此列 }, ``` ### 问题: order 列点击后不跳转 **解决**: 1. 确认 `dataIndex` 使用的是 `orderVO` 而不是 `orderId` 2. 确认 `key` 使用的是 `orderId` 3. 检查 BizContainer 配置中 `detail` 是否正确启用 ## 高级配置:Toolbar 和状态管理 ### Toolbar Tab 配置 对于需要按状态筛选的列表页面,可以添加 Tab 类型的 toolbar: **完整示例(参考 PaymentTaskList):** ```tsx import React, { useState } from 'react'; export default function ComponentNameList(props: IComponentNameListProps) { const intl = useIntl(); const intlPrefix = 'componentName'; // 1. 添加 activeKey 状态管理 const [activeKey, setActiveKey] = useState('PENDING'); return ( { setActiveKey(key as string); }, }, // 4. 可选:添加操作按钮 actions: [ { actionRef.current?.reload(); onValueChange?.(); }} />, ], }, }, columns, options: () => [], }} /> ); } ``` **关键点:** 1. **useState**: 使用 `useState` 管理当前选中的 tab 2. **params 筛选**: 当 `activeKey !== 'ALL'` 时,将状态传递给 API 查询参数 3. **toolbar.menu.type**: 设置为 `'tab'` 启用 tab 菜单 4. **onChange**: tab 切换时更新 `activeKey` 5. **actions**: 可选,在 toolbar 右侧添加操作按钮 **参考文件:** `components/PaymentTask/PaymentTaskList.tsx:538-587` ### 状态枚举映射(stateMap) 为状态字段创建枚举映射,统一管理状态显示文本和颜色: **定义模式:** ```tsx // 状态枚举映射 const stateMap: Record = { PENDING: { label: intl.formatMessage({ id: intlPrefix + '.state.pending', }), color: 'default', // default | processing | success | warning | error }, COMPLETED: { label: intl.formatMessage({ id: intlPrefix + '.state.completed', }), color: 'success', }, CANCELLED: { label: intl.formatMessage({ id: intlPrefix + '.state.cancelled', }), color: 'error', }, }; ``` **在列配置中使用 stateMap:** ```tsx { title: intl.formatMessage({ id: intlPrefix + '.column.state' }), dataIndex: 'state', key: 'state', valueType: 'select', valueEnum: Object.entries(stateMap).reduce( (acc, [key, value]) => ({ ...acc, [key]: { text: value.label, status: value.color as any }, }), {}, ), search: false, // 状态通常通过 tab 筛选,不需要在搜索表单中显示 render: (_, record) => { const stateInfo = stateMap[record.state as string]; return stateInfo ? ( {stateInfo.label} ) : ( {record.state} ); }, }, ``` **颜色映射规则:** | 状态类型 | color 值 | ProTable status | 适用场景 | |---------|----------|----------------|----------| | 默认/待处理 | `default` | `Default` | 待处理、初始状态 | | 进行中 | `processing` | `Processing` | 处理中、部分完成 | | 成功/完成 | `success` | `Success` | 已完成、已通过 | | 警告/注意 | `warning` | `Warning` | 部分完成、需注意 | | 错误/取消 | `error` | `Error` | 已取消、已拒绝 | **国际化翻译配置:** ```typescript componentName: { state: { pending: '待处理', completed: '已完成', cancelled: '已取消', }, tab: { all: '全部', pending: '待处理', completed: '已完成', cancelled: '已取消', }, }, ``` **最佳实践:** 1. **stateMap 和 tab items 保持一致**: 状态枚举值应与 tab 的 key 完全匹配 2. **使用国际化的 key**: stateMap 中的 key 使用后端枚举值(如 `PENDING`),label 使用国际化 3. **颜色语义化**: 根据状态含义选择合适的颜色 4. **添加 ALL tab**: 除了具体状态外,通常需要"全部"选项用于显示所有数据 **参考文件:** `components/Reconciliation/ReconciliationRecordList.tsx:32-72` ### 完整示例:带 Tab 筛选的列表组件 ```tsx import { BizContainer, BizValueType, ModeType } from '@/components'; import { business } from '@/services'; import { useIntl } from '@@/exports'; import { ProColumns } from '@ant-design/pro-components'; import React, { useState } from 'react'; export default function ReconciliationRecordList( props: IReconciliationRecordListProps, ) { const { ghost = false, search = true, mode = 'page' } = props; const intl = useIntl(); const intlPrefix = 'reconciliationRecord'; const [activeKey, setActiveKey] = useState('PENDING'); // 状态枚举映射 const stateMap: Record = { PENDING: { label: intl.formatMessage({ id: intlPrefix + '.state.pending' }), color: 'default', }, RECONCILED: { label: intl.formatMessage({ id: intlPrefix + '.state.reconciled' }), color: 'processing', }, PARTIAL_INVOICE: { label: intl.formatMessage({ id: intlPrefix + '.state.partialInvoice' }), color: 'warning', }, INVOICED: { label: intl.formatMessage({ id: intlPrefix + '.state.invoiced' }), color: 'success', }, PARTIAL_PAYMENT: { label: intl.formatMessage({ id: intlPrefix + '.state.partialPayment' }), color: 'warning', }, PAID: { label: intl.formatMessage({ id: intlPrefix + '.state.paid' }), color: 'success', }, }; const columns: ProColumns[] = [ // ... 列配置 { title: intl.formatMessage({ id: intlPrefix + '.column.state' }), dataIndex: 'state', key: 'state', valueType: 'select', valueEnum: Object.entries(stateMap).reduce( (acc, [key, value]) => ({ ...acc, [key]: { text: value.label, status: value.color as any }, }), {}, ), search: false, render: (_, record) => { const stateInfo = stateMap[record.state as string]; return stateInfo ? ( {stateInfo.label} ) : ( {record.state} ); }, }, ]; return ( page={{ fieldProps: { params: { ...(activeKey !== 'ALL' && { state: activeKey as BusinessAPI.ReconciliationVO['state'], }), }, toolbar: { menu: { type: 'tab', activeKey: activeKey, items: [ { key: 'ALL', label: intl.formatMessage({ id: intlPrefix + '.tab.all' }), }, { key: 'PENDING', label: intl.formatMessage({ id: intlPrefix + '.tab.pending' }), }, { key: 'RECONCILED', label: intl.formatMessage({ id: intlPrefix + '.tab.reconciled' }), }, // ... 其他状态 tab ], onChange: (key) => { setActiveKey(key as string); }, }, }, }, columns, options: () => [], }} /> ); } ``` **参考文件:** - `components/PaymentTask/PaymentTaskList.tsx` - 完整的 Tab 筛选示例 - `components/Reconciliation/ReconciliationRecordList.tsx` - 对账记录 Tab 筛选