refactor(payment): 重构支付相关组件和类型定义

- 更新公司支付账户模态框中的账户列表过滤逻辑
- 修改经销商支付账户列表中账户ID参数为可选类型
- 优化经销商搜索组件的状态管理和副作用处理
- 在支付记录列表中使用账户类别映射替代硬编码值枚举
- 为支付任务支付组件添加支付时间日期选择器
- 扩展对账单相关组件导出并新增发票选择功能
- 更新对账单发票列表的数据结构和初始化逻辑
- 在对账单列表中调整完成状态检查和发票上传按钮显示条件
- 重构对账单支付列表的列配置和表单字段定义
- 更新对账单搜索组件的状态管理逻辑
- 本地化文件中更新对账单相关字段标签和占位符文本
- 服务类型定义中更新账户类别和类型的枚举值
- 添加类型修复脚本用于处理数字数组类型转换问题
- 新增对账单发票表单项和模态框组件实现
This commit is contained in:
shenyifei 2026-01-13 16:02:37 +08:00
parent 832a0299df
commit 8b79f1bda7
20 changed files with 27783 additions and 151 deletions

View File

@ -5,9 +5,10 @@
"scripts": {
"build": "max build",
"dev": "max dev",
"fix-types": "node ../../scripts/fix-types.js",
"format": "prettier --cache --write ./src/pages ./src/services ./src/components ./src/constants ./src/models ./src/utils ./src/access.ts ./src/app.tsx",
"postinstall": "max setup",
"openapi": "max openapi && prettier --cache --write ./src/services",
"openapi": "max openapi && prettier --cache --write ./src/services && npm run fix-types",
"setup": "max setup",
"start": "npm run dev"
}

View File

