feat(dealer): 添加经销商应收账款功能并优化订单发货单相关组件

- 新增 DealerAccountRecordList 组件用于展示经销商应收账款记录
- 在 DealerList 中添加应收账款标签页和相关组件
- 为 OrderShipList 和 OrderShipModal 添加应收金额和调整金额列
- 新增 OrderSupplierSelectList 替代原有的 OrderSupplierInvoiceList
- 重构 ReconciliationSummary 使用 ProForm 组件并优化抹零功能
- 更新对账单创建页面逻辑,使用新的应收金额和调整金额字段
- 添加 ReceivablesDetail 页面用于查看应收账款明细
- 优化发货单选择列表的列配置和渲染方式
This commit is contained in:
shenyifei 2026-01-09 17:59:03 +08:00
parent 925b9f4f76
commit 9001490da1
26 changed files with 1587 additions and 381 deletions

View File

@ -0,0 +1,626 @@
# /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 函数
- 无需 valueTypeBizValueType 中未定义 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',
// 不需要 renderProTable 自动处理
}
```
**优势对比:**
- ❌ 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` 是否正确启用

View File

@ -0,0 +1,80 @@
---
paths: src/services/**/*.ts
---
# 规则:不要直接修改 src/services 目录,使用 typings.d.ts 获取类型
## 规则说明
`packages/app-operation/src/services/` 目录下的文件由 OpenAPI 自动生成,不应手动修改。
## 目录结构
```
src/services/
├── index.ts # 导出入口(自动生成)
├── business/ # 业务 API
│ ├── index.ts # 模块导出(自动生成)
│ ├── order.ts # API 实现(自动生成)
│ └── typings.d.ts # 类型定义(从 typings.d.ts 读取)
├── auth/
│ ├── index.ts
│ ├── user.ts
│ └── typings.d.ts
└── ...
```
## 禁止行为
- ❌ 直接修改 `src/services/**/*.ts` 文件
- ❌ 在 `.ts` 文件中添加、删除或修改 API 函数
- ❌ 手动编辑类型定义
## 正确做法
### 1. 获取类型信息
当需要查看或使用 API 类型时,**必须**使用 Glob/Grep 工具读取 `typings.d.ts` 文件:
```bash
# 正确:读取类型定义文件
Glob pattern: "packages/app-operation/src/services/**/typings.d.ts"
Grep pattern: "OrderVO" path: "packages/app-operation/src/services/business/typings.d.ts"
# 错误:直接读取 .ts 实现文件
Read file: "packages/app-operation/src/services/business/typings.d.ts"
```
### 2. 查找 API 函数
查找 API 函数时,通过模块名推断文件位置,使用 Grep 搜索:
```bash
# 查找订单相关 API
Grep path: "packages/app-operation/src/services" pattern: "order"
```
### 3. 更新 API
如需更新 API 类型定义,运行:
```bash
cd packages/app-operation
pnpm openapi
```
## 类型命名规范
`typings.d.ts` 中的类型遵循以下命名模式:
| 类型后缀 | 用途 | 示例 |
|---------|------|------|
| `VO` | 视图对象(响应) | `OrderVO` |
| `Qry` | 查询参数 | `OrderPageQry` |
| `Cmd` | 命令参数(创建/更新) | `OrderCreateCmd` |
| `PageQry` | 分页查询 | `OrderPageQry` |
## 违反此规则的后果
- 手动修改会在 `pnpm openapi` 后被覆盖
- 可能导致 API 类型与后端不一致
- 破坏自动生成机制的完整性

View File

