Commit 1564fd17b183856118735bb0799ddcb1377f38bc

Authored by 曾国涛
1 parent adfd7a78

feat: 开票功能开发

src/pages/Invoice/components/Invoice.tsx
1 -import { postServiceInvoiceGetInvoiceRecord } from '@/services';  
2 -import { useEffect, useState } from 'react';  
3 import styled from 'styled-components'; 1 import styled from 'styled-components';
4 const InvoiceTmpDiv = styled.div` 2 const InvoiceTmpDiv = styled.div`
5 font-size: 12px; 3 font-size: 12px;
@@ -147,19 +145,7 @@ const ProjectContainer = styled.div` @@ -147,19 +145,7 @@ const ProjectContainer = styled.div`
147 height: 30px; 145 height: 30px;
148 } 146 }
149 `; 147 `;
150 -export default ({ recordId }) => {  
151 - const [data, setData] = useState<any>({});  
152 - useEffect(() => {  
153 - const getData = async () => {  
154 - let ret = await postServiceInvoiceGetInvoiceRecord({  
155 - query: {  
156 - id: recordId,  
157 - },  
158 - });  
159 - setData(ret.data);  
160 - };  
161 - getData();  
162 - }, []); 148 +export default ({ data }) => {
163 return ( 149 return (
164 <div> 150 <div>
165 <InvoiceTmpDiv> 151 <InvoiceTmpDiv>
src/pages/Invoice/components/InvoiceModal.tsx
1 import Invoice from '@/pages/Invoice/components/Invoice'; 1 import Invoice from '@/pages/Invoice/components/Invoice';
  2 +import { postServiceInvoiceGetInvoiceRecord } from '@/services';
2 import { ModalForm } from '@ant-design/pro-components'; 3 import { ModalForm } from '@ant-design/pro-components';
3 -import { Form, message } from 'antd';  
4 -import { useEffect } from 'react'; 4 +import { Form } from 'antd';
  5 +import { useEffect, useState } from 'react';
5 6
6 -const waitTime = (time: number = 100) => {  
7 - return new Promise((resolve) => {  
8 - setTimeout(() => {  
9 - resolve(true);  
10 - }, time);  
11 - });  
12 -};  
13 -  
14 -export default ({ recordId }) => {  
15 - const [form] = Form.useForm(); 7 +export default ({ recordId, getRecord, button }) => {
  8 + const [data, setData] = useState<any>({});
16 useEffect(() => { 9 useEffect(() => {
17 - console.log('recordId', recordId); 10 + const getData = async () => {
  11 + let ret = await postServiceInvoiceGetInvoiceRecord({
  12 + query: {
  13 + id: recordId,
  14 + },
  15 + });
  16 + setData(ret.data);
  17 + };
  18 + if (recordId) {
  19 + getData();
  20 + }
18 }, []); 21 }, []);
  22 + const [form] = Form.useForm();
19 return ( 23 return (
20 <ModalForm 24 <ModalForm
21 title="预览发票" 25 title="预览发票"
22 - trigger={<a type="primary">预览</a>} 26 + trigger={button ? button : <a type="primary">预览</a>}
  27 + onOpenChange={(open) => {
  28 + if (open && getRecord) {
  29 + setData(getRecord());
  30 + }
  31 + }}
23 width={1200} 32 width={1200}
24 form={form} 33 form={form}
25 autoFocusFirstInput 34 autoFocusFirstInput
26 modalProps={{ 35 modalProps={{
27 destroyOnClose: true, 36 destroyOnClose: true,
28 - onCancel: () => console.log('run'),  
29 - }}  
30 - submitTimeout={2000}  
31 - onFinish={async (values) => {  
32 - await waitTime(2000);  
33 - console.log(values.name);  
34 - message.success('提交成功');  
35 - return true;  
36 }} 37 }}
37 > 38 >
38 <hr /> 39 <hr />
39 - <Invoice recordId={recordId} /> 40 + <Invoice data={data} />
40 </ModalForm> 41 </ModalForm>
41 ); 42 );
42 }; 43 };
src/pages/Invoice/components/InvoiceRecordDetailModal.tsx
@@ -70,7 +70,7 @@ export default ({ id, setVisible }) =&gt; { @@ -70,7 +70,7 @@ export default ({ id, setVisible }) =&gt; {
70 <Space> 70 <Space>
71 <ModalForm 71 <ModalForm
72 open 72 open
73 - title="新建表单" 73 + title="发票详情"
74 formRef={formRef} 74 formRef={formRef}
75 initialValues={initialValues} 75 initialValues={initialValues}
76 request={async () => { 76 request={async () => {
src/pages/Invoice/index.tsx
@@ -24,6 +24,7 @@ import { @@ -24,6 +24,7 @@ import {
24 postServiceInvoiceQueryInvoiceRecordList, 24 postServiceInvoiceQueryInvoiceRecordList,
25 postServiceOrderQuerySalesCode, 25 postServiceOrderQuerySalesCode,
26 } from '@/services'; 26 } from '@/services';
  27 +import { excelExport } from '@/services/exportRequest';
27 import { 28 import {
28 enumToProTableEnumValue, 29 enumToProTableEnumValue,
29 enumToSelect, 30 enumToSelect,
@@ -55,6 +56,7 @@ const InvoicePage = () =&gt; { @@ -55,6 +56,7 @@ const InvoicePage = () =&gt; {
55 const [invoiceRecordDetailVisible, setInvoiceRecordDetailVisible] = 56 const [invoiceRecordDetailVisible, setInvoiceRecordDetailVisible] =
56 useState(false); 57 useState(false);
57 const [invoiceRecord, setInvoiceRecord] = useState({}); 58 const [invoiceRecord, setInvoiceRecord] = useState({});
  59 + const [messageApi] = message.useMessage();
58 60
59 useEffect(() => { 61 useEffect(() => {
60 async function extracted() { 62 async function extracted() {
@@ -199,7 +201,13 @@ const InvoicePage = () =&gt; { @@ -199,7 +201,13 @@ const InvoicePage = () =&gt; {
199 hideInSearch: true, 201 hideInSearch: true,
200 ellipsis: true, 202 ellipsis: true,
201 }, 203 },
202 - 204 + {
  205 + title: '申请备注',
  206 + valueType: 'text',
  207 + dataIndex: 'applyInvoicingNotes',
  208 + hideInSearch: true,
  209 + ellipsis: true,
  210 + },
203 { 211 {
204 title: '购方名称', 212 title: '购方名称',
205 valueType: 'Text', 213 valueType: 'Text',
@@ -419,6 +427,13 @@ const InvoicePage = () =&gt; { @@ -419,6 +427,13 @@ const InvoicePage = () =&gt; {
419 hideInSearch: true, 427 hideInSearch: true,
420 ellipsis: true, 428 ellipsis: true,
421 }, 429 },
  430 + {
  431 + title: '失败原因',
  432 + valueType: 'text',
  433 + dataIndex: 'failureReason',
  434 + hideInSearch: true,
  435 + ellipsis: true,
  436 + },
422 437
423 { 438 {
424 title: '购方名称', 439 title: '购方名称',
@@ -826,6 +841,8 @@ const InvoicePage = () =&gt; { @@ -826,6 +841,8 @@ const InvoicePage = () =&gt; {
826 'AUDITING_NOT_PASSED', 841 'AUDITING_NOT_PASSED',
827 'CANCELED', 842 'CANCELED',
828 ], 843 ],
  844 + needBuildDetails: false,
  845 + needBuildSubOrders: true,
829 }, 846 },
830 }); 847 });
831 return { 848 return {
@@ -880,6 +897,37 @@ const InvoicePage = () =&gt; { @@ -880,6 +897,37 @@ const InvoicePage = () =&gt; {
880 defaultDom.cancel, 897 defaultDom.cancel,
881 ], 898 ],
882 }} 899 }}
  900 + search={{
  901 + labelWidth: 'auto',
  902 + defaultCollapsed: false,
  903 + optionRender: (searchConfig, formProps, dom) => [
  904 + ...dom,
  905 + <Button
  906 + key="out"
  907 + onClick={() => {
  908 + const values = searchConfig?.form?.getFieldsValue();
  909 + console.log(values);
  910 + messageApi.open({
  911 + type: 'loading',
  912 + content: '正在导出文件...',
  913 + duration: 0,
  914 + });
  915 + excelExport(
  916 + '/api/service/invoice/exportInvoiceRecords',
  917 + {
  918 + ...values,
  919 + statusIn: ['INVOICING', 'SUCCESS', 'FAIL'],
  920 + },
  921 + () => {
  922 + messageApi.destroy();
  923 + },
  924 + );
  925 + }}
  926 + >
  927 + 导出
  928 + </Button>,
  929 + ],
  930 + }}
883 request={async (params) => { 931 request={async (params) => {
884 let res = await postServiceInvoiceQueryInvoiceRecordList({ 932 let res = await postServiceInvoiceQueryInvoiceRecordList({
885 data: { 933 data: {
@@ -903,9 +951,6 @@ const InvoicePage = () =&gt; { @@ -903,9 +951,6 @@ const InvoicePage = () =&gt; {
903 }, 951 },
904 }} 952 }}
905 rowKey="id" 953 rowKey="id"
906 - search={{  
907 - labelWidth: 'auto',  
908 - }}  
909 options={{ 954 options={{
910 setting: { 955 setting: {
911 listsHeight: 400, 956 listsHeight: 400,
src/pages/Order/components/InvoicingDrawerForm.tsx
1 // import { PlusOutlined } from '@ant-design/icons'; 1 // import { PlusOutlined } from '@ant-design/icons';
  2 +import InvoiceModal from '@/pages/Invoice/components/InvoiceModal';
2 import { 3 import {
  4 + postServiceConstGetPayeeEnum,
3 postServiceConstInvoiceType, 5 postServiceConstInvoiceType,
4 postServiceConstInvoicingType, 6 postServiceConstInvoicingType,
5 postServiceConstListInvoiceDetailNames, 7 postServiceConstListInvoiceDetailNames,
@@ -16,11 +18,9 @@ import { @@ -16,11 +18,9 @@ import {
16 ProFormSelect, 18 ProFormSelect,
17 ProFormText, 19 ProFormText,
18 ProFormTextArea, 20 ProFormTextArea,
19 - ProFormUploadDragger,  
20 } from '@ant-design/pro-components'; 21 } from '@ant-design/pro-components';
21 -import { Form } from 'antd'; 22 +import { Button, Form } from 'antd';
22 import { useEffect } from 'react'; 23 import { useEffect } from 'react';
23 -import { PAYEE_OPTIONS } from '../constant';  
24 24
25 export default ({ dataList, mainOrder, setVisible, onClose }) => { 25 export default ({ dataList, mainOrder, setVisible, onClose }) => {
26 // let subOrderIds = dataList?.map((item) => { 26 // let subOrderIds = dataList?.map((item) => {
@@ -44,6 +44,18 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; { @@ -44,6 +44,18 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
44 drawerProps={{ 44 drawerProps={{
45 destroyOnClose: true, 45 destroyOnClose: true,
46 }} 46 }}
  47 + submitter={{
  48 + render: (props, defaultDoms) => {
  49 + return [
  50 + <InvoiceModal
  51 + key={'invoicePreview'}
  52 + button={<Button type="primary"> 发票预览 </Button>}
  53 + getRecord={form.getFieldsValue}
  54 + />,
  55 + ...defaultDoms,
  56 + ];
  57 + },
  58 + }}
47 submitTimeout={2000} 59 submitTimeout={2000}
48 onFinish={async (values) => { 60 onFinish={async (values) => {
49 postServiceInvoiceApplyInvoice({ 61 postServiceInvoiceApplyInvoice({
@@ -142,12 +154,42 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; { @@ -142,12 +154,42 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
142 }} 154 }}
143 /> 155 />
144 <ProFormSelect 156 <ProFormSelect
145 - name="partyBName" 157 + name="partyB"
146 label="开票收款单位" 158 label="开票收款单位"
147 - options={enumToSelect(PAYEE_OPTIONS)} 159 + request={async () => {
  160 + const res = await postServiceConstGetPayeeEnum();
  161 + let options = res?.data?.map((payee: any) => {
  162 + return {
  163 + ...payee,
  164 + label: payee.payeeName,
  165 + value: payee.name,
  166 + };
  167 + });
  168 + return options;
  169 + }}
  170 + onChange={(_, option) => {
  171 + if (option) {
  172 + form.setFieldsValue({
  173 + partyBName: option.payeeName,
  174 + partyBTaxid: option.taxId,
  175 + });
  176 + }
  177 + }}
148 placeholder="请选择收款单位" 178 placeholder="请选择收款单位"
149 rules={[{ required: true, message: '请选择收款单位!' }]} 179 rules={[{ required: true, message: '请选择收款单位!' }]}
150 /> 180 />
  181 + <ProFormText
  182 + name="partyBName"
  183 + label="开票收款单位名称"
  184 + hidden
  185 + rules={[{ required: true, message: '请选择收款单位!' }]}
  186 + />
  187 + <ProFormText
  188 + name="partyBTaxid"
  189 + label="开票收款单位税号"
  190 + hidden
  191 + rules={[{ required: true, message: '请选择收款单位!' }]}
  192 + />
151 <ProFormSelect 193 <ProFormSelect
152 name="isUrgent" 194 name="isUrgent"
153 label="是否加急" 195 label="是否加急"
@@ -158,15 +200,6 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; { @@ -158,15 +200,6 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
158 placeholder="请选择是否加急" 200 placeholder="请选择是否加急"
159 rules={[{ required: true, message: '请选择是否加急!' }]} 201 rules={[{ required: true, message: '请选择是否加急!' }]}
160 /> 202 />
161 - <ProFormUploadDragger  
162 - key="filePaths"  
163 - label="附件"  
164 - name="filePaths"  
165 - action="/api/service/order/fileProcess"  
166 - fieldProps={{  
167 - headers: { Authorization: localStorage.getItem('token') },  
168 - }}  
169 - />  
170 <ProFormList 203 <ProFormList
171 name="invoiceDetails" 204 name="invoiceDetails"
172 label="开票明细" 205 label="开票明细"
src/pages/Order/index.tsx
@@ -213,7 +213,6 @@ const OrderPage = () =&gt; { @@ -213,7 +213,6 @@ const OrderPage = () =&gt; {
213 }; 213 };
214 214
215 const refreshTable = () => { 215 const refreshTable = () => {
216 - console.log('刷新表格');  
217 mainTableRef.current?.reload(); 216 mainTableRef.current?.reload();
218 //刷新表格数据的时候,取消选中行 217 //刷新表格数据的时候,取消选中行
219 setSelectedRows([]); 218 setSelectedRows([]);
@@ -604,9 +603,6 @@ const OrderPage = () =&gt; { @@ -604,9 +603,6 @@ const OrderPage = () =&gt; {
604 mainOrderSelectedMap.clear(); 603 mainOrderSelectedMap.clear();
605 subOrderSelectedMap.clear(); 604 subOrderSelectedMap.clear();
606 } 605 }
607 -  
608 - console.log(mainOrderSelectedMap);  
609 - console.log(subOrderSelectedMap);  
610 }; 606 };
611 607
612 //表头渲染 608 //表头渲染
@@ -1289,7 +1285,6 @@ const OrderPage = () =&gt; { @@ -1289,7 +1285,6 @@ const OrderPage = () =&gt; {
1289 setCurrentMainId(record.id); 1285 setCurrentMainId(record.id);
1290 setCurretnOptSubId(optRecord.mainOrderId); 1286 setCurretnOptSubId(optRecord.mainOrderId);
1291 setReissueVisible(true); 1287 setReissueVisible(true);
1292 - console.log(reissueVisible);  
1293 }} 1288 }}
1294 > 1289 >
1295 重新开票 1290 重新开票
@@ -1320,7 +1315,6 @@ const OrderPage = () =&gt; { @@ -1320,7 +1315,6 @@ const OrderPage = () =&gt; {
1320 className="p-0" 1315 className="p-0"
1321 type="link" 1316 type="link"
1322 onClick={() => { 1317 onClick={() => {
1323 - console.log('here');  
1324 setCurrentMainId(record.id); 1318 setCurrentMainId(record.id);
1325 setCurretnOptSubId(optRecord.id); 1319 setCurretnOptSubId(optRecord.id);
1326 setCheckVisible(true); 1320 setCheckVisible(true);
@@ -1355,7 +1349,6 @@ const OrderPage = () =&gt; { @@ -1355,7 +1349,6 @@ const OrderPage = () =&gt; {
1355 className="p-0" 1349 className="p-0"
1356 type="link" 1350 type="link"
1357 onClick={() => { 1351 onClick={() => {
1358 - console.log('here');  
1359 setCurrentMainId(record.id); 1352 setCurrentMainId(record.id);
1360 setCurretnOptSubId(optRecord.id); 1353 setCurretnOptSubId(optRecord.id);
1361 setCheckVisible(true); 1354 setCheckVisible(true);
@@ -2251,9 +2244,6 @@ const OrderPage = () =&gt; { @@ -2251,9 +2244,6 @@ const OrderPage = () =&gt; {
2251 2244
2252 setSelectedSubOrderKeys(newSelectedSubOrderKeys); 2245 setSelectedSubOrderKeys(newSelectedSubOrderKeys);
2253 setSelectedRows(currentMainOrderSelectedSubOrderList); 2246 setSelectedRows(currentMainOrderSelectedSubOrderList);
2254 -  
2255 - console.log(mainOrderSelectedMap);  
2256 - console.log(subOrderSelectedMap);  
2257 }, 2247 },
2258 selectedRowKeys: selectedSubOrderKeys, 2248 selectedRowKeys: selectedSubOrderKeys,
2259 // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom 2249 // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
@@ -2685,7 +2675,6 @@ const OrderPage = () =&gt; { @@ -2685,7 +2675,6 @@ const OrderPage = () =&gt; {
2685 onClick={() => { 2675 onClick={() => {
2686 setCurrentMainId(record.id); 2676 setCurrentMainId(record.id);
2687 setReissueVisible(true); 2677 setReissueVisible(true);
2688 - console.log(reissueVisible);  
2689 }} 2678 }}
2690 > 2679 >
2691 重新开票 2680 重新开票
@@ -3396,7 +3385,6 @@ const OrderPage = () =&gt; { @@ -3396,7 +3385,6 @@ const OrderPage = () =&gt; {
3396 selectedSubOrders = record.subOrderInformationLists; 3385 selectedSubOrders = record.subOrderInformationLists;
3397 } 3386 }
3398 3387
3399 - console.log(selectedSubOrders);  
3400 for (let i = 0; i < selectedSubOrders.length; i++) { 3388 for (let i = 0; i < selectedSubOrders.length; i++) {
3401 if ( 3389 if (
3402 selectedSubOrders[i].afterInvoicingStatus !== 3390 selectedSubOrders[i].afterInvoicingStatus !==
@@ -4128,9 +4116,6 @@ const OrderPage = () =&gt; { @@ -4128,9 +4116,6 @@ const OrderPage = () =&gt; {
4128 let flat = [...subOrderSelectedMap.values()].flat(); 4116 let flat = [...subOrderSelectedMap.values()].flat();
4129 //遍历flat,判断afterInvoicingStatusList存在于canApplyAfterInvoicingStatus 4117 //遍历flat,判断afterInvoicingStatusList存在于canApplyAfterInvoicingStatus
4130 flat.forEach((item) => { 4118 flat.forEach((item) => {
4131 - console.log(item);  
4132 - console.log(item.invoicingStatus === 'UN_INVOICE');  
4133 - console.log(item.afterInvoicingStatus !== null);  
4134 if ( 4119 if (
4135 item.invoicingStatus === 'UN_INVOICE' || 4120 item.invoicingStatus === 'UN_INVOICE' ||
4136 (item.afterInvoicingStatus !== null && 4121 (item.afterInvoicingStatus !== null &&
@@ -4695,7 +4680,6 @@ const OrderPage = () =&gt; { @@ -4695,7 +4680,6 @@ const OrderPage = () =&gt; {
4695 <ReissueModal 4680 <ReissueModal
4696 setVisible={(val: boolean) => { 4681 setVisible={(val: boolean) => {
4697 setReissueVisible(val); 4682 setReissueVisible(val);
4698 - console.log(reissueVisible);  
4699 if (!val) { 4683 if (!val) {
4700 clearOptObject(); 4684 clearOptObject();
4701 } 4685 }
src/services/exportRequest.ts 0 → 100644
  1 +import axios from 'axios';
  2 +
  3 +export const excelExport = async (
  4 + url: any = '',
  5 + data: any = {},
  6 + exportLoadingDestory: any,
  7 +) => {
  8 + axios({
  9 + url: url,
  10 + method: 'post',
  11 + responseType: 'blob',
  12 + headers: { Authorization: localStorage.getItem('token') },
  13 + data,
  14 + })
  15 + .then((response) => {
  16 + // 我这里在拦截器里直接返回的response.data
  17 + const body = response.data;
  18 + let fileUrl = window.URL.createObjectURL(
  19 + new Blob([body], {
  20 + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  21 + }),
  22 + );
  23 + let a = document.createElement('a');
  24 + a.style.display = 'none';
  25 + a.href = fileUrl;
  26 + a.download = '开票记录.xlsx';
  27 + document.body.appendChild(a);
  28 + a.click();
  29 + window.URL.revokeObjectURL(a.href);
  30 + document.body.removeChild(a);
  31 + })
  32 + .catch((error) => {
  33 + // 处理错误
  34 + console.error('导出错误', error);
  35 + })
  36 + .finally(() => {
  37 + exportLoadingDestory();
  38 + });
  39 +};