@ -1,3 +1,4 @@
import { SelectModal } from '@/components';
import { business } from '@/services';
import { formatParam } from '@/utils/formatParam';
import { pagination } from '@/utils/pagination';
@ -7,7 +8,6 @@ import {
LightFilter,
ProColumns,
} from '@ant-design/pro-components';
import { SelectModal } from '@/components';
import { Alert, ModalProps, Row, Tag } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
@ -282,8 +282,14 @@ export default function CompanyPaymentAccountModal(
<>
{tips && <Alert type={'info'} message={tips} />}
<Row gutter={16}>
{accountList.map(
(item: BusinessAPI.CompanyPaymentAccountVO) => {
{accountList
.filter((item: BusinessAPI.CompanyPaymentAccountVO) => {
if (params.companyId) {
return item.companyId === params.companyId;
}
return true;
})
.map((item: BusinessAPI.CompanyPaymentAccountVO) => {
return (
<Tag
style={{
@ -309,8 +315,7 @@ export default function CompanyPaymentAccountModal(
{item.accountName}
</Tag>
);
},
)}
})}
</Row>
</>
);

View File

@ -14,7 +14,7 @@ import React from 'react';
interface IDealerPaymentAccountListProps {
ghost?: boolean;
dealerVO?: BusinessAPI.DealerVO;
accountId: BusinessAPI.DealerPaymentAccountVO['accountId'];
accountId?: BusinessAPI.DealerPaymentAccountVO['accountId'];
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;

View File

@ -4,7 +4,7 @@ import {
ProFormSelect,
ProFormSelectProps,
} from '@ant-design/pro-components';
import { useState } from 'react';
import { useEffect, useState } from 'react';
export interface IDealerSearchProps extends ProFormSelectProps {
form: FormInstance;
@ -17,8 +17,11 @@ export default function DealerSearch(props: IDealerSearchProps) {
const { form, selectedList, onFinish, params, ...rest } = props;
const [showDealerModal, setShowDealerModal] = useState<boolean>(false);
const [dealerList, setDealerList] =
useState<(BusinessAPI.DealerVO | undefined)[]>();
const [dealerList, setDealerList] = useState<BusinessAPI.DealerVO[]>();
useEffect(() => {
setDealerList(selectedList);
}, [selectedList]);
return (
<>
@ -31,11 +34,6 @@ export default function DealerSearch(props: IDealerSearchProps) {
},
placeholder: '请选择经销商',
options: dealerList?.map((dealerVO?: BusinessAPI.DealerVO) => {
console.log(
'dealerVO',
dealerVO,
`${dealerVO?.dealerType === 'MARKET' ? '市场' : '超市'} | ${dealerVO?.shortName}`,
);
return {
value: dealerVO?.dealerId,
label: `${dealerVO?.dealerType === 'MARKET' ? '市场' : '超市'} | ${dealerVO?.shortName}`,
@ -50,6 +48,7 @@ export default function DealerSearch(props: IDealerSearchProps) {
onCancel={() => setShowDealerModal(false)}
selectedList={selectedList}
onFinish={async (dealerVOList) => {
console.log('dealerVOList', dealerVOList);
if (dealerVOList.length > 0) {
const dealerVO = dealerVOList[0];
form.setFieldsValue({

View File

@ -16,6 +16,7 @@ export default function DealerSelect(props: IUserSelectProps) {
return (
<ProFormDependency name={['dealerVO', 'canChangeDealer']}>
{({ dealerVO, canChangeDealer }, form) => {
console.log('dealerVO', dealerVO);
return (
<DealerSearch
{...(canChangeDealer !== undefined && {

View File

@ -208,18 +208,7 @@ export default function PaymentRecordList(props: IPaymentRecordListProps) {
title: intl.formatMessage({ id: intlPrefix + '.column.accountCategory' }),
dataIndex: 'accountCategory',
valueType: 'select',
valueEnum: {
COMPANY_ACCOUNT: {
text: intl.formatMessage({
id: intlPrefix + '.accountCategory.company',
}),
},
PRIVATE_ACCOUNT: {
text: intl.formatMessage({
id: intlPrefix + '.accountCategory.private',
}),
},
},
valueEnum: accountCategoryMap,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.bankName' }),

View File

@ -16,6 +16,7 @@ import { useIntl } from '@@/exports';
import {
DrawerForm,
ProCard,
ProFormDateTimePicker,
ProFormDependency,
ProFormItem,
ProFormMoney,
@ -24,6 +25,7 @@ import {
} from '@ant-design/pro-components';
import { Col, message, Row, Space, Table } from 'antd';
import dayjs from 'dayjs';
import React from 'react';
export interface IPaymentTaskPayProps {
insertPosition?: InsertPosition;
@ -205,7 +207,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
</Row>
</ProCard>
</ProFormItem>
{/* 模块2瓜农车次列表 */}
<ProFormDependency name={['orderSupplierVOList']}>
{({ orderSupplierVOList }) => {
@ -307,7 +308,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
);
}}
</ProFormDependency>
{/* 模块3本次付款 */}
<ProFormMoney
name="paidAmount"
@ -337,7 +337,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
},
]}
/>
{/* 收款人 */}
<SupplierSelect
key={'supplierId'}
@ -365,7 +364,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
},
]}
/>
{/* 选择付款账户 */}
<CompanyPaymentAccountSelect
name={'companyPaymentAccountId'}
@ -382,8 +380,26 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
},
]}
/>
{/* 上传付款凭证 */}
<ProFormDateTimePicker
key="paidAt"
name="paidAt"
label={intl.formatMessage({
id: intlPrefix + '.form.paidAt.label',
})}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.paidAt.placeholder',
})}
required
rules={[
{
required: true,
message: intl.formatMessage({
id: intlPrefix + '.form.paidAt.required',
}),
},
]}
/>
,{/* 上传付款凭证 */}
<ProFormUploadMaterial
key={'paidCredentials'}
label={intl.formatMessage({
@ -394,7 +410,6 @@ export default function PaymentTaskPay(props: IPaymentTaskPayProps) {
maxCount: 9,
}}
/>
{/* 付款备注 */}
<ProFormDependency key={'remark'} name={['remark']}>
{({ remark }, form) => {

View File

@ -0,0 +1,66 @@
import { ReconciliationInvoiceModal } from '@/components';
import { ProFormSelect } from '@ant-design/pro-components';
import { ProFieldFCRenderProps } from '@ant-design/pro-provider';
import { useState } from 'react';
export interface IReconciliationInvoiceFormItemProps extends Omit<
ProFieldFCRenderProps,
'value' | 'onChange'
> {
value?: BusinessAPI.ReconciliationInvoiceVO['reconciliationInvoiceId'];
onChange?: (
value?: BusinessAPI.ReconciliationInvoiceVO['reconciliationInvoiceId'],
) => void;
}
export default function ReconciliationInvoiceFormItem(
props: IReconciliationInvoiceFormItemProps,
) {
const { value, onChange } = props;
const [showReconciliationInvoiceModal, setShowReconciliationInvoiceModal] =
useState<boolean>(false);
const [reconciliationInvoiceList, setReconciliationInvoiceList] =
useState<(BusinessAPI.ReconciliationInvoiceVO | undefined)[]>();
return (
<>
<ProFormSelect
fieldProps={{
open: false,
onClear: () => {
onChange?.(undefined);
},
onClick: () => {
setShowReconciliationInvoiceModal(true);
},
value: value,
placeholder: '请选择对账开票',
options: reconciliationInvoiceList?.map(
(reconciliationInvoiceVO?: BusinessAPI.ReconciliationInvoiceVO) => {
return {
value: reconciliationInvoiceVO?.reconciliationInvoiceId,
label: reconciliationInvoiceVO?.invoiceSn,
};
},
),
}}
/>
<ReconciliationInvoiceModal
title={'选择对账开票'}
open={showReconciliationInvoiceModal}
onOk={() => setShowReconciliationInvoiceModal(false)}
onCancel={() => setShowReconciliationInvoiceModal(false)}
onFinish={async (reconciliationInvoiceVOList) => {
if (reconciliationInvoiceVOList.length > 0) {
const reconciliationInvoiceVO = reconciliationInvoiceVOList[0];
onChange?.(reconciliationInvoiceVO?.reconciliationInvoiceId);
setReconciliationInvoiceList(reconciliationInvoiceVOList);
setShowReconciliationInvoiceModal(false);
}
}}
type={'radio'}
/>
</>
);
}

View File

@ -23,7 +23,8 @@ import React from 'react';
interface IReconciliationInvoiceListProps {
ghost?: boolean;
reconciliationId?: BusinessAPI.ReconciliationInvoiceVO['reconciliationId'];
reconciliationId?: BusinessAPI.ReconciliationVO['reconciliationId'];
reconciliationVO?: BusinessAPI.ReconciliationVO;
dealerId?: BusinessAPI.ReconciliationInvoiceVO['dealerId'];
search?: boolean;
onValueChange?: () => void;
@ -37,6 +38,7 @@ export default function ReconciliationInvoiceList(
const {
ghost = false,
reconciliationId,
reconciliationVO,
dealerId,
search = true,
mode = 'page',
@ -337,6 +339,16 @@ export default function ReconciliationInvoiceList(
formType: 'drawer',
formContext,
trigger,
initValues: {
...(reconciliationVO && {
reconciliationId: reconciliationVO.reconciliationId,
reconciliationVO: reconciliationVO,
canChangeReconciliation: false,
dealerId: reconciliationVO.dealerId,
dealerVO: reconciliationVO.dealerVO,
canChangeDealer: false,
}),
},
}}
update={false}
destroy={false}

View File

@ -0,0 +1,331 @@
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';
import {
ActionType,
LightFilter,
ProColumns,
} from '@ant-design/pro-components';
import { Alert, ModalProps, Row, Space, Tag } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
export interface IReconciliationInvoiceModalProps extends ModalProps {
title: string;
selectedList?: BusinessAPI.ReconciliationInvoiceVO[];
onFinish: (
reconciliationInvoiceVOList: BusinessAPI.ReconciliationInvoiceVO[],
) => void;
type: 'checkbox' | 'radio' | undefined;
params?: BusinessAPI.ReconciliationInvoicePageQry;
num?: number;
tips?: string;
extraFilter?: React.ReactNode[];
extraColumns?: ProColumns<BusinessAPI.ReconciliationInvoiceVO>[];
}
export default function ReconciliationInvoiceModal(
props: IReconciliationInvoiceModalProps,
) {
const {
title,
onFinish,
type,
selectedList,
params: initParams,
num = 10,
tips,
extraFilter = [],
extraColumns: initExtraColumns = [],
...rest
} = props;
const actionRef = useRef<ActionType>();
const sessionKey = `reconciliationInvoiceList`;
const [params, setParams] =
useState<BusinessAPI.ReconciliationInvoicePageQry>(initParams || {});
useEffect(() => {
if (initParams) {
setParams({
...params,
...initParams,
});
}
}, [initParams]);
const intl = useIntl();
const intlPrefix = 'reconciliationInvoice';
const columns: ProColumns<BusinessAPI.ReconciliationInvoiceVO>[] = [
{
title: intl.formatMessage({
id: intlPrefix + '.column.invoiceSn',
}),
dataIndex: 'invoiceSn',
key: 'invoiceSn',
renderText: (text: string) => <span className="font-medium">{text}</span>,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.dealer' }),
dataIndex: 'dealerVO',
key: 'dealerId',
search: false,
render: (_, record) =>
record.dealerVO?.shortName || record.dealerVO?.fullName,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.company' }),
dataIndex: 'companyVO',
key: 'companyId',
search: false,
render: (_, record) =>
record.companyVO?.shortName || record.companyVO?.fullName,
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.invoiceAmount',
}),
dataIndex: 'invoiceAmount',
key: 'invoiceAmount',
valueType: 'money',
search: false,
render: (_, record) => (
<span className="text-green-600 font-medium">
{formatCurrency(record.invoiceAmount)}
</span>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.invoiceDate' }),
dataIndex: 'invoiceDate',
key: 'invoiceDate',
valueType: 'dateTime',
search: false,
},
...(initExtraColumns || []),
];
function setReconciliationInvoiceVOStorage(
reconciliationInvoiceVO: BusinessAPI.ReconciliationInvoiceVO,
) {
const localReconciliationInvoiceList = localStorage.getItem(sessionKey);
const reconciliationInvoiceList = localReconciliationInvoiceList
? JSON.parse(localReconciliationInvoiceList)
: [];
reconciliationInvoiceList.forEach(
(item: BusinessAPI.ReconciliationInvoiceVO, index: number) => {
if (
item.reconciliationInvoiceId ===
reconciliationInvoiceVO.reconciliationInvoiceId
) {
reconciliationInvoiceList.splice(index, 1);
}
},
);
if (reconciliationInvoiceList.length < 5) {
reconciliationInvoiceList.unshift(reconciliationInvoiceVO);
localStorage.setItem(
sessionKey,
JSON.stringify(reconciliationInvoiceList),
);
} else {
reconciliationInvoiceList.pop();
reconciliationInvoiceList.unshift(reconciliationInvoiceVO);
localStorage.setItem(
sessionKey,
JSON.stringify(reconciliationInvoiceList),
);
}
}
return (
<SelectModal<
BusinessAPI.ReconciliationInvoiceVO,
BusinessAPI.ReconciliationInvoicePageQry
>
rowKey={'reconciliationInvoiceId'}
modalProps={{
title: title || '选择对账开票',
...rest,
destroyOnHidden: true,
afterOpenChange: (open) => {
if (!open) {
setParams({
...initParams,
});
}
},
}}
selectedList={selectedList}
tableProps={{
rowKey: 'reconciliationInvoiceId',
columns: columns,
columnsState: {
persistenceType: 'sessionStorage',
persistenceKey: 'reconciliationInvoiceModalColumnStateKey',
},
params: {
...params,
},
request: async (params, sorter, filter) => {
const { data, success, totalCount } =
await business.reconciliationInvoice.pageReconciliationInvoice({
reconciliationInvoicePageQry: formatParam<typeof params>(
params,
sorter,
filter,
),
});
return {
data: data || [],
total: totalCount,
success,
};
},
pagination: {
...pagination(),
position: ['bottomRight'],
},
tableAlertRender: ({ selectedRowKeys, selectedRows }) => {
// selectedRows 和 selectedList 组合在一起,去重
const selectedRowsMap = new Map<
string,
BusinessAPI.ReconciliationInvoiceVO
>();
selectedRows.forEach((item: BusinessAPI.ReconciliationInvoiceVO) => {
if (item) {
if (!selectedRowsMap.has(item.reconciliationInvoiceId)) {
selectedRowsMap.set(item.reconciliationInvoiceId, item);
}
}
});
selectedList?.forEach((item: BusinessAPI.ReconciliationInvoiceVO) => {
if (!selectedRowsMap.has(item.reconciliationInvoiceId)) {
selectedRowsMap.set(item.reconciliationInvoiceId, item);
}
});
let selectedTempList: BusinessAPI.ReconciliationInvoiceVO[] = [];
selectedRowsMap.forEach(
(item: BusinessAPI.ReconciliationInvoiceVO) => {
if (selectedRowKeys.includes(item.reconciliationInvoiceId)) {
selectedTempList.push(item);
}
},
);
return (
<Space size={12}>
<span> {selectedRowKeys.length} </span>
<Space wrap={true}>
{selectedTempList?.map(
(item: BusinessAPI.ReconciliationInvoiceVO) => {
return (
item && (
<span key={item.reconciliationInvoiceId}>
{item.invoiceSn}
</span>
)
);
},
)}
</Space>
</Space>
);
},
...(tips && {
tableExtraRender: () => {
return tips && <Alert type={'info'} message={tips} />;
},
}),
...(type === 'radio' && {
tableExtraRender: () => {
const localReconciliationInvoiceList =
localStorage.getItem(sessionKey);
if (localReconciliationInvoiceList) {
const reconciliationInvoiceList = JSON.parse(
localReconciliationInvoiceList,
);
return (
<>
{tips && <Alert type={'info'} message={tips} />}
<Row gutter={16}>
{reconciliationInvoiceList.map(
(item: BusinessAPI.ReconciliationInvoiceVO) => {
return (
<Tag
style={{
cursor: 'pointer',
}}
onClick={async () => {
const { data: reconciliationInvoiceVO } =
await business.reconciliationInvoice.showReconciliationInvoice(
{
reconciliationInvoiceShowQry: {
reconciliationInvoiceId:
item.reconciliationInvoiceId,
},
},
);
if (reconciliationInvoiceVO) {
onFinish([reconciliationInvoiceVO]);
setReconciliationInvoiceVOStorage(
reconciliationInvoiceVO,
);
}
}}
key={item.reconciliationInvoiceId}
>
{item.invoiceSn}
</Tag>
);
},
)}
</Row>
</>
);
}
},
}),
actionRef: actionRef,
toolbar: {
actions: [],
filter: (
<LightFilter
onFinish={async (values) => {
setParams({
...initParams,
...values,
});
}}
>
{extraFilter}
</LightFilter>
),
search: {
placeholder: '请输入发票编码',
onSearch: async (value: string) => {
setParams({
...params,
invoiceSn: value,
});
},
},
},
}}
onFinish={(reconciliationInvoiceVOList) => {
if (type === 'radio') {
if (reconciliationInvoiceVOList.length > 0) {
setReconciliationInvoiceVOStorage(reconciliationInvoiceVOList[0]);
}
}
onFinish(reconciliationInvoiceVOList);
}}
num={num}
type={type}
/>
);
}

View File

@ -0,0 +1,79 @@
import {
IReconciliationInvoiceModalProps,
ReconciliationInvoiceModal,
} from '@/components';
import {
FormInstance,
ProFormSelect,
ProFormSelectProps,
} from '@ant-design/pro-components';
import { useState } from 'react';
export interface IReconciliationInvoiceSearchProps extends ProFormSelectProps {
form: FormInstance;
selectedList?: IReconciliationInvoiceModalProps['selectedList'];
onFinish?: (
reconciliationInvoiceVOList: BusinessAPI.ReconciliationInvoiceVO[],
) => void;
params: BusinessAPI.ReconciliationInvoicePageQry;
}
export default function ReconciliationInvoiceSearch(
props: IReconciliationInvoiceSearchProps,
) {
const { form, selectedList, onFinish, params, ...rest } = props;
const [showReconciliationInvoiceModal, setShowReconciliationInvoiceModal] =
useState<boolean>(false);
const [reconciliationInvoiceList, setReconciliationInvoiceList] =
useState<(BusinessAPI.ReconciliationInvoiceVO | undefined)[]>();
return (
<>
<ProFormSelect
{...rest}
fieldProps={{
open: false,
onClick: () => {
setShowReconciliationInvoiceModal(true);
},
placeholder: '请选择对账开票',
options: reconciliationInvoiceList?.map(
(reconciliationInvoiceVO?: BusinessAPI.ReconciliationInvoiceVO) => {
return {
value: reconciliationInvoiceVO?.reconciliationInvoiceId,
label: reconciliationInvoiceVO?.invoiceSn,
};
},
),
}}
/>
<ReconciliationInvoiceModal
title={'选择对账开票'}
open={showReconciliationInvoiceModal}
onOk={() => setShowReconciliationInvoiceModal(false)}
onCancel={() => setShowReconciliationInvoiceModal(false)}
selectedList={selectedList}
onFinish={async (reconciliationInvoiceVOList) => {
if (reconciliationInvoiceVOList.length > 0) {
const reconciliationInvoiceVO = reconciliationInvoiceVOList[0];
form.setFieldsValue({
reconciliationInvoiceId:
reconciliationInvoiceVO?.reconciliationInvoiceId,
reconciliationInvoiceVO: reconciliationInvoiceVO,
});
form.validateFields([
'reconciliationInvoiceId',
'reconciliationInvoiceVO',
]);
setReconciliationInvoiceList(reconciliationInvoiceVOList);
setShowReconciliationInvoiceModal(false);
onFinish?.(reconciliationInvoiceVOList);
}
}}
type={'radio'}
params={params}
/>
</>
);
}

View File

@ -0,0 +1,57 @@
import { ReconciliationInvoiceSearch } from '@/components';
import { useIntl } from '@@/exports';
import {
ProFormDependency,
ProFormSelectProps,
} from '@ant-design/pro-components';
export type IReconciliationInvoiceSelectProps = {
onFinish?: (
reconciliationInvoiceVOList: BusinessAPI.ReconciliationInvoiceVO[],
) => void;
params: BusinessAPI.ReconciliationInvoicePageQry;
} & ProFormSelectProps;
export default function ReconciliationInvoiceSelect(
props: IReconciliationInvoiceSelectProps,
) {
const intl = useIntl();
return (
<ProFormDependency
name={['reconciliationInvoiceVO', 'canChangeReconciliationInvoice']}
>
{({ reconciliationInvoiceVO, canChangeReconciliationInvoice }, form) => {
return (
<ReconciliationInvoiceSearch
{...(canChangeReconciliationInvoice !== undefined && {
readonly: !canChangeReconciliationInvoice,
})}
className={'reconciliation-invoice-select'}
form={form}
{...(reconciliationInvoiceVO && {
selectedList: [reconciliationInvoiceVO],
})}
label={intl.formatMessage({
id: 'form.reconciliationInvoiceId.label',
})}
name={'reconciliationInvoiceId'}
required={true}
placeholder={intl.formatMessage({
id: 'form.reconciliationInvoiceId.placeholder',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'form.reconciliationInvoiceId.required',
}),
},
]}
{...props}
/>
);
}}
</ProFormDependency>
);
}

View File

@ -1,9 +1,11 @@
import {
BizContainer,
BizValueType,
ButtonAccess,
ModeType,
ReconciliationComplete,
ReconciliationCreate,
ReconciliationInvoiceList,
} from '@/components';
import { business } from '@/services';
import { formatCurrency } from '@/utils/format';
@ -279,7 +281,7 @@ export default function ReconciliationList(
},
columns,
options: (record, actionRef) => [
record.state && (
record.state === 'PENDING' && (
<ReconciliationComplete
key={'complete'}
reconciliationVO={record}
@ -290,6 +292,24 @@ export default function ReconciliationList(
}}
/>
),
record.state === 'RECONCILED' && (
<ReconciliationInvoiceList
mode={'create'}
ghost={true}
search={false}
reconciliationVO={record}
trigger={() => (
<ButtonAccess
permission={''}
key={'invoice'}
type={'link'}
size={'small'}
>
</ButtonAccess>
)}
/>
),
],
}}
create={false}

View File

@ -1,8 +1,24 @@
import { BizContainer, BizValueType, ModeType } from '@/components';
import {
BizContainer,
BizValueType,
CompanyPaymentAccountSelect,
DealerSelect,
ModeType,
ProFormUploadMaterial,
ReconciliationInvoiceSelect,
ReconciliationSelect,
} from '@/components';
import { business } from '@/services';
import { formatCurrency } from '@/utils/format';
import { useIntl } from '@@/exports';
import { ProColumns } from '@ant-design/pro-components';
import {
ProColumns,
ProFormDateTimePicker,
ProFormDependency,
ProFormMoney,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import React from 'react';
@ -32,39 +48,27 @@ export default function ReconciliationPaymentList(
const intlPrefix = 'reconciliationPayment';
// 账户类别映射
const accountCategoryMap: Record<number, { label: string; color: string }> = {
1: {
label: intl.formatMessage({
id: intlPrefix + '.accountCategory.corporate',
}),
color: 'processing',
const accountCategoryMap: Record<string, { text: string }> = {
COMPANY_ACCOUNT: {
text: intl.formatMessage({ id: intlPrefix + '.accountCategory.company' }),
},
2: {
label: intl.formatMessage({
id: intlPrefix + '.accountCategory.private',
}),
color: 'warning',
PRIVATE_ACCOUNT: {
text: intl.formatMessage({ id: intlPrefix + '.accountCategory.private' }),
},
};
// 账户类型映射
const accountTypeMap: Record<number, { label: string; color: string }> = {
1: {
label: intl.formatMessage({
id: intlPrefix + '.accountType.bankCard',
}),
color: 'default',
},
2: {
label: intl.formatMessage({
id: intlPrefix + '.accountType.alipay',
}),
const accountTypeMap: Record<string, { text: string; color: string }> = {
BANK_CARD: {
text: intl.formatMessage({ id: intlPrefix + '.accountType.bankCard' }),
color: 'blue',
},
3: {
label: intl.formatMessage({
id: intlPrefix + '.accountType.wechat',
}),
ALIPAY: {
text: intl.formatMessage({ id: intlPrefix + '.accountType.alipay' }),
color: 'cyan',
},
WECHAT: {
text: intl.formatMessage({ id: intlPrefix + '.accountType.wechat' }),
color: 'green',
},
};
@ -87,98 +91,46 @@ export default function ReconciliationPaymentList(
key: 'companyId',
valueType: 'company',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.accountCategory' }),
dataIndex: 'accountCategory',
key: 'accountCategory',
valueType: 'select',
valueEnum: {
1: {
text: accountCategoryMap[1].label,
status: accountCategoryMap[1].color as any,
},
2: {
text: accountCategoryMap[2].label,
status: accountCategoryMap[2].color as any,
},
},
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.accountType' }),
dataIndex: 'accountType',
key: 'accountType',
valueType: 'select',
valueEnum: {
1: {
text: accountTypeMap[1].label,
status: accountTypeMap[1].color as any,
},
2: {
text: accountTypeMap[2].label,
status: accountTypeMap[2].color as any,
},
3: {
text: accountTypeMap[3].label,
status: accountTypeMap[3].color as any,
},
},
search: false,
valueEnum: accountTypeMap,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.bankName' }),
dataIndex: 'bankName',
key: 'bankName',
ellipsis: true,
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.branchName' }),
dataIndex: 'branchName',
key: 'branchName',
ellipsis: true,
search: false,
title: intl.formatMessage({ id: intlPrefix + '.column.accountCategory' }),
dataIndex: 'accountCategory',
key: 'accountCategory',
valueType: 'select',
valueEnum: accountCategoryMap,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.accountName' }),
dataIndex: 'accountName',
key: 'accountName',
ellipsis: true,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.accountNumber' }),
dataIndex: 'accountNumber',
key: 'accountNumber',
ellipsis: true,
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.returnAmount' }),
dataIndex: 'returnAmount',
key: 'returnAmount',
title: intl.formatMessage({ id: intlPrefix + '.column.paidAmount' }),
dataIndex: 'paidAmount',
key: 'paidAmount',
valueType: 'money',
search: false,
render: (_, record) => (
<span className="text-green-600 font-medium">
{formatCurrency(record.returnAmount)}
</span>
),
renderText: (value: number) => formatCurrency(value),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.returnAt' }),
dataIndex: 'returnAt',
key: 'returnAt',
title: intl.formatMessage({ id: intlPrefix + '.column.paidAt' }),
dataIndex: 'paidAt',
key: 'paidAt',
valueType: 'dateTime',
search: false,
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.remark' }),
dataIndex: 'remark',
key: 'remark',
valueType: 'remark',
ellipsis: true,
search: false,
},
];
const detailColumns: ProDescriptionsItemProps<
@ -189,6 +141,149 @@ export default function ReconciliationPaymentList(
BizValueType
>[];
// 定义创建表单字段
const formContext = [
<DealerSelect name={'dealerId'} key={'dealerId'} />,
<ProFormDependency key={'reconciliationId'} name={['dealerId']}>
{({ dealerId }) => {
return (
<ReconciliationSelect
name="reconciliationId"
params={{
dealerId: dealerId,
}}
/>
);
}}
</ProFormDependency>,
<ProFormDependency
key={'reconciliationInvoiceId'}
name={['reconciliationId']}
>
{({ reconciliationId }) => {
return (
<ReconciliationInvoiceSelect
name="reconciliationInvoiceId"
params={{
reconciliationId: reconciliationId,
}}
/>
);
}}
</ProFormDependency>,
<ProFormSelect
key="companyId"
name="companyId"
label={intl.formatMessage({
id: intlPrefix + '.form.companyId.label',
})}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.companyId.placeholder',
})}
required
rules={[
{
required: true,
message: intl.formatMessage({
id: intlPrefix + '.form.companyId.required',
}),
},
]}
request={async (params) => {
const { data } = await business.company.listCompany({
companyListQry: {
...params,
},
});
return (
data?.map((item) => ({
label: item.fullName,
value: item.companyId,
})) || []
);
}}
/>,
<ProFormText
key={'companyPaymentAccountVO'}
name={'companyPaymentAccountVO'}
hidden={true}
transform={(companyPaymentAccountVO) => {
return {
companyPaymentAccountVO,
...companyPaymentAccountVO,
};
}}
/>,
<ProFormDependency key={'accountId'} name={['companyId']}>
{({ companyId }) => {
return (
<CompanyPaymentAccountSelect
name="companyPaymentAccountId"
params={{
companyId: companyId,
}}
transform={(companyPaymentAccountId) => {
return {
companyPaymentAccountId: companyPaymentAccountId,
accountId: companyPaymentAccountId,
};
}}
/>
);
}}
</ProFormDependency>,
<ProFormMoney
key="paidAmount"
name="paidAmount"
label={intl.formatMessage({
id: intlPrefix + '.form.paidAmount.label',
})}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.paidAmount.placeholder',
})}
required
rules={[
{
required: true,
message: intl.formatMessage({
id: intlPrefix + '.form.paidAmount.required',
}),
},
]}
fieldProps={{
precision: 2,
min: 0.01,
}}
/>,
<ProFormDateTimePicker
key="paidAt"
name="paidAt"
label={intl.formatMessage({ id: intlPrefix + '.form.paidAt.label' })}
placeholder={intl.formatMessage({
id: intlPrefix + '.form.paidAt.placeholder',
})}
required
rules={[
{
required: true,
message: intl.formatMessage({
id: intlPrefix + '.form.paidAt.required',
}),
},
]}
/>,
<ProFormUploadMaterial
key={'paidCredentials'}
label={intl.formatMessage({
id: intlPrefix + '.form.paidCredentials.label',
})}
name={'paidCredentials'}
fieldProps={{
maxCount: 9,
}}
/>,
];
return (
<BizContainer<
typeof business.reconciliationPayment,
@ -221,7 +316,11 @@ export default function ReconciliationPaymentList(
columns,
options: () => [],
}}
create={false}
create={{
formType: 'drawer',
formContext,
trigger,
}}
update={false}
destroy={false}
detail={{

View File

@ -4,7 +4,7 @@ import {
ProFormSelect,
ProFormSelectProps,
} from '@ant-design/pro-components';
import { useState } from 'react';
import { useEffect, useState } from 'react';
export interface IReconciliationSearchProps extends ProFormSelectProps {
form: FormInstance;
@ -21,7 +21,11 @@ export default function ReconciliationSearch(
const [showReconciliationModal, setShowReconciliationModal] =
useState<boolean>(false);
const [reconciliationList, setReconciliationList] =
useState<(BusinessAPI.ReconciliationVO | undefined)[]>();
useState<BusinessAPI.ReconciliationVO[]>();
useEffect(() => {
setReconciliationList(selectedList);
}, [selectedList]);
return (
<>

View File

@ -5,6 +5,12 @@ export {
export { default as ReconciliationCreate } from './ReconciliationCreate';
export { default as ReconciliationFormItem } from './ReconciliationFormItem';
export { default as ReconciliationInvoiceList } from './ReconciliationInvoiceList';
export {
default as ReconciliationInvoiceModal,
type IReconciliationInvoiceModalProps,
} from './ReconciliationInvoiceModal';
export { default as ReconciliationInvoiceSearch } from './ReconciliationInvoiceSearch';
export { default as ReconciliationInvoiceSelect } from './ReconciliationInvoiceSelect';
export { default as ReconciliationList } from './ReconciliationList';
export {
default as ReconciliationModal,

View File

@ -3028,6 +3028,11 @@ export default {
placeholder: '请选择对账单',
required: '对账单为必填项',
},
reconciliationInvoiceId: {
label: '对账开票',
placeholder: '请选择对账开票',
required: '请选择对账开票',
},
},
expenseRecord: {
column: {
@ -3065,7 +3070,7 @@ export default {
reconciliationAmount: '对账金额',
state: '状态',
createdAt: '创建时间',
remark: '备注',
remark: '对账备注',
option: '操作',
action: '操作',
},
@ -3140,7 +3145,7 @@ export default {
company: '入账公司',
invoiceAmount: '发票金额',
invoiceDate: '发票日期',
remark: '备注',
remark: '发票备注',
createdAt: '创建时间',
option: '操作',
},
@ -3221,14 +3226,14 @@ export default {
branchName: '支行名称',
accountName: '账户名称',
accountNumber: '账号',
returnAmount: '回款金额',
returnAt: '回款时间',
remark: '备注',
paidAmount: '回款金额',
paidAt: '回款时间',
remark: '回款备注',
createdAt: '创建时间',
option: '操作',
},
accountCategory: {
corporate: '对公账户',
company: '对公账户',
private: '私人账户',
},
accountType: {
@ -3262,5 +3267,43 @@ export default {
},
},
},
form: {
reconciliationId: {
label: '对账单',
},
dealerId: {
label: '经销商',
},
companyId: {
label: '收款公司',
placeholder: '请选择收款公司',
required: '请选择收款公司',
},
accountId: {
label: '收款账户',
placeholder: '请选择收款账户',
required: '请选择收款账户',
},
paidAmount: {
label: '回款金额',
placeholder: '请输入回款金额',
required: '请输入回款金额',
},
paidAt: {
label: '回款时间',
placeholder: '请选择回款时间',
required: '请选择回款时间',
},
paidCredentials: {
label: '回款凭证',
placeholder: '请上传回款凭证',
required: '请上传回款凭证',
tip: '拖拽凭证文件到此处或点击选择文件(支持 PDF、JPG、PNG 格式,单个文件不超过 10MB',
},
remark: {
label: '回款备注',
placeholder: '请输入回款备注',
},
},
},
};

View File

@ -5773,9 +5773,9 @@ declare namespace BusinessAPI {
/** 我方收款公司账户ID */
accountId: string;
/** 账户类别1_对公账户2_私人账户 */
accountCategory: boolean;
accountCategory: 'COMPANY_ACCOUNT' | 'PRIVATE_ACCOUNT';
/** 账户类型1_银行卡2_支付宝3_微信 */
accountType: boolean;
accountType: 'BANK_CARD' | 'ALIPAY' | 'WECHAT';
/** 银行名称 */
bankName?: string;
/** 支行名称 */
@ -5785,9 +5785,11 @@ declare namespace BusinessAPI {
/** 银行账号&#x2F;支付宝账号&#x2F;微信账号 */
accountNumber: string;
/** 回款时间 */
returnAt?: string;
paidAt?: string;
/** 回款金额 */
returnAmount: number;
paidAmount: number;
/** 回款凭证 */
paidCredentials?: string[];
/** 备注 */
remark?: string;
/** 创建时间 */
@ -5845,9 +5847,9 @@ declare namespace BusinessAPI {
/** 我方收款公司账户ID */
accountId: string;
/** 账户类别1_对公账户2_私人账户 */
accountCategory: boolean;
accountCategory: 'COMPANY_ACCOUNT' | 'PRIVATE_ACCOUNT';
/** 账户类型1_银行卡2_支付宝3_微信 */
accountType: boolean;
accountType: 'BANK_CARD' | 'ALIPAY' | 'WECHAT';
/** 银行名称 */
bankName?: string;
/** 支行名称 */
@ -5857,9 +5859,11 @@ declare namespace BusinessAPI {
/** 银行账号&#x2F;支付宝账号&#x2F;微信账号 */
accountNumber: string;
/** 回款时间 */
returnAt?: string;
paidAt?: string;
/** 回款金额 */
returnAmount: number;
paidAmount: number;
/** 回款凭证 */
paidCredentials?: string[];
/** 备注 */
remark?: string;
/** 创建时间 */
@ -5878,9 +5882,9 @@ declare namespace BusinessAPI {
/** 我方收款公司账户ID */
accountId: string;
/** 账户类别1_对公账户2_私人账户 */
accountCategory: boolean;
accountCategory: 'COMPANY_ACCOUNT' | 'PRIVATE_ACCOUNT';
/** 账户类型1_银行卡2_支付宝3_微信 */
accountType: boolean;
accountType: 'BANK_CARD' | 'ALIPAY' | 'WECHAT';
/** 银行名称 */
bankName?: string;
/** 支行名称 */
@ -5890,9 +5894,11 @@ declare namespace BusinessAPI {
/** 银行账号&#x2F;支付宝账号&#x2F;微信账号 */
accountNumber: string;
/** 回款时间 */
returnAt?: string;
paidAt?: string;
/** 回款金额 */
returnAmount: number;
paidAmount: number;
/** 回款凭证 */
paidCredentials?: string[];
/** 备注 */
remark?: string;
/** 创建时间 */

64
scripts/fix-types.js Normal file
View File

@ -0,0 +1,64 @@
const fs = require("fs");
const path = require("path");
function processFile(filePath) {
try {
let content = fs.readFileSync(filePath, "utf8");
const original = content;
// 替换所有 number[] 为 string[]
content = content.replace(/number\[\]/g, "string[]");
// 可选:替换其他相关类型
content = content.replace(/Array<number>/g, "Array<string>");
content = content.replace(
/ReadonlyArray<number>/g,
"ReadonlyArray<string>",
);
if (content !== original) {
fs.writeFileSync(filePath, content, "utf8");
console.log(`✓ Updated: ${path.relative(process.cwd(), filePath)}`);
return true;
}
return false;
} catch (error) {
console.error(`✗ Error processing ${filePath}:`, error.message);
return false;
}
}
function processDirectory(dirPath) {
let updatedCount = 0;
function walk(dir) {
const items = fs.readdirSync(dir);
for (const item of items) {
const fullPath = path.join(dir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
walk(fullPath);
} else if (item.endsWith(".ts") || item.endsWith(".tsx")) {
if (processFile(fullPath)) {
updatedCount++;
}
}
}
}
walk(dirPath);
return updatedCount;
}
// 主程序
const targetDir = "src/services";
if (fs.existsSync(targetDir)) {
console.log(`Processing directory: ${targetDir}`);
const updated = processDirectory(targetDir);
console.log(`\nDone! Updated ${updated} files.`);
} else {
console.error(`Directory not found: ${targetDir}`);
process.exit(1);
}

File diff suppressed because one or more lines are too long