Commit b5fb40081227e20e5e5139aabd4e459ec4d57a66

Authored by zhongnanhuang
2 parents 9fd3a213 fbd42d15

Merge branch 'znh' into 'develop'

feat: update 申请开票,部分开票



See merge request !20
src/pages/Order/components/ApplyForInvoicingModal.tsx
@@ -47,7 +47,8 @@ export default ({ setCheckVisible, subOrders, onClose }) => { @@ -47,7 +47,8 @@ export default ({ setCheckVisible, subOrders, onClose }) => {
47 }); 47 });
48 48
49 const res = await postServiceOrderApplyInvoicing({ data: values }); 49 const res = await postServiceOrderApplyInvoicing({ data: values });
50 - if ((res.result = RESPONSE_CODE.SUCCESS)) { 50 +
  51 + if (res.result === RESPONSE_CODE.SUCCESS) {
51 message.success(res.message); 52 message.success(res.message);
52 onClose(); 53 onClose();
53 } 54 }
src/pages/Order/components/CheckModal.tsx
@@ -4,8 +4,12 @@ import { @@ -4,8 +4,12 @@ import {
4 postServiceOrderFinanceCheckOrder, 4 postServiceOrderFinanceCheckOrder,
5 } from '@/services'; 5 } from '@/services';
6 import { ModalForm, ProFormTextArea } from '@ant-design/pro-components'; 6 import { ModalForm, ProFormTextArea } from '@ant-design/pro-components';
7 -import { Button, Form, message } from 'antd';  
8 -import { CHECK_TYPE } from '../constant'; 7 +import { Button, Form, Modal, UploadFile, message } from 'antd';
  8 +import Upload, { RcFile, UploadProps } from 'antd/es/upload';
  9 +import { useRef, useState } from 'react';
  10 +import { CHECK_TYPE, COMFIR_RECEIPT_IMAGES_NUMBER } from '../constant';
  11 +// import { cloneDeep } from 'lodash';
  12 +import { PlusOutlined } from '@ant-design/icons';
