import ButtonConfirm from '@/components/ButtomConfirm'; import { RESPONSE_CODE } from '@/constants/enum'; import InvoiceRecordDetailModal from '@/pages/Invoice/InvoiceRecord/components/InvoiceRecordDetailModal'; import ManualInvoicingModal from '@/pages/Invoice/InvoiceRecord/components/ManualInvoicingModal'; import { PAYEE_OPTIONS } from '@/pages/Order/constant'; import { postServiceBankStatementEditBankStatement, postServiceConstAfterInvoicingInvoiceRecordStatus, postServiceConstInvoiceType, postServiceInvoiceConfirmInvoice, postServiceInvoiceDownloadInvoice, postServiceInvoiceInvoicing, postServiceInvoiceQueryInvoiceRecordList, postServiceOrderQuerySalesCode, } from '@/services'; import { excelExport } from '@/services/exportRequest'; import { enumToProTableEnumValue, enumToSelect } from '@/utils'; import { ActionType, ModalForm, ProTable } from '@ant-design/pro-components'; import { Button, Divider, Space, Table, Tooltip, message } from 'antd'; import axios from 'axios'; import { useEffect, useRef, useState } from 'react'; const InvoiceRecord = () => { const processedRecordRef = useRef<ActionType>(); const [invoiceTypeValueEnum, setInvoiceTypeValueEnum] = useState({}); const [salesCodeValueEnum, setSalesCodeValueEnum] = useState({}); const [invoiceRecordDetailVisible, setInvoiceRecordDetailVisible] = useState(false); const [invoiceRecord, setInvoiceRecord] = useState({}); const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { async function extracted() { let invoiceTypeRet = await postServiceConstInvoiceType(); setInvoiceTypeValueEnum(invoiceTypeRet.data); } extracted().catch(console.error); }, []); useEffect(() => { async function extracted() { const res = await postServiceOrderQuerySalesCode(); let map = {}; res.data?.forEach((item) => { map[item.userName] = { text: item.userName, status: item.userName, }; }); setSalesCodeValueEnum(map); } extracted().catch(console.error); }, []); const downloadImportTemplate = async (urls) => { messageApi.open({ type: 'loading', content: '下载中', duration: 0, }); axios({ url: '/api/service/invoice/batchDown', method: 'post', responseType: 'blob', headers: { Authorization: localStorage.getItem('token') }, data: { invoiceRecordIds: urls, }, }) .then((response) => { // 创建一个新的 Blob 对象,它包含了服务器响应的数据(即你的 Excel 文件) const blob = new Blob([response.data]); // Excel 的 MIME 类型 const downloadUrl = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = '发票.zip'; // 你可以为文件命名 document.body.appendChild(a); a.click(); // 模拟点击操作来下载文件 URL.revokeObjectURL(downloadUrl); // 释放掉 blob 对象所占用的内存 document.body.removeChild(a); }) .catch((error) => { // 处理错误 console.error('下载错误', error); }) .finally(() => { messageApi.destroy(); }); }; const processedRecordColumns = [ { dataIndex: 'index', valueType: 'indexBorder', width: 50, }, { title: '开票编号', valueType: 'text', dataIndex: 'id', width: 100, copyable: true, ellipsis: true, }, { title: '发票号码', valueType: 'text', dataIndex: 'invoiceNumber', width: 150, copyable: true, ellipsis: true, }, { title: '重开发票', key: 'reissueInvoiceNumbers', width: 150, ellipsis: true, hideInSearch: true, render: (_, record) => { //将record.reissueInvoiceNumbers用,拼接 const numbersText = record.reissueInvoiceNumbers?.join(','); return record.reissueInvoiceNumbers?.map((item) => { return ( <Tooltip title={numbersText} key={item}> <Space key={item}> {item} <Divider type={'vertical'} /> </Space> </Tooltip> ); }); }, }, { title: '开票日期', dataIndex: 'invoicingDate', valueType: 'date', width: 140, hideInSearch: true, ellipsis: true, }, { title: '发票类型', valueType: 'Text', dataIndex: 'typeText', width: 100, hideInSearch: true, ellipsis: true, }, { title: '发票状态', valueType: 'Text', dataIndex: 'statusText', width: 100, hideInSearch: true, ellipsis: true, }, { title: '购方名称', valueType: 'text', dataIndex: 'partyAName', hideInSearch: true, ellipsis: true, }, { title: '联系人', valueType: 'text', dataIndex: 'contacts', width: 100, hideInSearch: true, ellipsis: true, }, { title: '购方税号', valueType: 'text', dataIndex: 'partyATaxid', ellipsis: true, }, { title: '收款单位', valueType: 'text', dataIndex: 'partyBName', hideInSearch: true, ellipsis: true, }, { title: '申请人', valueType: 'text', width: 100, dataIndex: 'createByName', hideInSearch: true, ellipsis: true, }, { title: '开票金额(元)', valueType: 'money', dataIndex: 'price', width: 100, hideInSearch: true, ellipsis: true, }, { title: '备注', valueType: 'text', dataIndex: 'contacts', hideInSearch: true, ellipsis: true, }, { title: '发票下载次数', dataIndex: 'downloadCount', valueType: 'Text', hideInSearch: true, }, { title: '失败原因', valueType: 'text', dataIndex: 'failureReason', hideInSearch: true, ellipsis: true, }, { title: '购方名称', valueType: 'text', dataIndex: 'partyANameLike', hideInTable: true, }, { title: '主订单号', valueType: 'Text', dataIndex: 'mainOrderId', hideInTable: true, }, { title: '子订单号', valueType: 'Text', dataIndex: 'subOrderId', hideInTable: true, }, { title: '发票类型', valueType: 'select', dataIndex: 'type', filters: true, onFilter: true, hideInTable: true, valueEnum: enumToProTableEnumValue(invoiceTypeValueEnum), }, { title: '开票状态', key: 'status', valueType: 'select', dataIndex: 'status', filters: true, onFilter: true, hideInTable: true, request: async () => { const res = await postServiceConstAfterInvoicingInvoiceRecordStatus(); return enumToSelect(res.data); }, }, { title: '申请人', valueType: 'select', dataIndex: 'createByName', filters: true, onFilter: true, hideInTable: true, valueEnum: salesCodeValueEnum, }, { title: '联系人', valueType: 'text', dataIndex: 'contactsLike', hideInTable: true, }, { title: '开票日期', dataIndex: 'invoicingDate', valueType: 'dateRange', hideInTable: true, search: { transform: (value) => { if (value) { return { invoicingDateGe: value[0], invoicingDateLe: value[1], }; } }, }, }, { title: '收款单位', valueType: 'select', dataIndex: 'partyB', filters: true, onFilter: true, hideInTable: true, valueEnum: enumToProTableEnumValue(PAYEE_OPTIONS), }, { title: '操作', valueType: 'option', key: 'option', render: (text, record) => [ <> {record.paths.includes('DETAIL') && ( <a key="detail" onClick={() => { setInvoiceRecordDetailVisible(true); setInvoiceRecord(record); }} > 详情 </a> )} </>, <> {record.status === 'SUCCESS' && record.paths.includes('DOWNLOAD_INVOICE') && ( <a href={record.invoiceAddress} download onClick={() => { postServiceInvoiceDownloadInvoice({ data: record.id, }); }} > 下载发票 </a> )} </>, <> {record.status === 'FAIL' && record.paths.includes('RETRY') && ( <ModalForm title="提示" trigger={ <Button type="link" danger> 重试 </Button> } autoFocusFirstInput modalProps={{ destroyOnClose: true, }} submitTimeout={2000} onFinish={async () => { const res = await postServiceInvoiceInvoicing({ data: { invoiceRecordIds: [record.id], }, }); if (res) { message.success(res.message); processedRecordRef?.current?.reload(); } return true; }} > 确定重试订单信息吗? </ModalForm> )} </>, <> {record.paths.includes('INVOICING') && ( <ManualInvoicingModal key={'ManualInvoicingModal'} record={record} ></ManualInvoicingModal> )} </>, <> {record.paths?.includes('INVOICE_CONFIRM') && ( <ButtonConfirm key="delete" className="p-0" title={'确认发票?'} text="确认发票" onConfirm={async () => { let res = await postServiceInvoiceConfirmInvoice({ data: { invoiceIds: [record.invoice?.id] }, }); if (res.result === RESPONSE_CODE.SUCCESS) { message.success(res.message); processedRecordRef?.current?.reload(); } else { message.error(res.message); } }} /> )} </>, ], }, ]; return ( <div className="invoice-index"> <ProTable columns={processedRecordColumns} actionRef={processedRecordRef} cardBordered pagination={{ showSizeChanger: true, // 显示可以选择每页显示条数的下拉菜单 pageSizeOptions: ['10', '20', '50', '100'], // 设置可以选择的每页显示条数选项 }} editable={{ type: 'multiple', onSave: async (rowKey, data) => { await postServiceBankStatementEditBankStatement({ data: data }); }, actionRender: (row, config, defaultDom) => [ defaultDom.save, defaultDom.cancel, ], }} search={{ labelWidth: 'auto', defaultCollapsed: false, optionRender: (searchConfig, formProps, dom) => [ ...dom, <Button key="out" onClick={() => { const values = searchConfig?.form?.getFieldsValue(); console.log(values); messageApi.open({ type: 'loading', content: '正在导出文件...', }); excelExport( '/api/service/invoice/exportInvoiceRecords', { ...values, statusIn: ['INVOICING', 'SUCCESS', 'FAIL', 'REISSUED'], }, () => { messageApi.destroy(); }, ); }} > 导出 </Button>, ], }} request={async (params) => { let res = await postServiceInvoiceQueryInvoiceRecordList({ data: { ...params, statusIn: ['INVOICING', 'SUCCESS', 'FAIL', 'REISSUED'], }, }); return { data: res?.data?.data, total: res?.data?.total || 0, }; }} columnsState={{ persistenceKey: 'pro-table-singe-demos', persistenceType: 'localStorage', defaultValue: { option: { fixed: 'right', disable: true }, }, onChange(value) { console.log('value: ', value); }, }} rowKey="id" options={{ setting: { listsHeight: 400, }, }} rowSelection={{ // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom // 注释该行则默认不显示下拉选项 selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT], alwaysShowAlert: true, }} tableAlertOptionRender={({ selectedRowKeys, selectedRows }) => { return ( <Space size={16}> <Button type={'primary'} onClick={() => { const ids = selectedRows.map((item) => { return item.id; }); downloadImportTemplate(ids); }} disabled={selectedRowKeys.length === 0} > 下载发票 </Button> </Space> ); }} form={{}} dateFormatter="string" headerTitle="待开票列表" scroll={{ x: 2400, y: 360 }} toolBarRender={() => []} /> {invoiceRecordDetailVisible ? ( <InvoiceRecordDetailModal key="detail" id={invoiceRecord.id} setVisible={setInvoiceRecordDetailVisible} reloadTable={() => { processedRecordRef?.current?.reload(); }} /> ) : ( '' )} {contextHolder} </div> ); }; export default InvoiceRecord;