@ -0,0 +1,31 @@
---
paths: swagger/*.json
---
# 规则:不要加载或修改 swagger/*.json 文件
## 规则说明
`swagger/` 目录下的 JSON 文件是后端服务自动生成的 OpenAPI 规范文件,由后端团队维护。
## 禁止行为
- ❌ 直接读取 `swagger/*.json` 文件
- ❌ 修改 `swagger/*.json` 文件
- ❌ 在前端代码中依赖这些文件
- ❌ 将 swagger JSON 文件纳入版本控制(如需跟踪,应由后端处理)
## 正确做法
1. **更新 API 类型**:如需更新前端 API 类型定义,运行命令:
```bash
cd packages/app-operation
pnpm openapi
```
2. **使用生成的 services**:前端应使用 `src/services/` 目录下自动生成的 API 客户端代码,而非直接解析 swagger 文件
## 违反此规则的后果
- 可能导致前端与后端 API 不同步
- 手动修改的 swagger 文件会在下次后端同步时被覆盖
- 破坏前后端约定的 OpenAPI 规范

View File

@ -272,27 +272,30 @@ pnpm test
## 开发注意事项
1. **不要直接修改 services/ 目录**
- 这些文件由 OpenAPI 自动生成
- 修改 swagger/ 规范后重新生成
- 这些文件由 OpenAPI 自动生成,修改后会被覆盖
- 详细规则见 [.claude/rules/services-generated.md](./.claude/rules/services-generated.md)
2. **遵循组件命名约定**
2. **不要加载或修改 swagger/*.json 文件**
- 详细规则见 [.claude/rules/swagger-json.md](./.claude/rules/swagger-json.md)
3. **遵循组件命名约定**
- 页面组件:文件名 + `Page` 后缀,导出为 `Page`
- 业务组件:语义化命名,如 `NewComponentList`
3. **国际化是必需的**
4. **国际化是必需的**
- 所有用户可见文本必须使用 `useIntl()``intl.formatMessage()`
- 定义 `intlPrefix` 前缀
4. **权限控制**
5. **权限控制**
- 页面使用 `PageContainer` 包装
- 操作按钮使用 `ButtonAccess` 包装
- 权限标识格式:`operation-{resource}-{action}`
5. **响应式设计**
6. **响应式设计**
- 使用 `isMobile` 属性自动适配移动端
- 表单宽度和布局会自动调整
6. **状态管理**
7. **状态管理**
- 使用 UmiJS 内置状态管理initialState、model
- 组件间通信使用 `actionRef`
@ -316,9 +319,14 @@ pnpm test
## 相关文档
### 开发规则
- services 自动生成规则:[.claude/rules/services-generated.md](./.claude/rules/services-generated.md)
- 不要查看或修改 swagger JSON[.claude/rules/swagger-json.md](./.claude/rules/swagger-json.md)
- 项目架构设计:`.lingma/rules/design.md`
- Biz 组件设计规则:`.lingma/rules/biz.md`
- 添加新页面指南:`.lingma/rules/add-new-page.md`
### 官方文档
- UmiJS Max 文档https://umijs.org/docs/max/introduce
- Ant Design 文档https://ant.design
- PNPM 文档https://pnpm.io

View File

@ -0,0 +1,203 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import { business } from '@/services';
import { formatCurrency } from '@/utils/format';
import { useIntl } from '@@/exports';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { Tag } from 'antd';
import React from 'react';
interface IDealerAccountRecordListProps {
ghost?: boolean;
dealerVO?: BusinessAPI.DealerVO;
orderVO?: BusinessAPI.OrderVO;
dealerAccountRecordId: BusinessAPI.DealerAccountRecordVO['dealerAccountRecordId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function DealerAccountRecordList(
props: IDealerAccountRecordListProps,
) {
const {
ghost = false,
dealerVO,
orderVO,
dealerAccountRecordId,
search = true,
mode = 'page',
trigger,
onValueChange,
} = props;
const intl = useIntl();
const intlPrefix = 'dealerAccountRecord';
// 变动类型映射
const targetTypeMap: Record<string, { label: string; color: string }> = {
ORIGIN_PURCHASE_SHIP: {
label: intl.formatMessage({
id: intlPrefix + '.targetType.originPurchaseShip',
}),
color: 'blue',
},
MARKET_PURCHASE_SHIP: {
label: intl.formatMessage({
id: intlPrefix + '.targetType.marketPurchaseShip',
}),
color: 'green',
},
MARKET_TRANSFER_SHIP: {
label: intl.formatMessage({
id: intlPrefix + '.targetType.marketTransferShip',
}),
color: 'orange',
},
};
const columns: ProColumns<BusinessAPI.DealerAccountRecordVO, BizValueType>[] =
[
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealer' }),
dataIndex: 'dealerVO',
key: 'dealerId',
valueType: 'dealer',
hidden: !!dealerVO, // 如果已传入 dealerId则隐藏此列
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.order' }),
dataIndex: 'orderVO',
key: 'orderId',
valueType: 'order',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderShip' }),
dataIndex: ['orderShipVO', 'orderSn'],
key: 'targetId',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.recordSn' }),
dataIndex: 'recordSn',
key: 'recordSn',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.targetType' }),
dataIndex: 'targetType',
key: 'targetType',
valueType: 'select',
valueEnum: Object.entries(targetTypeMap).reduce(
(acc, [key, value]) => ({
...acc,
[key]: { text: value.label, status: value.color as any },
}),
{},
),
render: (_, record) => {
const typeInfo = targetTypeMap[record.targetType];
return typeInfo ? (
<Tag color={typeInfo.color}>{typeInfo.label}</Tag>
) : (
<Tag>{record.targetType}</Tag>
);
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.beforeAmount' }),
dataIndex: 'beforeAmount',
key: 'beforeAmount',
valueType: 'money',
search: false,
render: (_, record) => (
<span className="text-gray-500">
{formatCurrency(record.beforeAmount)}
</span>
),
},
{
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'}
>
{record.amount >= 0 ? <ArrowUpOutlined /> : <ArrowDownOutlined />}
<span className="ml-1">
{formatCurrency(Math.abs(record.amount))}
</span>
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.afterAmount' }),
dataIndex: 'afterAmount',
key: 'afterAmount',
valueType: 'money',
search: false,
render: (_, record) => (
<span className="text-blue-600 font-medium">
{formatCurrency(record.afterAmount)}
</span>
),
},
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.DealerAccountRecordVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.DealerAccountRecordVO,
BizValueType
>[];
return (
<BizContainer<
typeof business.dealerAccountRecord,
BusinessAPI.DealerAccountRecordVO,
BusinessAPI.DealerAccountRecordPageQry
>
rowKey={'dealerAccountRecordId'}
permission={'operation-dealer-account-record'}
func={business.dealerAccountRecord}
method={'dealerAccountRecord'}
methodUpper={'DealerAccountRecord'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{
ghost,
}}
remark={{
mode: 'editor',
}}
status={false}
page={{
fieldProps: {
bordered: true,
ghost,
//@ts-ignore
search,
params: {
...(dealerVO && { dealerId: dealerVO.dealerId }),
...(orderVO && { orderId: orderVO.orderId }),
},
},
columns,
options: () => [],
}}
create={false}
update={false}
destroy={false}
detail={{
rowId: dealerAccountRecordId,
formType: 'drawer',
columns: detailColumns,
trigger,
}}
/>
);
}

View File

@ -1,6 +1,7 @@
import {
BizContainer,
BizValueType,
DealerAccountRecordList,
DealerPaymentAccountList,
DealerPurchaseSetting,
DealerRebateCustomerList,
@ -367,6 +368,18 @@ export default function DealerList(props: IDealerListProps) {
onValueChange: () => void,
) => {
return [
{
label: `应收账款`,
key: 'dealerAccountRecord',
children: (
<DealerAccountRecordList
dealerVO={dealerVO}
ghost={true}
search={false}
onValueChange={onValueChange}
/>
),
},
{
label: `结算账户`,
key: 'dealerPaymentAccountList',

View File

@ -1,12 +1,13 @@
export { default as DealerAccountRecordList } from './DealerAccountRecordList';
export { default as DealerFormItem } from './DealerFormItem';
export { default as DealerList } from './DealerList';
export { default as DealerModal } from './DealerModal';
export type { IDealerModalProps } from './DealerModal';
export { default as DealerPaymentAccountList } from './DealerPaymentAccountList';
export { default as DealerPurchaseSetting } from './DealerPurchaseSetting';
export { default as DealerRebateCustomerList } from './DealerRebateCustomerList';
export { default as DealerSearch } from './DealerSearch';
export { default as DealerSelect } from './DealerSelect';
export { default as DealerStallList } from './DealerStallList';
export { default as DealerWarehouseList } from './DealerWarehouseList';
export { default as DeliveryTemplate } from './DeliveryTemplate';
export { default as DealerPurchaseSetting } from './DealerPurchaseSetting';
export { default as DealerStallList } from './DealerStallList';

View File

@ -1,6 +1,8 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import { business } from '@/services';
import { formatCurrency } from '@/utils/format';
import { useIntl } from '@@/exports';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import React, { useState } from 'react';
@ -86,6 +88,43 @@ export default function OrderShipList(props: IOrderShipListProps) {
key: 'watermelonGrade',
search: false,
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.receivableAmount',
}),
dataIndex: 'receivableAmount',
key: 'receivableAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span className="text-blue-600 font-medium">
{formatCurrency(record.receivableAmount)}
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.adjustedAmount' }),
dataIndex: 'adjustedAmount',
key: 'adjustedAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span
className={
record.adjustedAmount >= 0 ? 'text-green-600' : 'text-red-600'
}
>
{record.adjustedAmount >= 0 ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
<span className="ml-1">
{formatCurrency(Math.abs(record.adjustedAmount))}
</span>
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.type' }),
dataIndex: 'type',

View File

@ -1,5 +1,6 @@
import { SelectModal } from '@/components';
import { business } from '@/services';
import { formatCurrency } from '@/utils/format';
import { formatParam } from '@/utils/formatParam';
import { pagination } from '@/utils/pagination';
import { useIntl } from '@@/exports';
@ -7,6 +8,7 @@ import {
ActionType,
LightFilter,
ProColumns,
ProFormDateRangePicker,
ProFormSelect,
} from '@ant-design/pro-components';
import { Alert, ModalProps, Space, Tag } from 'antd';
@ -99,6 +101,8 @@ export default function OrderShipModal(props: IOrderShipModalProps) {
dataIndex: 'shippingDate',
key: 'shippingDate',
valueType: 'date',
sorter: true,
defaultSortOrder: 'ascend',
},
{
title: intl.formatMessage({
@ -114,6 +118,38 @@ export default function OrderShipModal(props: IOrderShipModalProps) {
key: 'watermelonGrade',
search: false,
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.receivableAmount',
}),
dataIndex: 'receivableAmount',
key: 'receivableAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span className="text-blue-600 font-medium">
{formatCurrency(record.receivableAmount)}
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.adjustedAmount' }),
dataIndex: 'adjustedAmount',
key: 'adjustedAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span
className={
record.adjustedAmount >= 0 ? 'text-green-600' : 'text-red-600'
}
>
<span className="ml-1">
{formatCurrency(Math.abs(record.adjustedAmount))}
</span>
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.type' }),
dataIndex: 'type',
@ -329,6 +365,16 @@ export default function OrderShipModal(props: IOrderShipModalProps) {
}}
>
{extraFilter}
{/* 发货时间筛选 */}
<ProFormDateRangePicker
label={'发货时间'}
name={'shippingDate'}
placeholder={'请选择发货时间'}
fieldProps={{
allowClear: true,
picker: 'date',
}}
/>
<ProFormSelect
label={'发货单状态'}
name={'state'}

