ERPTurbo_Admin/packages/app-operation/src/components/Order/OrderSupplierList.tsx
shenyifei f5feec84e3 feat(biz): 添加供应商发票功能并优化表格组件
- 在BizProvider中新增supplierInvoice类型支持
- 添加SupplierInvoiceList组件用于显示供应商发票信息
- 为经销商显示添加市场/超市标识区分
- 更新公司支付账户模态框类型定义以支持业务值类型
- 优化订单成本列表中的公司字段显示方式
- 在订单供应商列表中新增发票上传状态显示
- 为订单供应商模态框添加开票状态逻辑处理
- 更新多个模态框的列配置以支持业务值类型
- 调整选择模态框布局大小和分页配置
- 移除支付记录列表的编辑删除功能
- 优化订单供应商选择列表的发票相关字段显示
2026-01-14 11:45:56 +08:00

522 lines
13 KiB
TypeScript

import {
BizContainer,
BizValueType,
ButtonAccess,
ModeType,
} from '@/components';
import { business } from '@/services';
import { formatBankCard, formatIdCard, formatPhone } from '@/utils/format';
import { useIntl } from '@@/exports';
import { EyeInvisibleOutlined, EyeTwoTone } from '@ant-design/icons';
import { ProColumns } from '@ant-design/pro-components';
import { ProDescriptionsItemProps } from '@ant-design/pro-descriptions';
import { Button, Image, Modal, Space, Tag } from 'antd';
import React, { useState } from 'react';
interface IOrderSupplierListProps {
ghost?: boolean;
orderId?: BusinessAPI.OrderVO['orderId'];
supplierInvoiceVO?: BusinessAPI.SupplierInvoiceVO;
search?: boolean;
onValueChange?: () => void;
mode?: ModeType;
trigger?: () => React.ReactNode;
}
export default function OrderSupplierList(props: IOrderSupplierListProps) {
const {
ghost = false,
orderId,
search = true,
mode = 'page',
trigger,
onValueChange,
supplierInvoiceVO,
} = props;
const intl = useIntl();
const intlPrefix = 'orderSupplier';
const [showIdCard, setShowIdCard] = useState<Record<string, boolean>>({});
const [showBankCard, setShowBankCard] = useState<Record<string, boolean>>({});
const [showPhone, setShowPhone] = useState<Record<string, boolean>>({});
const [activeKey, setActiveKey] = useState<string>('ALL');
// 发票预览相关状态
const [invoiceVisible, setInvoiceVisible] = useState(false);
const [contractVisible, setContractVisible] = useState(false);
const [wechatQrVisible, setWechatQrVisible] = useState(false);
const [paymentVoucherVisible, setPaymentVoucherVisible] = useState(false);
const [currentRecord, setCurrentRecord] =
useState<BusinessAPI.OrderSupplierVO | null>(null);
const columns: 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.supplierInvoice' }),
dataIndex: 'supplierInvoiceVO',
key: 'supplierInvoiceId',
search: false,
valueType: 'supplierInvoice',
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.invoiceUpload' }),
dataIndex: 'invoiceUpload',
key: 'invoiceUpload',
search: false,
valueType: 'switch',
render: (_, record) => (
<Tag color={record.invoiceUpload ? 'green' : 'red'}>
{record.invoiceUpload
? intl.formatMessage({
id: intlPrefix + '.column.invoiceUpload.uploaded',
})
: intl.formatMessage({
id: intlPrefix + '.column.invoiceUpload.notUploaded',
})}
</Tag>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.contractUpload' }),
dataIndex: 'contractUpload',
key: 'contractUpload',
search: false,
valueType: 'switch',
render: (_, record) => (
<Tag color={record.contractUpload ? 'green' : 'red'}>
{record.contractUpload
? intl.formatMessage({
id: intlPrefix + '.column.contractUpload.uploaded',
})
: intl.formatMessage({
id: intlPrefix + '.column.contractUpload.notUploaded',
})}
</Tag>
),
},
{
title: intl.formatMessage({
id: intlPrefix + '.column.idCard',
}),
dataIndex: 'idCard',
key: 'idCard',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatIdCard(record.idCard, showIdCard[record.orderSupplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowIdCard((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showIdCard[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.phone' }),
dataIndex: 'phone',
key: 'phone',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatPhone(record.phone, showPhone[record.orderSupplierId])}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowPhone((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showPhone[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.bankCard' }),
dataIndex: 'bankCard',
key: 'bankCard',
search: false,
render: (_, record) => (
<div className="flex items-center">
<span>
{formatBankCard(
record?.bankCard || '',
showBankCard[record.orderSupplierId],
)}
</span>
<span
className="ml-2 cursor-pointer"
onClick={() => {
setShowBankCard((prev) => ({
...prev,
[record.orderSupplierId]: !prev[record.orderSupplierId],
}));
}}
>
{showBankCard[record.orderSupplierId] ? (
<EyeTwoTone />
) : (
<EyeInvisibleOutlined />
)}
</span>
</div>
),
},
{
title: intl.formatMessage({ id: intlPrefix + '.column.orderCompany' }),
dataIndex: 'orderCompany',
key: 'companyId',
valueType: 'company',
},
{
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',
},
},
},
];
const detailColumns: ProDescriptionsItemProps<
BusinessAPI.OrderSupplierVO,
BizValueType
>[] = columns as ProDescriptionsItemProps<
BusinessAPI.OrderSupplierVO,
BizValueType
>[];
return (
<>
<BizContainer<
typeof business.orderSupplier,
BusinessAPI.OrderSupplierVO,
BusinessAPI.OrderSupplierPageQry
>
rowKey={'orderSupplierId'}
permission={'operation-order-supplier'}
func={business.orderSupplier}
method={'orderSupplier'}
methodUpper={'OrderSupplier'}
intlPrefix={intlPrefix}
modeType={mode}
onValueChange={onValueChange}
container={{
ghost,
}}
status={false}
page={{
fieldProps: {
bordered: true,
ghost,
//@ts-ignore
search,
params: {
...(activeKey !== 'ALL' && {
isPaid: activeKey! as any,
}),
...(supplierInvoiceVO && {
invoiceId: supplierInvoiceVO.supplierInvoiceId,
}),
poStates: ['COMPLETED'],
type: 'FARMER',
},
tableAlertOptionRender: (props) => {
return (
<Space>
<Button
key={'cancel'}
type={'link'}
onClick={() => props.onCleanSelected()}
size={'middle'}
>
</Button>
</Space>
);
},
toolbar: {
menu: {
type: 'tab',
activeKey: activeKey,
items: [
// 全部
{
key: 'ALL',
label: intl.formatMessage({
id: intlPrefix + '.tab.all',
}),
},
// 已支付
{
key: 'true',
label: intl.formatMessage({
id: intlPrefix + '.tab.paid',
}),
},
// 未支付
{
key: 'false',
label: intl.formatMessage({
id: intlPrefix + '.tab.unpaid',
}),
},
],
onChange: (key) => {
setActiveKey(key as string);
},
},
},
},
columns,
options: (orderSupplierVO) => {
let btn = [];
if (orderSupplierVO.invoiceUpload) {
btn.push(
<ButtonAccess
permission="operation-order-supplier-view-invoice"
key="view-invoice"
type="link"
onClick={() => {
setCurrentRecord(orderSupplierVO);
setInvoiceVisible(true);
}}
>
</ButtonAccess>,
);
}
if (orderSupplierVO.wechatQr) {
btn.push(
<ButtonAccess
permission="operation-order-supplier-view-wechat-qr-code"
key="wechat-qr-code"
type="link"
onClick={() => {
setCurrentRecord(orderSupplierVO);
setWechatQrVisible(true);
}}
>
</ButtonAccess>,
);
}
if (orderSupplierVO.contractUpload) {
btn.push(
<ButtonAccess
permission="operation-order-supplier-view-contract"
key="view-contract"
type="link"
onClick={() => {
setCurrentRecord(orderSupplierVO);
setContractVisible(true);
}}
>
</ButtonAccess>,
);
}
if (orderSupplierVO.isPaid) {
btn.push(
<ButtonAccess
permission="operation-order-supplier-view-payment-voucher"
key="payment-voucher"
type="link"
onClick={() => {
setCurrentRecord(orderSupplierVO);
setPaymentVoucherVisible(true);
}}
>
</ButtonAccess>,
);
}
return btn;
},
}}
create={false}
update={false}
destroy={false}
detail={{
rowId: orderId,
formType: 'drawer',
columns: detailColumns,
trigger,
}}
/>
{/* 发票预览Modal */}
<Modal
title="查看发票"
open={invoiceVisible}
onCancel={() => setInvoiceVisible(false)}
footer={null}
width={800}
centered
>
{currentRecord?.invoiceImg && currentRecord.invoiceImg.length > 0 ? (
<div className="space-y-4">
{currentRecord.invoiceImg.map((item, index) => (
<div key={index} className="text-center">
<p className="mb-2"> {index + 1}</p>
<Image
width="100%"
height={400}
src={item.filePath}
alt={item.fileName || `发票${index + 1}`}
placeholder={<div className="text-gray-400">...</div>}
/>
<p className="text-sm text-gray-500 mt-2">
{item.fileName || `发票${index + 1}`}
</p>
</div>
))}
</div>
) : (
<div className="text-center py-8 text-gray-500"></div>
)}
</Modal>
{/* 合同预览Modal */}
<Modal
title="查看合同"
open={contractVisible}
onCancel={() => setContractVisible(false)}
footer={null}
width={800}
centered
>
{currentRecord?.contractImg && currentRecord.contractImg.length > 0 ? (
<div className="space-y-4">
{currentRecord.contractImg.map((item, index) => (
<div key={index} className="text-center">
<p className="mb-2"> {index + 1}</p>
<Image
width="100%"
height={400}
src={item.filePath}
alt={item.fileName || `合同${index + 1}`}
placeholder={<div className="text-gray-400">...</div>}
/>
<p className="text-sm text-gray-500 mt-2">
{item.fileName || `合同${index + 1}`}
</p>
</div>
))}
</div>
) : (
<div className="text-center py-8 text-gray-500"></div>
)}
</Modal>
{/* 微信收款码预览Modal */}
<Modal
title="查看微信收款码"
open={wechatQrVisible}
onCancel={() => setWechatQrVisible(false)}
footer={null}
width={400}
centered
>
{currentRecord?.wechatQr ? (
<div className="text-center">
<Image
width="100%"
src={currentRecord.wechatQr}
alt="微信收款码"
placeholder={<div className="text-gray-400">...</div>}
/>
</div>
) : (
<div className="text-center py-8 text-gray-500"></div>
)}
</Modal>
{/* 付款凭证预览Modal */}
<Modal
title="查看付款凭证"
open={paymentVoucherVisible}
onCancel={() => setPaymentVoucherVisible(false)}
footer={null}
width={800}
centered
>
<div className="text-center py-8 text-gray-500"></div>
</Modal>
</>
);
}