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 1 import styled from 'styled-components';
4 2 const InvoiceTmpDiv = styled.div`
5 3 font-size: 12px;
... ... @@ -147,19 +145,7 @@ const ProjectContainer = styled.div`
147 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 149 return (
164 150 <div>
165 151 <InvoiceTmpDiv>
... ...
src/pages/Invoice/components/InvoiceModal.tsx
1 1 import Invoice from '@/pages/Invoice/components/Invoice';
  2 +import { postServiceInvoiceGetInvoiceRecord } from '@/services';
2 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 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 23 return (
20 24 <ModalForm
21 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 32 width={1200}
24 33 form={form}
25 34 autoFocusFirstInput
26 35 modalProps={{
27 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 39 <hr />
39   - <Invoice recordId={recordId} />
  40 + <Invoice data={data} />
40 41 </ModalForm>
41 42 );
42 43 };
... ...
src/pages/Invoice/components/InvoiceRecordDetailModal.tsx
... ... @@ -70,7 +70,7 @@ export default ({ id, setVisible }) =&gt; {
70 70 <Space>
71 71 <ModalForm
72 72 open
73   - title="新建表单"
  73 + title="发票详情"
74 74 formRef={formRef}
75 75 initialValues={initialValues}
76 76 request={async () => {
... ...
src/pages/Invoice/index.tsx
... ... @@ -24,6 +24,7 @@ import {
24 24 postServiceInvoiceQueryInvoiceRecordList,
25 25 postServiceOrderQuerySalesCode,
26 26 } from '@/services';
  27 +import { excelExport } from '@/services/exportRequest';
27 28 import {
28 29 enumToProTableEnumValue,
29 30 enumToSelect,
... ... @@ -55,6 +56,7 @@ const InvoicePage = () =&gt; {
55 56 const [invoiceRecordDetailVisible, setInvoiceRecordDetailVisible] =
56 57 useState(false);
57 58 const [invoiceRecord, setInvoiceRecord] = useState({});
  59 + const [messageApi] = message.useMessage();
58 60  
59 61 useEffect(() => {
60 62 async function extracted() {
... ... @@ -199,7 +201,13 @@ const InvoicePage = () =&gt; {
199 201 hideInSearch: true,
200 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 212 title: '购方名称',
205 213 valueType: 'Text',
... ... @@ -419,6 +427,13 @@ const InvoicePage = () =&gt; {
419 427 hideInSearch: true,
420 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 439 title: '购方名称',
... ... @@ -826,6 +841,8 @@ const InvoicePage = () =&gt; {
826 841 'AUDITING_NOT_PASSED',
827 842 'CANCELED',
828 843 ],
  844 + needBuildDetails: false,
  845 + needBuildSubOrders: true,
829 846 },
830 847 });
831 848 return {
... ... @@ -880,6 +897,37 @@ const InvoicePage = () =&gt; {
880 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 931 request={async (params) => {
884 932 let res = await postServiceInvoiceQueryInvoiceRecordList({
885 933 data: {
... ... @@ -903,9 +951,6 @@ const InvoicePage = () =&gt; {
903 951 },
904 952 }}
905 953 rowKey="id"
906   - search={{
907   - labelWidth: 'auto',
908   - }}
909 954 options={{
910 955 setting: {
911 956 listsHeight: 400,
... ...
src/pages/Order/components/InvoicingDrawerForm.tsx
1 1 // import { PlusOutlined } from '@ant-design/icons';
  2 +import InvoiceModal from '@/pages/Invoice/components/InvoiceModal';
2 3 import {
  4 + postServiceConstGetPayeeEnum,
3 5 postServiceConstInvoiceType,
4 6 postServiceConstInvoicingType,
5 7 postServiceConstListInvoiceDetailNames,
... ... @@ -16,11 +18,9 @@ import {
16 18 ProFormSelect,
17 19 ProFormText,
18 20 ProFormTextArea,
19   - ProFormUploadDragger,
20 21 } from '@ant-design/pro-components';
21   -import { Form } from 'antd';
  22 +import { Button, Form } from 'antd';
22 23 import { useEffect } from 'react';
23   -import { PAYEE_OPTIONS } from '../constant';
24 24  
25 25 export default ({ dataList, mainOrder, setVisible, onClose }) => {
26 26 // let subOrderIds = dataList?.map((item) => {
... ... @@ -44,6 +44,18 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
44 44 drawerProps={{
45 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 59 submitTimeout={2000}
48 60 onFinish={async (values) => {
49 61 postServiceInvoiceApplyInvoice({
... ... @@ -142,12 +154,42 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
142 154 }}
143 155 />
144 156 <ProFormSelect
145   - name="partyBName"
  157 + name="partyB"
146 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 178 placeholder="请选择收款单位"
149 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 193 <ProFormSelect
152 194 name="isUrgent"
153 195 label="是否加急"
... ... @@ -158,15 +200,6 @@ export default ({ dataList, mainOrder, setVisible, onClose }) =&gt; {
158 200 placeholder="请选择是否加急"
159 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 203 <ProFormList
171 204 name="invoiceDetails"
172 205 label="开票明细"
... ...
src/pages/Order/index.tsx
... ... @@ -213,7 +213,6 @@ const OrderPage = () =&gt; {
213 213 };
214 214  
215 215 const refreshTable = () => {
216   - console.log('刷新表格');
217 216 mainTableRef.current?.reload();
218 217 //刷新表格数据的时候,取消选中行
219 218 setSelectedRows([]);
... ... @@ -604,9 +603,6 @@ const OrderPage = () =&gt; {
604 603 mainOrderSelectedMap.clear();
605 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 1285 setCurrentMainId(record.id);
1290 1286 setCurretnOptSubId(optRecord.mainOrderId);
1291 1287 setReissueVisible(true);
1292   - console.log(reissueVisible);
1293 1288 }}
1294 1289 >
1295 1290 重新开票
... ... @@ -1320,7 +1315,6 @@ const OrderPage = () =&gt; {
1320 1315 className="p-0"
1321 1316 type="link"
1322 1317 onClick={() => {
1323   - console.log('here');
1324 1318 setCurrentMainId(record.id);
1325 1319 setCurretnOptSubId(optRecord.id);
1326 1320 setCheckVisible(true);
... ... @@ -1355,7 +1349,6 @@ const OrderPage = () =&gt; {
1355 1349 className="p-0"
1356 1350 type="link"
1357 1351 onClick={() => {
1358   - console.log('here');
1359 1352 setCurrentMainId(record.id);
1360 1353 setCurretnOptSubId(optRecord.id);
1361 1354 setCheckVisible(true);
... ... @@ -2251,9 +2244,6 @@ const OrderPage = () =&gt; {
2251 2244  
2252 2245 setSelectedSubOrderKeys(newSelectedSubOrderKeys);
2253 2246 setSelectedRows(currentMainOrderSelectedSubOrderList);
2254   -
2255   - console.log(mainOrderSelectedMap);
2256   - console.log(subOrderSelectedMap);
2257 2247 },
2258 2248 selectedRowKeys: selectedSubOrderKeys,
2259 2249 // 自定义选择项参考: https://ant.design/components/table-cn/#components-table-demo-row-selection-custom
... ... @@ -2685,7 +2675,6 @@ const OrderPage = () =&gt; {
2685 2675 onClick={() => {
2686 2676 setCurrentMainId(record.id);
2687 2677 setReissueVisible(true);
2688   - console.log(reissueVisible);
2689 2678 }}
2690 2679 >
2691 2680 重新开票
... ... @@ -3396,7 +3385,6 @@ const OrderPage = () =&gt; {
3396 3385 selectedSubOrders = record.subOrderInformationLists;
3397 3386 }
3398 3387  
3399   - console.log(selectedSubOrders);
3400 3388 for (let i = 0; i < selectedSubOrders.length; i++) {
3401 3389 if (
3402 3390 selectedSubOrders[i].afterInvoicingStatus !==
... ... @@ -4128,9 +4116,6 @@ const OrderPage = () =&gt; {
4128 4116 let flat = [...subOrderSelectedMap.values()].flat();
4129 4117 //遍历flat,判断afterInvoicingStatusList存在于canApplyAfterInvoicingStatus
4130 4118 flat.forEach((item) => {
4131   - console.log(item);
4132   - console.log(item.invoicingStatus === 'UN_INVOICE');
4133   - console.log(item.afterInvoicingStatus !== null);
4134 4119 if (
4135 4120 item.invoicingStatus === 'UN_INVOICE' ||
4136 4121 (item.afterInvoicingStatus !== null &&
... ... @@ -4695,7 +4680,6 @@ const OrderPage = () =&gt; {
4695 4680 <ReissueModal
4696 4681 setVisible={(val: boolean) => {
4697 4682 setReissueVisible(val);
4698   - console.log(reissueVisible);
4699 4683 if (!val) {
4700 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 +};
... ...