View File

@ -1,9 +1,19 @@
import { CompanyList, DealerList, OrderList } from '@/components';
import { BizValueType } from '@/components';
import BizProvider from '@/components/Biz/BizProvider';
import { formatCurrency } from '@/utils/format';
import { useIntl } from '@@/exports';
import { Space, Table, TableProps } from 'antd';
import { camelCase } from 'lodash';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
import {
ProColumns,
ProTable,
ProTableProps,
} from '@ant-design/pro-components';
type IOrderShipSelectListProps = TableProps<BusinessAPI.OrderShipVO>;
type IOrderShipSelectListProps = ProTableProps<
BusinessAPI.OrderShipVO,
BusinessAPI.OrderShipPageQry,
BizValueType
>;
export default function OrderShipSelectList(props: IOrderShipSelectListProps) {
const intl = useIntl();
@ -11,69 +21,24 @@ export default function OrderShipSelectList(props: IOrderShipSelectListProps) {
const { ...tableProps } = props;
// 订单列表列定义
const orderColumns = [
const orderColumns: ProColumns<BusinessAPI.OrderShipVO, BizValueType>[] = [
{
title: intl.formatMessage({ id: intlPrefix + '.column.order' }),
dataIndex: 'orderVO',
key: 'orderId',
render: (orderShipVO: BusinessAPI.OrderShipVO) => {
const orderVO = orderShipVO.orderVO;
return (
orderVO && (
<OrderList
ghost={true}
mode={'detail'}
orderId={orderShipVO.orderId}
trigger={() => (
<Space>
<a>
{`${orderVO.orderVehicle?.dealerName} | 第 ${orderVO.orderVehicle?.vehicleNo || '暂无'} 车 | ${orderVO.orderSn || '暂无'}`}
</a>
</Space>
)}
/>
)
);
},
valueType: 'order',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealer' }),
dataIndex: 'dealerVO',
key: 'dealerId',
render: (orderShipVO: BusinessAPI.OrderShipVO) => {
return (
orderShipVO && (
<DealerList
ghost={true}
mode={'detail'}
dealerId={orderShipVO.dealerId}
trigger={() => (
<Space>
<a>{orderShipVO.dealerName}</a>
</Space>
)}
/>
)
);
},
valueType: 'dealer',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.company' }),
dataIndex: 'companyVO',
key: 'companyId',
render: (orderShipVO: BusinessAPI.OrderShipVO) => {
return (
orderShipVO && (
<CompanyList
ghost={true}
mode={'detail'}
companyId={orderShipVO.companyId}
trigger={() => (
<Space>
<a>{orderShipVO.companyName}</a>
</Space>
)}
/>
)
);
},
valueType: 'company',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderSn' }),
@ -112,35 +77,111 @@ export default function OrderShipSelectList(props: IOrderShipSelectListProps) {
key: 'watermelonGrade',
search: false,
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.receivableAmount',
}),
dataIndex: 'receivableAmount',
key: 'receivableAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span className="text-blue-600 font-medium">
{formatCurrency(record.receivableAmount)}
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.adjustedAmount' }),
dataIndex: 'adjustedAmount',
key: 'adjustedAmount',
search: false,
valueType: 'money',
render: (_, record) => (
<span
className={
record.adjustedAmount >= 0 ? 'text-green-600' : 'text-red-600'
}
>
{record.adjustedAmount >= 0 ? (
<ArrowUpOutlined />
) : (
<ArrowDownOutlined />
)}
<span className="ml-1">
{formatCurrency(Math.abs(record.adjustedAmount))}
</span>
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.type' }),
dataIndex: 'type',
key: 'type',
render: (orderShipVO: BusinessAPI.OrderShipVO) => {
return intl.formatMessage({
id: `${intlPrefix}.type.${camelCase(orderShipVO.type) || 'unknown'}`,
});
valueType: 'select',
valueEnum: {
PURCHASE_SHIP: intl.formatMessage({
id: intlPrefix + '.column.type.enum.purchaseShip',
}),
TRANSFER_SHIP: intl.formatMessage({
id: intlPrefix + '.column.type.enum.transferShip',
}),
CHANGE_SHIP: intl.formatMessage({
id: intlPrefix + '.column.type.enum.changeShip',
}),
RETURN_SHIP: intl.formatMessage({
id: intlPrefix + '.column.type.enum.returnShip',
}),
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.state' }),
dataIndex: 'state',
key: 'state',
valueType: 'select',
search: false,
render: (orderShipVO: BusinessAPI.OrderShipVO) => {
return intl.formatMessage({
id: `${intlPrefix}.state.${camelCase(orderShipVO.state) || 'unknown'}`,
});
valueEnum: {
DRAFT: intl.formatMessage({
id: intlPrefix + '.column.state.enum.draft',
}),
WAIT_SHIPMENT: intl.formatMessage({
id: intlPrefix + '.column.state.enum.waitShipment',
}),
WAIT_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.enum.waitPayment',
}),
PARTIAL_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.enum.partialPayment',
}),
FULL_PAYMENT: intl.formatMessage({
id: intlPrefix + '.column.state.enum.fullPayment',
}),
REJECT_FINISH: intl.formatMessage({
id: intlPrefix + '.column.state.enum.rejectFinish',
}),
FINISH: intl.formatMessage({
id: intlPrefix + '.column.state.enum.finish',
}),
},
},
...(tableProps.columns || []),
];
return (
<Table
{...tableProps}
rowKey="ordershipId"
columns={orderColumns}
pagination={false}
size="small"
/>
<BizProvider>
<ProTable
ghost={true}
search={false}
options={false}
{...tableProps}
rowKey="ordershipId"
columns={orderColumns}
pagination={false}
size="small"
scroll={{
x: 'max-content',
}}
/>
</BizProvider>
);
}

View File

@ -0,0 +1,135 @@
import { BizValueType, CompanyList } from '@/components';
import BizProvider from '@/components/Biz/BizProvider';
import { business } from '@/services';
import { useIntl } from '@@/exports';
import {
ProColumns,
ProTable,
ProTableProps,
} from '@ant-design/pro-components';
type IOrderSupplierSelectListProps = ProTableProps<
BusinessAPI.OrderSupplierVO,
BusinessAPI.OrderSupplierPageQry,
BizValueType
>;
export default function OrderSupplierSelectList(
props: IOrderSupplierSelectListProps,
) {
const intl = useIntl();
const intlPrefix = 'orderSupplier';
const { ...tableProps } = props;
// 订单列表列定义
const orderColumns: ProColumns<BusinessAPI.OrderSupplierVO, BizValueType>[] =
[
{
title: intl.formatMessage({ id: intlPrefix + '.column.order' }),
dataIndex: 'orderVO',
key: 'orderId',
valueType: 'order',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.name' }),
dataIndex: 'name',
key: 'name',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.invoiceAmount' }),
dataIndex: 'invoiceAmount',
key: 'invoiceAmount',
valueType: 'money',
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.depositAmount' }),
dataIndex: 'depositAmount',
key: 'depositAmount',
valueType: 'money',
search: false,
},
// 剩余付款金额
{
title: intl.formatMessage({
id: intlPrefix + '.column.remainingAmount',
}),
dataIndex: 'remainingAmount',
key: 'remainingAmount',
valueType: 'money',
search: false,
renderText: (_, record) => {
return record.invoiceAmount - record.depositAmount || 0;
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderCompany' }),
dataIndex: 'orderCompany',
valueType: 'select',
request: async (params) => {
const { data } = await business.company.listCompany({
companyListQry: {
...params,
},
});
return (
data?.map((item) => {
return {
label: item.fullName,
value: item.companyId,
};
}) || []
);
},
render: (_, record) => {
return (
<CompanyList
ghost={true}
mode={'detail'}
companyId={record.orderCompany.companyId}
trigger={() => <a href={'#'}>{record.orderCompany.fullName}</a>}
/>
);
},
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.isPaid' }),
dataIndex: 'isPaid',
key: 'isPaid',
valueType: 'select',
valueEnum: {
true: {
text: intl.formatMessage({
id: intlPrefix + '.column.isPaid.paid',
}),
status: 'success',
},
false: {
text: intl.formatMessage({
id: intlPrefix + '.column.isPaid.unpaid',
}),
status: 'processing',
},
},
},
...(tableProps.columns || []),
];
return (
<BizProvider>
<ProTable
ghost={true}
search={false}
options={false}
{...tableProps}
rowKey="orderSupplierId"
columns={orderColumns}
pagination={false}
size="small"
scroll={{
x: 'max-content',
}}
/>
</BizProvider>
);
}

View File

@ -11,7 +11,8 @@ export { default as OrderSelect } from './OrderSelect';
export { default as OrderShipList } from './OrderShipList';
export { default as OrderShipModal } from './OrderShipModal';
export type { IOrderShipModalProps } from './OrderShipModal';
export { default as OrderShipSelectList } from './OrderShipSelectList';
export { default as OrderStallList } from './OrderStallList';
export { default as OrderSupplierList } from './OrderSupplierList';
export { default as OrderSupplierModal } from './OrderSupplierModal';
export { default as OrderShipSelectList } from './OrderShipSelectList';
export { default as OrderSupplierSelectList } from './OrderSupplierSelectList';

View File

@ -1,84 +0,0 @@
import { OrderList, SupplierFarmerList } from '@/components';
import { Space, Table, TableProps } from 'antd';
type IOrderSupplierInvoiceListProps = TableProps<BusinessAPI.OrderSupplierVO>;
export default function OrderSupplierInvoiceList(
props: IOrderSupplierInvoiceListProps,
) {
const { ...tableProps } = props;
// 订单列表列定义
const orderColumns = [
{
title: '采购单',
dataIndex: 'orderVO',
key: 'orderId',
render: (orderVO: BusinessAPI.OrderVO) => {
return (
orderVO && (
<OrderList
ghost={true}
mode={'detail'}
orderId={orderVO.orderId}
trigger={() => (
<Space>
<a>
{`${orderVO.orderVehicle?.dealerName} | 第 ${orderVO.orderVehicle?.vehicleNo || '暂无'} 车 | ${orderVO.orderSn || '暂无'}`}
</a>
</Space>
)}
/>
)
);
},
},
{
title: '瓜农名称',
key: 'name',
render: (orderSupplierVO: BusinessAPI.OrderSupplierVO) => {
return (
orderSupplierVO && (
<SupplierFarmerList
ghost={true}
mode={'detail'}
supplierId={orderSupplierVO.supplierId}
trigger={() => <a>{orderSupplierVO.name}</a>}
/>
)
);
},
},
{
title: '货款金额(元)',
dataIndex: 'invoiceAmount',
key: 'invoiceAmount',
valueType: 'money',
},
{
title: '定金金额(元)',
dataIndex: 'depositAmount',
key: 'depositAmount',
valueType: 'money',
},
{
title: '应付金额(元)',
key: 'remainingAmount',
valueType: 'money',
search: false,
render: (record: BusinessAPI.OrderSupplierVO) => {
return record.invoiceAmount - record.depositAmount || 0;
},
},
...(tableProps.columns || []),
];
return (
<Table
{...tableProps}
rowKey="orderSupplierId"
columns={orderColumns}
pagination={false}
size="small"
/>
);
}

View File

@ -2,7 +2,7 @@ import {
BizEditor,
ButtonAccess,
InsertPosition,
OrderSupplierInvoiceList,
OrderSupplierSelectList,
SupplierFarmerList,
SupplierInvoiceList,
SupplierSelect,
@ -241,7 +241,7 @@ export default function PaymentTaskCreate(props: IPaymentTaskCreateProps) {
style={{ marginBottom: 16 }}
>
{selectedOrderSupplierList.length > 0 ? (
<OrderSupplierInvoiceList
<OrderSupplierSelectList
rowKey="orderSupplierId"
dataSource={selectedOrderSupplierList}
columns={[
@ -326,6 +326,7 @@ export default function PaymentTaskCreate(props: IPaymentTaskCreateProps) {
{
title: '操作',
key: 'action',
fixed: 'right',
render: (record: BusinessAPI.OrderSupplierVO) => (
<Button
type="link"

View File

@ -3,7 +3,7 @@ import {
BizValueType,
InvoicerStatisticCard,
ModeType,
OrderSupplierInvoiceList,
OrderSupplierSelectList,
PaymentRecordList,
PaymentTaskCreate,
PaymentTaskPay,
@ -251,7 +251,7 @@ export default function PaymentTaskList(props: IPaymentTaskListProps) {
{ count: statistics.selfInvoiceCount },
),
children: (
<OrderSupplierInvoiceList
<OrderSupplierSelectList
rowKey="orderSupplierId"
dataSource={statistics.selfInvoiceList}
columns={[
@ -320,7 +320,7 @@ export default function PaymentTaskList(props: IPaymentTaskListProps) {
{ count: statistics.proxyInvoiceCount },
),
children: (
<OrderSupplierInvoiceList
<OrderSupplierSelectList
rowKey="orderSupplierId"
dataSource={statistics.proxyInvoiceList}
columns={[
@ -389,7 +389,7 @@ export default function PaymentTaskList(props: IPaymentTaskListProps) {
{ count: statistics.noInvoiceCount },
),
children: (
<OrderSupplierInvoiceList
<OrderSupplierSelectList
rowKey="orderSupplierId"
columns={[
{

View File

@ -3,7 +3,7 @@ import {
ButtonAccess,
CompanyPaymentAccountSelect,
InsertPosition,
OrderSupplierInvoiceList,
OrderSupplierSelectList,
ProFormUploadMaterial,
SupplierFarmerList,
SupplierInvoiceList,
@ -220,7 +220,7 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
})}
>
<ProCard bordered>
<OrderSupplierInvoiceList
<OrderSupplierSelectList
rowKey="orderSupplierId"
dataSource={orderSupplierVOList || []}
columns={[

View File

@ -1,5 +1,4 @@
export { default as InvoicerStatisticCard } from './InvoicerStatisticCard';
export { default as OrderSupplierInvoiceList } from './OrderSupplierInvoiceList';
export { default as PaymentTaskCreate } from './PaymentTaskCreate';
export { default as PaymentTaskFormItem } from './PaymentTaskFormItem';
export { default as PaymentTaskList } from './PaymentTaskList';

View File

@ -1,3 +1,4 @@
import { ReconciliationShipCardProps } from '@/components';
import { formatCurrency } from '@/utils/format';
import { DownOutlined, UpOutlined } from '@ant-design/icons';
import {
@ -56,11 +57,7 @@ export default function ReconciliationShipCard({
};
// 计算汇总数据
const originalAmount =
shipOrder.orderShipItemList?.reduce(
(sum, item) => sum + (item.totalAmount || 0),
0,
) || 0;
const originalAmount = shipOrder.receivableAmount;
const adjustmentTotal = adjustments.reduce((sum, adj) => sum + adj.amount, 0);

View File

@ -1,13 +1,13 @@
import { ReconciliationSummaryProps } from '@/components';
import { formatCurrency } from '@/utils/format';
import {
Checkbox,
Col,
Input,
InputNumber,
Row,
Space,
Typography,
} from 'antd';
ProForm,
ProFormDependency,
ProFormMoney,
ProFormSwitch,
ProFormTextArea,
} from '@ant-design/pro-components';
import { Row, Typography } from 'antd';
import { useIntl } from 'umi';
const { Text } = Typography;
@ -25,25 +25,18 @@ const ADJUSTMENT_TYPE_LABELS: Record<string, string> = {
export default function ReconciliationSummary({
shipOrders,
adjustmentsMap,
roundOffEnabled = false,
onRoundOffChange,
roundOffAmount,
onRoundOffAmountChange,
roundOffRemark = '',
onRoundOffRemarkChange,
roundOff,
onFinish,
}: ReconciliationSummaryProps) {
const intl = useIntl();
const intlPrefix = 'reconciliation';
// 计算原应收总额
const originalTotalAmount = shipOrders.reduce((sum, order) => {
const orderTotal =
order.orderShipItemList?.reduce(
(total, item) => total + (item.totalAmount || 0),
0,
) || 0;
return sum + orderTotal;
}, 0);
const originalTotalAmount =
shipOrders.reduce(
(total, item) => total + (item.receivableAmount || 0),
0,
) || 0;
// 按类型汇总调整项
const adjustmentTotalsByType = Object.values(adjustmentsMap)
@ -67,7 +60,7 @@ export default function ReconciliationSummary({
// 计算实际应收总额(包含抹零金额)
const actualTotalAmount =
originalTotalAmount + adjustmentTotal + (roundOffAmount || 0);
originalTotalAmount - adjustmentTotal + (roundOff?.roundOffAmount || 0);
return (
<div
@ -116,74 +109,76 @@ export default function ReconciliationSummary({
backgroundColor: '#f8f9fa',
}}
>
<Row style={{ marginBottom: 16 }}>
<Checkbox
checked={roundOffEnabled}
onChange={(e) => onRoundOffChange?.(e.target.checked)}
>
{intl.formatMessage({
<ProForm
submitter={false}
request={async () => {
return Promise.resolve({
roundOffEnabled: roundOff?.roundOffEnabled || false,
roundOffAmount: roundOff?.roundOffAmount || 0,
roundOffRemark: roundOff?.roundOffRemark || '',
});
}}
onValuesChange={(_, formData) => {
onFinish?.(formData);
}}
layout="horizontal"
>
<ProFormSwitch
label={intl.formatMessage({
id: `${intlPrefix}.summary.roundOffEnabled`,
})}
</Checkbox>
</Row>
{roundOffEnabled && (
<>
<Row style={{ marginBottom: 12 }}>
<Col>
<Space>
<Text type="secondary">
{intl.formatMessage({
name={'roundOffEnabled'}
/>
<ProFormDependency name={['roundOffEnabled']}>
{({ roundOffEnabled }) =>
roundOffEnabled && (
<>
<ProFormMoney
label={intl.formatMessage({
id: `${intlPrefix}.summary.roundOffAmount`,
})}
</Text>
<InputNumber
style={{ width: 200 }}
name="roundOffAmount"
placeholder={intl.formatMessage({
id: `${intlPrefix}.summary.roundOffAmountPlaceholder`,
})}
value={roundOffAmount}
onChange={onRoundOffAmountChange}
precision={2}
min={-999999999}
max={999999999}
fieldProps={{
precision: 2,
min: -999999999,
max: 999999999,
}}
/>
</Space>
</Col>
</Row>
<Row>
<Col>
<Space>
<Text type="secondary">
{intl.formatMessage({
<ProFormTextArea
label={intl.formatMessage({
id: `${intlPrefix}.summary.roundOffRemark`,
})}
</Text>
<Input
style={{ width: 300 }}
name="roundOffRemark"
placeholder={intl.formatMessage({
id: `${intlPrefix}.summary.roundOffRemarkPlaceholder`,
})}
value={roundOffRemark}
onChange={(e) => onRoundOffRemarkChange?.(e.target.value)}
fieldProps={{
autoSize: { minRows: 3, maxRows: 6 },
showCount: true,
maxLength: 200,
}}
/>
</Space>
</Col>
</Row>
</>
)}
</>
)
}
</ProFormDependency>
</ProForm>
</div>
{/* 调整总额 */}
<Row justify="space-between" align="middle" style={{ marginBottom: 10 }}>
<Text type="secondary">
{intl.formatMessage({
id: `${intlPrefix}.summary.adjustmentTotal`,
})}
</Text>
<Text type={adjustmentTotal < 0 ? 'danger' : undefined}>
{formatCurrency(adjustmentTotal)}
</Text>
</Row>
{/*/!* 调整总额 *!/*/}
{/*<Row justify="space-between" align="middle" style={{ marginBottom: 10 }}>*/}
{/* <Text type="secondary">*/}
{/* {intl.formatMessage({*/}
{/* id: `${intlPrefix}.summary.adjustmentTotal`,*/}
{/* })}*/}
{/* </Text>*/}
{/* <Text type={adjustmentTotal < 0 ? 'danger' : undefined}>*/}
{/* {formatCurrency(adjustmentTotal)}*/}
{/* </Text>*/}
{/*</Row>*/}
{/* 实际应收总额 */}
<Row

View File

@ -24,22 +24,24 @@ export interface ReconciliationShipCardProps {
onExpandChange?: (expanded: boolean) => void;
}
/** 抹零数据 */
export interface RoundOff {
/** 是否抹零 */
roundOffEnabled?: boolean;
/** 抹零金额 */
roundOffAmount?: number;
/** 抹零备注 */
roundOffRemark?: string;
}
/** 对账汇总Props */
export interface ReconciliationSummaryProps {
/** 发货单列表 */
shipOrders: BusinessAPI.OrderShipVO[];
/** 调整项数据 Map<发货单ID, 调整项数组> */
adjustmentsMap: Record<string, ReconciliationAdjustment[]>;
/** 是否抹零 */
roundOffEnabled?: boolean;
/** 抹零值变化回调 */
onRoundOffChange?: (enabled: boolean) => void;
/** 抹零金额 */
roundOffAmount?: number;
/** 抹零金额变化回调 */
onRoundOffAmountChange?: (amount: number | null) => void;
/** 抹零备注 */
roundOffRemark?: string;
/** 抹零备注变化回调 */
onRoundOffRemarkChange?: (remark: string) => void;
/** 抹零数据 */
roundOff?: RoundOff;
/** 抹零注变化回调 */
onFinish?: (roundOff: RoundOff) => void;
}

View File

@ -1209,6 +1209,32 @@ export default {
},
},
},
dealerAccountRecord: {
column: {
dealer: '经销商',
order: '采购单',
orderShip: '发货单',
recordSn: '流水编号',
targetType: '变动类型',
beforeAmount: '变动前金额',
amount: '变动金额',
afterAmount: '变动后金额',
remark: '备注',
createdAt: '创建时间',
option: '操作',
},
targetType: {
originPurchaseShip: '产地采购发货单',
marketPurchaseShip: '市场采购发货单',
marketTransferShip: '市场调货发货单',
},
modal: {
view: {
title: '查看账款明细',
button: '详情',
},
},
},
boxSpec: {
column: {
name: '规格名称',
@ -2117,6 +2143,8 @@ export default {
shippingDate: '发货日期',
estimatedArrivalDate: '预计到仓时间',
watermelonGrade: '西瓜品级',
receivableAmount: '应收金额(元)',
adjustedAmount: '调整金额(元)',
company: '入账公司',
type: '发货单类型',
'type.enum.purchaseShip': '采购发货单',

View File

@ -0,0 +1,5 @@
import { DealerAccountRecordList } from '@/components';
export default function Page() {
return <DealerAccountRecordList />;
}

View File

@ -2,15 +2,15 @@ import {
OrderShipSelectList,
PageContainer,
ReconciliationAdjustment,
ReconciliationShipCard,
ReconciliationSummary,
ReconciliationSummaryProps,
} from '@/components';
import DealerModal from '@/components/Dealer/DealerModal';
import OrderShipModal from '@/components/Order/OrderShipModal';
import { formatCurrency } from '@/utils/format';
import { ProCard, ProFormTextArea } from '@ant-design/pro-components';
import { Button, Col, Row, Space, Steps, Table, Tag } from 'antd';
import { useCallback, useState } from 'react';
import { useState } from 'react';
export default function Page() {
const [current, setCurrent] = useState(0);
@ -35,45 +35,47 @@ export default function Page() {
);
const [roundOffRemark, setRoundOffRemark] = useState('');
// 添加调整项
const handleAddAdjustment = useCallback((shipOrderId: string) => {
const newAdjustment: ReconciliationAdjustment = {
id: `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
type: 'LOSS',
quantity: 0,
unitPrice: 0,
amount: 0,
remark: '',
};
setAdjustmentsMap((prev) => ({
...prev,
[shipOrderId]: [...(prev[shipOrderId] || []), newAdjustment],
}));
}, []);
// 删除调整项
const handleRemoveAdjustment = useCallback(
(shipOrderId: string, adjustmentId: string) => {
setAdjustmentsMap((prev) => ({
...prev,
[shipOrderId]: (prev[shipOrderId] || []).filter(
(adj) => adj.id !== adjustmentId,
),
}));
},
[],
);
// // 添加调整项
// const handleAddAdjustment = useCallback((shipOrderId: string) => {
// const newAdjustment: ReconciliationAdjustment = {
// id: `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`,
// type: 'LOSS',
// quantity: 0,
// unitPrice: 0,
// amount: 0,
// remark: '',
// };
// setAdjustmentsMap((prev) => ({
// ...prev,
// [shipOrderId]: [...(prev[shipOrderId] || []), newAdjustment],
// }));
// }, []);
//
// // 删除调整项
// const handleRemoveAdjustment = useCallback(
// (shipOrderId: string, adjustmentId: string) => {
// setAdjustmentsMap((prev) => ({
// ...prev,
// [shipOrderId]: (prev[shipOrderId] || []).filter(
// (adj) => adj.id !== adjustmentId,
// ),
// }));
// },
// [],
// );
let receivableAmount =
selectedShipOrderList?.reduce(
(sum, item) => sum + (item.receivableAmount || 0),
0,
) || 0;
let adjustedAmount =
selectedShipOrderList?.reduce(
(sum, item) => sum + (item.adjustedAmount || 0),
0,
) || 0;
// 计算总金额
const totalAmount = selectedShipOrderList.reduce((sum, order) => {
// 使用发货单明细的总金额
const orderTotal =
order.orderShipItemList?.reduce(
(total, item) => total + (item.totalAmount || 0),
0,
) || 0;
return sum + orderTotal;
}, 0);
const orderCount = selectedShipOrderList.length;
return (
@ -144,16 +146,6 @@ export default function Page() {
</div>
</div>
</Col>
<Col span={8}>
<div>
<div style={{ color: '#999', marginBottom: 4 }}>
</div>
<div style={{ fontWeight: 'bold' }}>
{selectedDealer.fullName || '-'}
</div>
</div>
</Col>
<Col span={8}>
<div>
<div style={{ color: '#999', marginBottom: 4 }}>
@ -166,6 +158,16 @@ export default function Page() {
</div>
</div>
</Col>
<Col span={8}>
<div>
<div style={{ color: '#999', marginBottom: 4 }}>
</div>
<div style={{ fontWeight: 'bold' }}>
{selectedDealer.receivable || '-'}
</div>
</div>
</Col>
</Row>
) : (
<div
@ -222,6 +224,8 @@ export default function Page() {
{
title: '操作',
key: 'action',
fixed: 'right',
width: 80,
render: (_: any, record: BusinessAPI.OrderShipVO) => (
<Button
type="link"
@ -249,24 +253,36 @@ export default function Page() {
pagination={false}
size="small"
summary={(pageData) => {
let totalAmount = 0;
pageData.forEach((record) => {
const total =
record.orderShipItemList?.reduce(
(sum, item) => sum + (item.totalAmount || 0),
0,
) || 0;
totalAmount += total;
});
let receivableAmount =
pageData?.reduce(
(sum, item) => sum + (item.receivableAmount || 0),
0,
) || 0;
let adjustedAmount =
pageData?.reduce(
(sum, item) => sum + (item.adjustedAmount || 0),
0,
) || 0;
return (
<Table.Summary fixed>
<Table.Summary.Row>
<Table.Summary.Cell index={0} colSpan={3}>
<Table.Summary.Cell index={0} colSpan={9}>
<strong></strong>
</Table.Summary.Cell>
<Table.Summary.Cell index={1}>
<strong style={{ color: '#ff4d4f' }}>
{formatCurrency(totalAmount)}
<strong className="text-blue-600 font-medium">
{formatCurrency(receivableAmount)}
</strong>
</Table.Summary.Cell>
<Table.Summary.Cell index={1}>
<strong
className={
adjustedAmount >= 0
? 'text-green-600'
: 'text-red-600'
}
>
{formatCurrency(adjustedAmount)}
</strong>
</Table.Summary.Cell>
<Table.Summary.Cell index={2} />
@ -316,7 +332,7 @@ export default function Page() {
</div>
<div style={{ fontWeight: 'bold', color: '#ff4d4f' }}>
{formatCurrency(totalAmount)}
{formatCurrency(receivableAmount - adjustedAmount)}
</div>
</div>
</Col>
@ -356,31 +372,35 @@ export default function Page() {
{current === 2 && (
<>
{/* 对账车次清单 */}
<ProCard title={'对账车次清单'} style={{ marginTop: 16 }} bordered>
{selectedShipOrderList.map((shipOrder) => (
<ReconciliationShipCard
key={shipOrder.orderShipId}
shipOrder={shipOrder}
adjustments={adjustmentsMap[shipOrder.orderShipId] || []}
onAddAdjustment={handleAddAdjustment}
onRemoveAdjustment={handleRemoveAdjustment}
/>
))}
</ProCard>
{/*<ProCard title={'对账车次清单'} style={{ marginTop: 16 }} bordered>*/}
{/* {selectedShipOrderList.map((shipOrder) => (*/}
{/* <ReconciliationShipCard*/}
{/* key={shipOrder.orderShipId}*/}
{/* shipOrder={shipOrder}*/}
{/* adjustments={adjustmentsMap[shipOrder.orderShipId] || []}*/}
{/* onAddAdjustment={handleAddAdjustment}*/}
{/* onRemoveAdjustment={handleRemoveAdjustment}*/}
{/* />*/}
{/* ))}*/}
{/*</ProCard>*/}
{/* 对账汇总 */}
<ProCard title={'对账汇总'} style={{ marginTop: 16 }} bordered>
<ReconciliationSummary
shipOrders={selectedShipOrderList}
adjustmentsMap={adjustmentsMap}
roundOffEnabled={roundOffEnabled}
onRoundOffChange={setRoundOffEnabled}
roundOffAmount={roundOffAmount}
onRoundOffAmountChange={(value) =>
setRoundOffAmount(value ?? undefined)
}
roundOffRemark={roundOffRemark}
onRoundOffRemarkChange={setRoundOffRemark}
roundOff={{
roundOffEnabled: roundOffEnabled,
roundOffAmount: roundOffAmount,
roundOffRemark: roundOffRemark,
}}
onFinish={(
roundOff: ReconciliationSummaryProps['roundOff'],
) => {
setRoundOffEnabled(roundOff?.roundOffEnabled || false);
setRoundOffAmount(roundOff?.roundOffAmount);
setRoundOffRemark(roundOff?.roundOffRemark || '');
}}
/>
</ProCard>

View File

@ -100,7 +100,7 @@ declare namespace AuthAPI {
/** 用户ID */
userId: string;
/** 角色ID */
roleIdList: number[];
roleIdList: string[];
/** 角色信息 */
userRoleList?: UserRoleVO[];
};
@ -195,7 +195,7 @@ declare namespace AuthAPI {
type RoleMenuTreeQry = {
/** 角色权限 */
roleId?: number[];
roleId?: string[];
/** 平台ID */
platformId: string;
};
@ -329,7 +329,7 @@ declare namespace AuthAPI {
/** 备注 */
remark?: string;
/** 客户标签 */
labelId?: number[];
labelId?: string[];
/** 用户ID */
userId: string;
};

View File

@ -208,7 +208,7 @@ declare namespace BusinessAPI {
/** 品牌图片URL */
image?: string;
/** 纸箱规格ID */
specIds?: number[];
specIds?: string[];
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -279,7 +279,7 @@ declare namespace BusinessAPI {
/** 品牌图片URL */
image?: string;
/** 纸箱规格ID */
specIds?: number[];
specIds?: string[];
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -296,7 +296,7 @@ declare namespace BusinessAPI {
/** 品牌图片URL */
image?: string;
/** 纸箱规格ID */
specIds?: number[];
specIds?: string[];
/** 备注 */
remark?: string;
/** 状态1_启用0_禁用 */
@ -1021,7 +1021,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */
status: boolean;
/** 成本项ID */
costItemIds?: number[];
costItemIds?: string[];
};
type CostDestroyCmd = {
@ -1249,7 +1249,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */
status: boolean;
/** 成本项ID */
costItemIds?: number[];
costItemIds?: string[];
};
type CostVO = {
@ -1280,7 +1280,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */
status: boolean;
/** 项目id集合 */
costItemIds?: number[];
costItemIds?: string[];
/** 创建时间 */
createdAt?: string;
/** 项目列表 */
@ -1364,6 +1364,19 @@ declare namespace BusinessAPI {
status?: boolean;
/** 经销商账款明细ID */
dealerAccountRecordId?: string;
/** 流水编号 */
recordSn?: string;
/** 经销商ID */
dealerId?: string;
/** 订单ID */
orderId?: string;
/** 变动对象ID */
targetId?: string;
/** 变动类型:1-产地采购发货单2-市场采购发货单3-市场调货发货单; */
targetType?:
| 'ORIGIN_PURCHASE_SHIP'
| 'MARKET_PURCHASE_SHIP'
| 'MARKET_TRANSFER_SHIP';
offset?: number;
};
@ -1426,6 +1439,12 @@ declare namespace BusinessAPI {
remark?: string;
/** 创建时间 */
createdAt?: string;
/** 经销商信息 */
dealerVO?: DealerVO;
/** 订单信息 */
orderVO?: OrderVO;
/** 发货单信息 */
orderShipVO?: OrderShipVO;
};
type DealerCreateCmd = {
@ -2080,7 +2099,7 @@ declare namespace BusinessAPI {
/** 登录密码 */
password: string;
/** 角色ID */
roleId: number[];
roleId: string[];
};
type EmployeeDestroyCmd = {
@ -2173,7 +2192,7 @@ declare namespace BusinessAPI {
/** 用户ID */
userId: string;
/** 角色ID */
roleIdList: number[];
roleIdList: string[];
/** 角色信息 */
userRoleList?: UserRoleVO[];
};
@ -2610,7 +2629,7 @@ declare namespace BusinessAPI {
/** 平台id */
platformId: string;
/** 角色Id */
roleId?: number[];
roleId?: string[];
/** 是否隐藏 */
hideInMenu?: boolean;
/** 权限Id */
@ -2683,7 +2702,7 @@ declare namespace BusinessAPI {
/** 平台id */
platformId: string;
/** 角色Id */
roleId?: number[];
roleId?: string[];
/** 是否隐藏 */
hideInMenu?: boolean;
/** 权限Id */
@ -3110,7 +3129,7 @@ declare namespace BusinessAPI {
| 'LOGISTICS_TYPE'
| 'EXPENSE_TYPE';
/** 关联项目id */
costItemIds?: number[];
costItemIds?: string[];
/** 是否选中 */
selected: boolean;
/** 是否已付款 */
@ -3147,7 +3166,7 @@ declare namespace BusinessAPI {
| 'LOGISTICS_TYPE'
| 'EXPENSE_TYPE';
/** 关联项目id */
costItemIds?: number[];
costItemIds?: string[];
/** 是否付款 */
isPaid?: boolean;
};
@ -3280,7 +3299,7 @@ declare namespace BusinessAPI {
| 'LOGISTICS_TYPE'
| 'EXPENSE_TYPE';
/** 关联项目id */
costItemIds?: number[];
costItemIds?: string[];
/** 创建时间 */
createdAt: string;
/** 采购订单状态: 0_草稿1_审核中2_已完成3_已关闭 */
@ -3878,9 +3897,9 @@ declare namespace BusinessAPI {
/** 备注 */
remark?: string;
/** 应收金额(元) */
receivableAmount?: number;
receivableAmount: number;
/** 调整总额(元) */
adjustedAmount?: number;
adjustedAmount: number;
/** 创建人ID */
createdBy: string;
/** 创建人姓名 */
@ -4018,7 +4037,7 @@ declare namespace BusinessAPI {
/** 产品名称 */
productName?: string;
/** 关联费用id */
costIds?: number[];
costIds?: string[];
/** 成本模板 */
costTemplate?: string;
/** 是否已付定金 */
@ -5349,7 +5368,7 @@ declare namespace BusinessAPI {
/** 产品名称 */
name: string;
/** 关联成本费用id */
costIds?: number[];
costIds?: string[];
/** 成本模板 */
costTemplate?: string;
/** 备注 */
@ -5416,7 +5435,7 @@ declare namespace BusinessAPI {
/** 产品名称 */
name: string;
/** 关联成本费用id */
costIds?: number[];
costIds?: string[];
/** 成本模板 */
costTemplate?: string;
/** 备注 */
@ -5441,7 +5460,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */
status: boolean;
/** 成本ID集合 */
costIds?: number[];
costIds?: string[];
/** 成本费用 */
costVOList?: CostVO[];
/** 成本模板 */
@ -5468,7 +5487,7 @@ declare namespace BusinessAPI {
/** 角色详情 */
description?: string;
/** 角色id */
menuId: number[];
menuId: string[];
};
type RoleDestroyCmd = {
@ -5490,7 +5509,7 @@ declare namespace BusinessAPI {
/** 角色编号 */
roleId?: string;
/** 应用角色Id */
roleIdList?: number[];
roleIdList?: string[];
/** 平台Id */
platformId?: string;
/** 平台Id */
@ -5537,7 +5556,7 @@ declare namespace BusinessAPI {
/** 角色详情 */
description?: string;
/** 角色id */
menuId: number[];
menuId: string[];
/** 角色ID */
roleId: string;
};
@ -5558,9 +5577,9 @@ declare namespace BusinessAPI {
/** 平台 */
platformVO?: PlatformVO;
/** 权限列表 */
permissionId: number[];
permissionId: string[];
/** 菜单列表 */
menuId: number[];
menuId: string[];
/** 创建时间 */
createdAt: string;
};
@ -6445,7 +6464,7 @@ declare namespace BusinessAPI {
/** 备注 */
remark?: string;
/** 客户标签 */
labelId?: number[];
labelId?: string[];
};
type UserDestroyCmd = {
@ -6467,7 +6486,7 @@ declare namespace BusinessAPI {
/** 状态1_启用0_禁用 */
status?: boolean;
/** 用户ID */
userIdList?: number[];
userIdList?: string[];
/** 用户名 */
name?: string;
};
@ -6510,9 +6529,9 @@ declare namespace BusinessAPI {
/** 是否是管理员 */
isAdmin?: boolean;
/** 会员id列表 */
userIdList?: number[];
userIdList?: string[];
/** 排除的用户id列表 */
excludeUserIdList?: number[];
excludeUserIdList?: string[];
/** 小区id */
communityId?: number;
offset?: number;
@ -6522,7 +6541,7 @@ declare namespace BusinessAPI {
/** 用户ID */
userId: string;
/** 角色ID */
roleIdList?: number[];
roleIdList?: string[];
/** 是否覆盖 */
cover: boolean;
};
@ -6567,7 +6586,7 @@ declare namespace BusinessAPI {
/** 备注 */
remark?: string;
/** 客户标签 */
labelId?: number[];
labelId?: string[];
/** 用户ID */
userId: string;
};

File diff suppressed because one or more lines are too long