- 添加 ReconciliationRecordCreate 组件实现对账单创建流程 - 更新 DealerAccountRecordList 组件中 dealerAccountRecordId 字段为可选 - 修改 DealerModal 中显示经销商名称格式 - 调整 LeftMenu 样式中的 margin 属性 - 重构 PaymentTask 和 Reconciliation 组件中的渲染逻辑 - 简化 ReconciliationSummary 组件的参数传递方式 - 更新国际化配置文件增加对账相关翻译 - 替换 ReconciliationCreate 页面使用新的创建组件
26 KiB
/create-page-component Command
当使用此命令时,创建一个新的页面组件及其相关的业务组件。
目的
在 ERPTurbo_Admin 项目中快速创建一个标准的页面组件,包括:
- 业务组件(基于 BizContainer)
- 页面组件
- 国际化翻译
- 组件导出配置
前置条件
- 项目使用 UmiJS Max + Ant Design + TypeScript
- 已有 BizContainer 核心业务组件
- 已有 API 服务层(通过 OpenAPI 自动生成)
使用方法
/create-page-component <页面名称> <API服务名称>
参数说明:
<页面名称>: 页面名称,如ReceivablesDetail<API服务名称>: 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/<业务目录>/ 下创建 <ComponentName>List.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<BusinessAPI.XXXVO, BizValueType>[] = [
{
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 (
<BizContainer<
typeof business.apiService,
BusinessAPI.XXXVO,
BusinessAPI.XXXPageQry
>
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 中添加导出:
export { default as ComponentNameList } from './ComponentNameList';
4. 创建页面组件
在 packages/app-operation/src/pages/ 下创建 <PageName>.tsx:
import { ComponentNameList } from '@/components';
export default function Page() {
return <ComponentNameList />;
}
关键点:
- 文件名与路由一致
- 导出为
Page函数 - 直接渲染业务组件
5. 添加国际化翻译
在 packages/app-operation/src/locales/zh-CN.ts 中添加翻译:
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 列表:
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 的情况:
// 1. 检查 BizValueType 是否包含对应类型
// 2. 如果包含,使用 xxxVO + xxxId + xxx 模式
{
dataIndex: 'orderVO', // 完整的 VO 字段名
key: 'orderId', // 对应的主键字段
valueType: 'order', // BizValueType 中定义的类型
}
无内置 valueType 的情况:
// 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 列(采购单关联)
{
title: intl.formatMessage({ id: intlPrefix + '.column.order' }),
dataIndex: 'orderVO',
key: 'orderId',
valueType: 'order',
},
要点:
- BizContainer 内部会自动处理订单链接和展示
- 支持点击跳转到订单详情页
- 自动显示订单编号或关键信息
参考文件: components/Order/OrderSupplierList.tsx:54-59
dealer 列(经销商关联)
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealer' }),
dataIndex: 'dealerVO',
key: 'dealerId',
valueType: 'dealer',
hidden: !!dealerId, // 可选:如果已传入 dealerId,则隐藏此列
},
要点:
- 显示经销商名称链接
- 支持点击跳转到经销商详情
- 可根据业务需求使用
hidden属性控制显示
参考文件:
components/Dealer/DealerPaymentAccountList.tsx:44-48components/Order/OrderShipList.tsx:41-45
company 列(公司关联)
{
title: intl.formatMessage({ id: intlPrefix + '.column.company' }),
dataIndex: 'companyVO',
key: 'companyId',
valueType: 'company',
},
要点:
- 显示公司名称链接
- 支持点击跳转到公司详情
- 常用于展示入账公司、销售公司等
参考文件: components/Order/OrderShipList.tsx:47-51
无内置 valueType 的关联列
对于 BizValueType 中没有定义的类型(如 orderShipVO),使用嵌套 dataIndex 数组:
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderShip' }),
dataIndex: ['orderShipVO', 'orderSn'], // 直接访问嵌套字段
key: 'orderShipId',
// 不需要指定 valueType
},
优点:
- ✅ 简洁:无需自定义 render 函数
- ✅ 类型安全:利用 ProTable 的嵌套字段访问
- ✅ 自动处理空值:ProTable 自动处理 undefined 情况
更多嵌套示例:
// 访问嵌套对象的单个字段
{ dataIndex: ['orderShipVO', 'orderSn'] } // 发货单编号
{ dataIndex: ['dealerVO', 'shortName'] } // 经销商简称
{ dataIndex: ['companyVO', 'fullName'] } // 公司全称
{ dataIndex: ['orderVO', 'orderVehicle', 'plate'] } // 多层嵌套:订单的车辆车牌
参考文件: components/Dealer/DealerAccountRecordList.tsx:73-77
条件隐藏关联列
当组件已通过 props 传入关联对象的 ID 时,可以隐藏该列避免冗余:
// 接收 props
interface IDealerAccountRecordListProps {
dealerId?: BusinessAPI.DealerAccountRecordVO['dealerId'];
orderId?: BusinessAPI.DealerAccountRecordVO['orderId'];
}
// 列配置
{
dataIndex: 'dealerVO',
key: 'dealerId',
valueType: 'dealer',
hidden: !!dealerId, // 如果已传入 dealerId,则隐藏此列
},
使用场景:
- 列表作为详情页的子组件时
- 已知特定经销商/订单的记录列表
- 避免显示冗余的关联信息
select 列(枚举类型)
对于有固定选项的枚举字段:
{
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 列(金额格式化)
金额字段会自动格式化,也可以自定义渲染:
{
title: intl.formatMessage({ id: intlPrefix + '.column.amount' }),
dataIndex: 'amount',
key: 'amount',
valueType: 'money',
search: false,
render: (_, record) => (
<span className={record.amount >= 0 ? 'text-green-600' : 'text-red-600'}>
{formatCurrency(record.amount)}
</span>
),
},
权限配置格式
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 类型错误
解决:
- 确认 API 服务文件存在于
services/business/ - 检查类型定义是否存在于
typings.d.ts - 确保
business.apiService导入正确
问题: 权限报错
解决: 确认 permission 属性格式为 operation-<resource>-<action>
问题: order 列不显示或报错
原因分析:
- TypeScript 类型定义中可能没有
orderVO字段 - API 返回数据时实际包含
orderVO字段(后端动态扩展)
解决方案:
-
方案1: 如果类型定义中缺少
orderVO,可以在组件中扩展类型:// 在组件顶部添加类型扩展 declare module '@/services/business' { namespace BusinessAPI { interface DealerAccountRecordVO { orderVO?: OrderVO; } } } -
方案2: 使用类型断言(不推荐,但可用):
const columns: ProColumns<any, BizValueType>[] = [ // ... ]; -
方案3: 联系后端更新 OpenAPI 规范,确保
orderVO字段在类型定义中
问题: dealer 列不显示经销商名称
原因: BizContainer 内部会自动处理,但需要确保 dealerVO 字段存在
解决:
- 确认 API 返回数据包含
dealerVO对象 - 检查
dataIndex使用的是dealerVO而不是dealerId - 检查
valueType是否设置为dealer
问题: 自定义关联列(如 orderShip)显示为 [object Object]
原因: 直接渲染对象而没有提取具体字段
解决:
// ❌ 错误写法 - 直接渲染整个对象
{
dataIndex: 'orderShipVO',
render: (_, record) => <span>{record.orderShipVO}</span>,
}
// ❌ 错误写法 - 使用自定义 render 访问字段
{
dataIndex: 'orderShipVO',
render: (_, record) =>
record.orderShipVO ? (
<span>{record.orderShipVO.orderSn}</span>
) : null,
}
// ✅ 正确写法 - 使用嵌套 dataIndex 数组
{
dataIndex: ['orderShipVO', 'orderSn'], // 直接访问嵌套字段
key: 'orderShipId',
// 不需要 render,ProTable 自动处理
}
优势对比:
- ❌ render 方式:代码冗长,需要手动处理空值
- ✅ 嵌套 dataIndex:简洁清晰,ProTable 自动处理所有情况
问题: 关联列在嵌套组件中重复显示
原因: 当列表作为详情页的子组件时,父组件已通过 props 传入 ID
解决: 使用 hidden 属性条件隐藏
{
dataIndex: 'dealerVO',
key: 'dealerId',
valueType: 'dealer',
hidden: !!dealerId, // 如果已传入 dealerId,则隐藏此列
},
问题: order 列点击后不跳转
解决:
- 确认
dataIndex使用的是orderVO而不是orderId - 确认
key使用的是orderId - 检查 BizContainer 配置中
detail是否正确启用
高级配置:Toolbar 和状态管理
Toolbar Tab 配置
对于需要按状态筛选的列表页面,可以添加 Tab 类型的 toolbar:
完整示例(参考 PaymentTaskList):
import React, { useState } from 'react';
export default function ComponentNameList(props: IComponentNameListProps) {
const intl = useIntl();
const intlPrefix = 'componentName';
// 1. 添加 activeKey 状态管理
const [activeKey, setActiveKey] = useState<string>('PENDING');
return (
<BizContainer
// ... 其他配置
page={{
fieldProps: {
// 2. 将 activeKey 传递给 params 作为筛选条件
params: {
...(activeKey !== 'ALL' && {
state: activeKey as BusinessAPI.XXXVO['state'],
}),
},
// 3. 配置 toolbar
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
// ... 其他状态 tab
],
onChange: (key) => {
setActiveKey(key as string);
},
},
// 4. 可选:添加操作按钮
actions: [
<ComponentCreate
key={'create'}
onFinish={() => {
actionRef.current?.reload();
onValueChange?.();
}}
/>,
],
},
},
columns,
options: () => [],
}}
/>
);
}
关键点:
- useState: 使用
useState管理当前选中的 tab - params 筛选: 当
activeKey !== 'ALL'时,将状态传递给 API 查询参数 - toolbar.menu.type: 设置为
'tab'启用 tab 菜单 - onChange: tab 切换时更新
activeKey - actions: 可选,在 toolbar 右侧添加操作按钮
参考文件: components/PaymentTask/PaymentTaskList.tsx:538-587
状态枚举映射(stateMap)
为状态字段创建枚举映射,统一管理状态显示文本和颜色:
定义模式:
// 状态枚举映射
const stateMap: Record<string, { label: string; color: string }> = {
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:
{
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 ? (
<span style={{ color: stateInfo.color === 'default' ? undefined : stateInfo.color }}>
{stateInfo.label}
</span>
) : (
<span>{record.state}</span>
);
},
},
颜色映射规则:
| 状态类型 | color 值 | ProTable status | 适用场景 |
|---|---|---|---|
| 默认/待处理 | default |
Default |
待处理、初始状态 |
| 进行中 | processing |
Processing |
处理中、部分完成 |
| 成功/完成 | success |
Success |
已完成、已通过 |
| 警告/注意 | warning |
Warning |
部分完成、需注意 |
| 错误/取消 | error |
Error |
已取消、已拒绝 |
国际化翻译配置:
componentName: {
state: {
pending: '待处理',
completed: '已完成',
cancelled: '已取消',
},
tab: {
all: '全部',
pending: '待处理',
completed: '已完成',
cancelled: '已取消',
},
},
最佳实践:
- stateMap 和 tab items 保持一致: 状态枚举值应与 tab 的 key 完全匹配
- 使用国际化的 key: stateMap 中的 key 使用后端枚举值(如
PENDING),label 使用国际化 - 颜色语义化: 根据状态含义选择合适的颜色
- 添加 ALL tab: 除了具体状态外,通常需要"全部"选项用于显示所有数据
参考文件: components/Reconciliation/ReconciliationRecordList.tsx:32-72
完整示例:带 Tab 筛选的列表组件
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<string>('PENDING');
// 状态枚举映射
const stateMap: Record<string, { label: string; color: string }> = {
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<BusinessAPI.ReconciliationVO, BizValueType>[] = [
// ... 列配置
{
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 ? (
<span className={`text-${stateInfo.color}-600`}>
{stateInfo.label}
</span>
) : (
<span>{record.state}</span>
);
},
},
];
return (
<BizContainer>
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 筛选