# /create-page-component Command

当使用此命令时，创建一个新的页面组件及其相关的业务组件。

## 目的

在 ERPTurbo_Admin 项目中快速创建一个标准的页面组件，包括：
1. 业务组件（基于 BizContainer）
2. 页面组件
3. 国际化翻译
4. 组件导出配置

## 前置条件

- 项目使用 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`：

**组件结构模板：**
```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` 中添加导出：

```typescript
export { default as ComponentNameList } from './ComponentNameList';
```

### 4. 创建页面组件

在 `packages/app-operation/src/pages/` 下创建 `<PageName>.tsx`：

```tsx
import { ComponentNameList } from '@/components';

export default function Page() {
	return <ComponentNameList />;
}
```

**关键点：**
- 文件名与路由一致
- 导出为 `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) => (
		<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 类型错误
**解决**:
1. 确认 API 服务文件存在于 `services/business/`
2. 检查类型定义是否存在于 `typings.d.ts`
3. 确保 `business.apiService` 导入正确

### 问题: 权限报错
**解决**: 确认 `permission` 属性格式为 `operation-<resource>-<action>`

### 问题: 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<any, BizValueType>[] = [
    // ...
  ];
  ```

- 方案3: 联系后端更新 OpenAPI 规范，确保 `orderVO` 字段在类型定义中

### 问题: dealer 列不显示经销商名称
**原因**: BizContainer 内部会自动处理，但需要确保 `dealerVO` 字段存在

**解决**:
1. 确认 API 返回数据包含 `dealerVO` 对象
2. 检查 `dataIndex` 使用的是 `dealerVO` 而不是 `dealerId`
3. 检查 `valueType` 是否设置为 `dealer`

### 问题: 自定义关联列（如 orderShip）显示为 [object Object]
**原因**: 直接渲染对象而没有提取具体字段

**解决**:
```tsx
// ❌ 错误写法 - 直接渲染整个对象
{
	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` 属性条件隐藏
```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<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: () => [],
			}}
		/>
	);
}
```

**关键点：**
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<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：**
```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 ? (
			<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` | 已取消、已拒绝 |

**国际化翻译配置：**
```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<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 筛选