9 export default ({ 13 export default ({
10 setCheckVisible, 14 setCheckVisible,
11 data, 15 data,
@@ -13,6 +17,130 @@ export default ({ @@ -13,6 +17,130 @@ export default ({
13 orderCheckType, 17 orderCheckType,
14 onClose, 18 onClose,
15 }) => { 19 }) => {
  20 + const [previewOpen, setPreviewOpen] = useState(false);
  21 + const [previewImage, setPreviewImage] = useState('');
  22 + const [previewTitle, setPreviewTitle] = useState('');
  23 + const fileListObj = useRef<UploadFile[]>([]); //使用引用类型,使得在useEffect里面设置监听事件后,不用更新监听事件也能保持obj与外界一致
  24 + const getBase64 = (file: RcFile): Promise<string> =>
  25 + new Promise((resolve, reject) => {
  26 + const reader = new FileReader();
  27 + reader.readAsDataURL(file);
  28 + reader.onload = () => resolve(reader.result as string);
  29 + reader.onerror = (error) => reject(error);
  30 + });
  31 + const [fileList, setFileList] = useState<UploadFile[]>([]);
  32 + const [uploading, setUploading] = useState(false);
  33 + const handleCancel = () => setPreviewOpen(false);
  34 + console.log(uploading);
  35 + const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
  36 + //fileListObj得在change里变化,change的参数是已经处理过的file数组
  37 + //beforeUpload中的参数file是未处理过,还需要Base64拿到文件数据处理
  38 + fileListObj.current = newFileList;
  39 + setFileList(newFileList);
  40 + };
  41 +
  42 + // /** 粘贴快捷键的回调 */
  43 + // const onPaste = async (e: any) => {
  44 + // /** 获取剪切板的数据clipboardData */
  45 + // let clipboardData = e.clipboardData,
  46 + // i = 0,
  47 + // items,
  48 + // item,
  49 + // types;
  50 +
  51 + // /** 为空判断 */
  52 + // if (clipboardData) {
  53 + // items = clipboardData.items;
  54 + // if (!items) {
  55 + // message.info('您的剪贴板中没有照片');
  56 + // return;
  57 + // }
  58 +
  59 + // item = items[0];
  60 + // types = clipboardData.types || [];
  61 + // /** 遍历剪切板的数据 */
  62 + // for (; i < types.length; i++) {
  63 + // if (types[i] === 'Files') {
  64 + // item = items[i];
  65 + // break;
  66 + // }
  67 + // }
  68 +
  69 + // /** 判断文件是否为图片 */
  70 + // if (item && item.kind === 'file' && item.type.match(/^image\//i)) {
  71 + // const imgItem = item.getAsFile();
  72 + // const newFileList = cloneDeep(fileListObj.current);
  73 + // let filteredArray = newFileList.filter(
  74 + // (obj) => obj.status !== 'removed',
  75 + // ); //过滤掉状态为已删除的照片
  76 + // const listItem = {
  77 + // ...imgItem,
  78 + // status: 'done',
  79 + // url: await getBase64(imgItem),
  80 + // originFileObj: imgItem,
  81 + // };
  82 +
  83 + // if (filteredArray.length >= COMFIR_RECEIPT_IMAGES_NUMBER) {
  84 + // message.info('发货凭证照片数量不能超过3');
  85 + // return;
  86 + // }
  87 + // fileListObj.current = filteredArray;
  88 + // filteredArray.push(listItem);
  89 + // setFileList(filteredArray);
  90 + // return;
  91 + // }
  92 + // }
  93 +
  94 + // message.info('您的剪贴板中没有照片');
  95 + // };
  96 + // useEffect(() => {
  97 + // document.addEventListener('paste', onPaste);
  98 + // return () => {
  99 + // document.removeEventListener('paste', onPaste);
  100 + // };
  101 + // }, []);
  102 + const uploadButton = (
  103 + <div>
  104 + <PlusOutlined />
  105 + <div style={{ marginTop: 8 }}>上传凭证</div>
  106 + </div>
  107 + );
  108 + const handlePreview = async (file: UploadFile) => {
  109 + if (!file.url && !file.preview) {
  110 + file.preview = await getBase64(file.originFileObj as RcFile);
  111 + }
  112 + setPreviewImage(file.url || (file.preview as string));
  113 + setPreviewOpen(true);
  114 + setPreviewTitle(
  115 + file.name ||
  116 + file.originFileObj?.name ||
  117 + file.url!.substring(file.url!.lastIndexOf('/') + 1),
  118 + );
  119 + };
  120 +
  121 + const handleBeforeUpload = (file: any) => {
  122 + setFileList([...fileList, file]);
  123 + return true;
  124 + };
  125 +
  126 + const props: UploadProps = {
  127 + onRemove: (file) => {
  128 + const index = fileList.indexOf(file);
  129 + const newFileList = fileList.slice();
  130 + newFileList.splice(index, 1);
  131 + setFileList(newFileList);
  132 + },
  133 + beforeUpload: handleBeforeUpload,
  134 + listType: 'picture-card',
  135 + onPreview: handlePreview,
  136 + fileList,
  137 + onChange: handleChange,
  138 + accept: 'image/png, image/jpeg, image/png',
  139 + action: '/api/service/order/fileProcess',
  140 + name: 'files',
  141 + headers: { Authorization: localStorage.getItem('token') },
  142 + };
  143 +
16 const [form] = Form.useForm<{ name: string; company: string }>(); 144 const [form] = Form.useForm<{ name: string; company: string }>();
17 let subOrderIds: any[] = []; 145 let subOrderIds: any[] = [];
18 //是单条子订单审核 146 //是单条子订单审核
@@ -57,103 +185,139 @@ export default ({ @@ -57,103 +185,139 @@ export default ({
57 } 185 }
58 186
59 return ( 187 return (
60 - <ModalForm<{  
61 - name: string;  
62 - company: string;  
63 - }>  
64 - width={500}  
65 - open  
66 - title="审核"  
67 - form={form}  
68 - autoFocusFirstInput  
69 - modalProps={{  
70 - okText: '通过',  
71 - cancelText: '驳回',  
72 - destroyOnClose: true,  
73 - onCancel: () => {  
74 - setCheckVisible(false);  
75 - },  
76 - }}  
77 - submitter={{  
78 - render: (props, defaultDoms) => {  
79 - let myDoms = [];  
80 - myDoms.push(  
81 - <Button  
82 - key="驳回"  
83 - onClick={() => {  
84 - if (checkType(CHECK_TYPE.NORMAL)) {  
85 - doCheck({  
86 - flag: false,  
87 - ids: subOrderIds,  
88 - externalProcurement: 0,  
89 - checkNotes: form.getFieldValue('name'),  
90 - });  
91 - return;  
92 - }  
93 -  
94 - //财务审核  
95 - doFinancailCheck({  
96 - checkNotes: form.getFieldValue('name'),  
97 - ids: subOrderIds,  
98 - checkPassOrReject: false,  
99 - });  
100 - }}  
101 - >  
102 - 驳回  
103 - </Button>,  
104 - );  
105 -  
106 - //如果不是财务审核,那么显示这个外部采购  
107 - if (checkType(CHECK_TYPE.NORMAL)) { 188 + <>
  189 + <ModalForm<{
  190 + name: string;
  191 + company: string;
  192 + }>
  193 + width={500}
  194 + open
  195 + title="审核"
  196 + form={form}
  197 + autoFocusFirstInput
  198 + modalProps={{
  199 + okText: '通过',
  200 + cancelText: '驳回',
  201 + destroyOnClose: true,
  202 + onCancel: () => {
  203 + setCheckVisible(false);
  204 + },
  205 + }}
  206 + submitter={{
  207 + render: (props, defaultDoms) => {
  208 + let myDoms = [];
108 myDoms.push( 209 myDoms.push(
109 <Button 210 <Button
110 - key="外部采购" 211 + key="驳回"
111 onClick={() => { 212 onClick={() => {
112 - doCheck({  
113 - flag: false,  
114 - ids: subOrderIds,  
115 - externalProcurement: 1, 213 + if (checkType(CHECK_TYPE.NORMAL)) {
  214 + doCheck({
  215 + flag: false,
  216 + ids: subOrderIds,
  217 + externalProcurement: 0,
  218 + checkNotes: form.getFieldValue('name'),
  219 + });
  220 + return;
  221 + }
  222 +
  223 + //附件处理
  224 + console.log(fileList);
  225 + let fileUrls = fileList?.map((file) => {
  226 + return { url: file.response?.data[0] };
  227 + });
  228 + //财务审核
  229 + doFinancailCheck({
116 checkNotes: form.getFieldValue('name'), 230 checkNotes: form.getFieldValue('name'),
  231 + ids: subOrderIds,
  232 + checkPassOrReject: false,
  233 + invoicingCheckAnnex: fileUrls,
117 }); 234 });
  235 + setUploading(false);
118 }} 236 }}
119 > 237 >
120 - 外部采购 238 + 驳回
121 </Button>, 239 </Button>,
122 ); 240 );
  241 +
  242 + //如果不是财务审核,那么显示这个外部采购
  243 + if (checkType(CHECK_TYPE.NORMAL)) {
  244 + myDoms.push(
  245 + <Button
  246 + key="外部采购"
  247 + onClick={() => {
  248 + doCheck({
  249 + flag: false,
  250 + ids: subOrderIds,
  251 + externalProcurement: 1,
  252 + checkNotes: form.getFieldValue('name'),
  253 + });
  254 + }}
  255 + >
  256 + 外部采购
  257 + </Button>,
  258 + );
  259 + }
  260 +
  261 + //确认
  262 + myDoms.push(defaultDoms[1]);
  263 + return myDoms;
  264 + },
  265 + }}
  266 + submitTimeout={2000}
  267 + onFinish={async (values) => {
  268 + if (checkType(CHECK_TYPE.NORMAL)) {
  269 + //审核通过
  270 + return doCheck({
  271 + flag: true,
  272 + ids: subOrderIds,
  273 + externalProcurement: 0,
  274 + checkNotes: values.name,
  275 + });
123 } 276 }
124 277
125 - //确认  
126 - myDoms.push(defaultDoms[1]);  
127 - return myDoms;  
128 - },  
129 - }}  
130 - submitTimeout={2000}  
131 - onFinish={async (values) => {  
132 - if (checkType(CHECK_TYPE.NORMAL)) {  
133 - //审核通过  
134 - return doCheck({  
135 - flag: true,  
136 - ids: subOrderIds,  
137 - externalProcurement: 0, 278 + //附件处理
  279 + console.log(fileList);
  280 + let fileUrls = fileList?.map((file) => {
  281 + return { url: file.response?.data[0] };
  282 + });
  283 + //财务审核
  284 + return doFinancailCheck({
138 checkNotes: values.name, 285 checkNotes: values.name,
  286 + ids: subOrderIds,
  287 + checkPassOrReject: true,
  288 + invoicingCheckAnnex: fileUrls,
139 }); 289 });
140 - }  
141 -  
142 - //财务审核  
143 - return doFinancailCheck({  
144 - checkNotes: values.name,  
145 - ids: subOrderIds,  
146 - checkPassOrReject: true,  
147 - });  
148 - }}  
149 - onOpenChange={setCheckVisible}  
150 - >  
151 - <div>请特别注意订单总金额与订单金额。</div>  
152 - <ProFormTextArea  
153 - width="lg"  
154 - name="name"  
155 - placeholder="若驳回,请填写驳回理由"  
156 - />  
157 - </ModalForm> 290 + }}
  291 + onOpenChange={setCheckVisible}
  292 + >
  293 + <div>请特别注意订单总金额与订单金额。</div>
  294 + <ProFormTextArea
  295 + width="lg"
  296 + name="name"
  297 + placeholder="若驳回,请填写驳回理由"
  298 + />
  299 + {checkType(CHECK_TYPE.FINALCIAL) ? (
  300 + <>
  301 + {/* <div className="pb-4 text-xs decoration-gray-50">可复制照片粘贴</div> */}
  302 + <Upload {...props}>
  303 + {fileList.length < COMFIR_RECEIPT_IMAGES_NUMBER
  304 + ? uploadButton
  305 + : ''}
  306 + </Upload>
  307 + </>
  308 + ) : (
  309 + ''
  310 + )}
  311 + </ModalForm>
  312 +
  313 + <Modal
  314 + open={previewOpen}
  315 + title={previewTitle}
  316 + footer={null}
  317 + onCancel={handleCancel}
  318 + >
  319 + <img alt="图片预览" style={{ width: '100%' }} src={previewImage} />
  320 + </Modal>
  321 + </>
158 ); 322 );
159 }; 323 };
src/pages/Order/components/FinancialDrawer.tsx
@@ -64,6 +64,7 @@ export default ({ @@ -64,6 +64,7 @@ export default ({
64 invoicingNotes: form.getFieldValue('invoicingNotes'), 64 invoicingNotes: form.getFieldValue('invoicingNotes'),
65 invoicingStatus: form.getFieldValue('invoicingStatus'), 65 invoicingStatus: form.getFieldValue('invoicingStatus'),
66 mainorderOrSubOrderInvoicing: isMainOrder, 66 mainorderOrSubOrderInvoicing: isMainOrder,
  67 + afterInvoicingStatus: form.getFieldValue('afterInvoicingStatus'),
67 }; 68 };
68 if (isEdit) { 69 if (isEdit) {
69 res = await postServiceOrderEditOrder({ data: body }); 70 res = await postServiceOrderEditOrder({ data: body });
@@ -140,17 +141,19 @@ export default ({ @@ -140,17 +141,19 @@ export default ({
140 ] 141 ]
141 : ''} 142 : ''}
142 143
143 - {/* <ProFormSelect 144 + <ProFormSelect
144 placeholder="是否完全开票" 145 placeholder="是否完全开票"
145 - name="invoicingType" 146 + name="afterInvoicingStatus"
146 width="lg" 147 width="lg"
147 label="是否完全开票" 148 label="是否完全开票"
148 - options={[{label:'部分开票',value:'部分开票'},{label:'完全开票',value:'完全开票'}]}  
149 - onChange={setInvoicingStatus}  
150 - initialValue={'完全开票'} 149 + options={[
  150 + { label: '部分开票', value: 'PARTIAL_INVOICING' },
  151 + { label: '完全开票', value: 'COMPLETE_INVOICING' },
  152 + ]}
  153 + initialValue={'PARTIAL_INVOICING'}
151 // disabled={mainInfoDisbled} 154 // disabled={mainInfoDisbled}
152 - rules={[{ required: true, message: '是否需要开票必填' }]}  
153 - /> */} 155 + rules={[{ required: true, message: '是否完全开票必填' }]}
  156 + />
154 <ProFormTextArea 157 <ProFormTextArea
155 width="lg" 158 width="lg"
156 name="invoicingNotes" 159 name="invoicingNotes"
src/pages/Order/components/OtherInfoModal.tsx 0 → 100644
  1 +import { Col, Modal, Row } from 'antd';
  2 +
  3 +const DeliverModal = ({ data, setVisible }) => {
  4 + return (
  5 + <Modal
  6 + open
  7 + width={900}
  8 + title={'其他信息'}
  9 + onOk={async () => {}}
  10 + onCancel={() => {
  11 + setVisible(false);
  12 + }}
  13 + >
  14 + <Row gutter={[16, 24]}>
  15 + <Col span={6}>
  16 + <span className="text-[#333333]">销售申请开票附件</span>
  17 + </Col>
  18 + <Col span={18}>{data.customerName}</Col>
  19 + <Col span={6}>
  20 + <span className="className='text-[#333333]'">联系方式</span>
  21 + </Col>
  22 + <Col span={18}>{data.customerContactNumber}</Col>
  23 +
  24 + <Col span={6}>
  25 + <span className="className='text-[#333333]'">收货地址</span>
  26 + </Col>
  27 + <Col span={18}>{data.customerShippingAddress}</Col>
  28 +
  29 + <Col span={6}>
  30 + <span className="className='text-[#333333]'">单位联系人</span>
  31 + </Col>
  32 + <Col span={18}>{data.institutionContactName}</Col>
  33 + <Col span={6}>
  34 + <span className="className='text-[#333333]'">单位名称</span>
  35 + </Col>
  36 + <Col span={18}>{data.institution}</Col>
  37 + <Col span={6}>
  38 + <span className="className='text-[#333333]'">开户银行</span>
  39 + </Col>
  40 + <Col span={18}>{data.bank}</Col>
  41 +
  42 + <Col span={6}>
  43 + <span className="className='text-[#333333]'">银行账号</span>
  44 + </Col>
  45 + <Col span={18}>{data.bankAccountNumber}</Col>
  46 + <Col span={6}>
  47 + <span className="className='text-[#333333]'">开票识别号</span>
  48 + </Col>
  49 + <Col span={18}>{data.invoiceIdentificationNumber}</Col>
  50 + </Row>
  51 + </Modal>
  52 + );
  53 +};
  54 +
  55 +export default DeliverModal;
src/pages/Order/constant.ts
@@ -127,6 +127,11 @@ export const FINANCIAL_STATUS_OPTIONS = { @@ -127,6 +127,11 @@ export const FINANCIAL_STATUS_OPTIONS = {
127 UN_INVOICING: '取消开票', 127 UN_INVOICING: '取消开票',
128 }; 128 };
129 129
  130 +export const AFTER_INVOICING_STATUS = {
  131 + PARTIAL_INVOICING: '部分开票',
  132 + COMPLETE_INVOICING: '完全开票',
  133 +};
  134 +
130 export const TAGS_COLOR = new Map<string, string>([ 135 export const TAGS_COLOR = new Map<string, string>([
131 ['UN_INVOICE', 'success'], 136 ['UN_INVOICE', 'success'],
132 ['INVOICED', 'processing'], 137 ['INVOICED', 'processing'],
@@ -364,6 +369,13 @@ export const MAIN_ORDER_COLUMNS = [ @@ -364,6 +369,13 @@ export const MAIN_ORDER_COLUMNS = [
364 valueEnum: enumToProTableEnumValue(INVOCING_STATUS_OPTIONS), 369 valueEnum: enumToProTableEnumValue(INVOCING_STATUS_OPTIONS),
365 }, 370 },
366 { 371 {
  372 + title: '是否完全开票',
  373 + dataIndex: 'afterInvoicingStatus',
  374 + valueType: 'select',
  375 + hideInTable: true,
  376 + valueEnum: enumToProTableEnumValue(AFTER_INVOICING_STATUS),
  377 + },
  378 + {
367 title: '开票日期', 379 title: '开票日期',
368 dataIndex: 'invoicingTime', 380 dataIndex: 'invoicingTime',
369 valueType: 'dateRange', 381 valueType: 'dateRange',
src/pages/Order/index.tsx
@@ -55,6 +55,7 @@ import OrderNotesEditModal from &#39;./components/OrderNotesEditModal&#39;; @@ -55,6 +55,7 @@ import OrderNotesEditModal from &#39;./components/OrderNotesEditModal&#39;;
55 import ProcureCheckModal from './components/ProcureCheckModal'; 55 import ProcureCheckModal from './components/ProcureCheckModal';
56 import SubOrderComfirmReceiptImagesModal from './components/SubOrderComfirmReceiptImagesModal'; 56 import SubOrderComfirmReceiptImagesModal from './components/SubOrderComfirmReceiptImagesModal';
57 import { 57 import {
  58 + AFTER_INVOICING_STATUS,
58 AFTE_SALES_PLAN_OPTIONS, 59 AFTE_SALES_PLAN_OPTIONS,
59 CHECK_TYPE, 60 CHECK_TYPE,
60 LOGISTICS_STATUS_OPTIONS, 61 LOGISTICS_STATUS_OPTIONS,
@@ -392,6 +393,20 @@ const OrderPage = () =&gt; { @@ -392,6 +393,20 @@ const OrderPage = () =&gt; {
392 }} 393 }}
393 /> 394 />
394 </Flex> 395 </Flex>
  396 +
  397 + {optRecord.applyInvoicingNotes !== undefined &&
  398 + optRecord.applyInvoicingNotes !== null ? (
  399 + <Flex title={optRecord.notes}>
  400 + <div className="max-w-[90%] whitespace-no-wrap overflow-hidden overflow-ellipsis">
  401 + <span className="text-[#8C8C8C]">
  402 + 开票备注:
  403 + {optRecord.applyInvoicingNotes}
  404 + </span>
  405 + </div>
  406 + </Flex>
  407 + ) : (
  408 + ''
  409 + )}
395 </Flex> 410 </Flex>
396 <Flex className="w-[16%]" vertical gap="small"> 411 <Flex className="w-[16%]" vertical gap="small">
397 <div 412 <div
@@ -456,18 +471,25 @@ const OrderPage = () =&gt; { @@ -456,18 +471,25 @@ const OrderPage = () =&gt; {
456 </div> 471 </div>
457 472
458 {/* 开票类型 */} 473 {/* 开票类型 */}
459 - <div className="overflow-hidden whitespace-no-wrap overflow-ellipsis">  
460 - <span className="text-slate-700">  
461 - {getInvoicingType(optRecord)}  
462 - </span>  
463 - </div> 474 + {getInvoicingType(optRecord) === undefined ? (
  475 + <div className="overflow-hidden whitespace-no-wrap overflow-ellipsis">
  476 + <span className="text-slate-700">
  477 + {getInvoicingType(optRecord)}
  478 + </span>
  479 + </div>
  480 + ) : (
  481 + ''
  482 + )}
464 483
465 {/* 开票状态 */} 484 {/* 开票状态 */}
466 - {/* <div className="overflow-hidden whitespace-no-wrap overflow-ellipsis"> 485 + <div className="overflow-hidden whitespace-no-wrap overflow-ellipsis">
467 <span className="text-slate-700"> 486 <span className="text-slate-700">
468 - 部分开票 487 + {enumValueToLabel(
  488 + optRecord.afterInvoicingStatus,
  489 + AFTER_INVOICING_STATUS,
  490 + )}
469 </span> 491 </span>
470 - </div> */} 492 + </div>
471 </Flex> 493 </Flex>
472 494
473 <Flex className="w-[10%]" vertical gap="small"> 495 <Flex className="w-[10%]" vertical gap="small">