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 47 });
48 48  
49 49 const res = await postServiceOrderApplyInvoicing({ data: values });
50   - if ((res.result = RESPONSE_CODE.SUCCESS)) {
  50 +
  51 + if (res.result === RESPONSE_CODE.SUCCESS) {
51 52 message.success(res.message);
52 53 onClose();
53 54 }
... ...
src/pages/Order/components/CheckModal.tsx
... ... @@ -4,8 +4,12 @@ import {
4 4 postServiceOrderFinanceCheckOrder,
5 5 } from '@/services';
6 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 13 export default ({
10 14 setCheckVisible,
11 15 data,
... ... @@ -13,6 +17,130 @@ export default ({
13 17 orderCheckType,
14 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 144 const [form] = Form.useForm<{ name: string; company: string }>();
17 145 let subOrderIds: any[] = [];
18 146 //是单条子订单审核
... ... @@ -57,103 +185,139 @@ export default ({
57 185 }
58 186  
59 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 209 myDoms.push(
109 210 <Button
110   - key="外部采购"
  211 + key="驳回"
111 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 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 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 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 64 invoicingNotes: form.getFieldValue('invoicingNotes'),
65 65 invoicingStatus: form.getFieldValue('invoicingStatus'),
66 66 mainorderOrSubOrderInvoicing: isMainOrder,
  67 + afterInvoicingStatus: form.getFieldValue('afterInvoicingStatus'),
67 68 };
68 69 if (isEdit) {
69 70 res = await postServiceOrderEditOrder({ data: body });
... ... @@ -140,17 +141,19 @@ export default ({
140 141 ]
141 142 : ''}
142 143  
143   - {/* <ProFormSelect
  144 + <ProFormSelect
144 145 placeholder="是否完全开票"
145   - name="invoicingType"
  146 + name="afterInvoicingStatus"
146 147 width="lg"
147 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 154 // disabled={mainInfoDisbled}
152   - rules={[{ required: true, message: '是否需要开票必填' }]}
153   - /> */}
  155 + rules={[{ required: true, message: '是否完全开票必填' }]}
  156 + />
154 157 <ProFormTextArea
155 158 width="lg"
156 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 127 UN_INVOICING: '取消开票',
128 128 };
129 129  
  130 +export const AFTER_INVOICING_STATUS = {
  131 + PARTIAL_INVOICING: '部分开票',
  132 + COMPLETE_INVOICING: '完全开票',
  133 +};
  134 +
130 135 export const TAGS_COLOR = new Map<string, string>([
131 136 ['UN_INVOICE', 'success'],
132 137 ['INVOICED', 'processing'],
... ... @@ -364,6 +369,13 @@ export const MAIN_ORDER_COLUMNS = [
364 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 379 title: '开票日期',
368 380 dataIndex: 'invoicingTime',
369 381 valueType: 'dateRange',
... ...
src/pages/Order/index.tsx
... ... @@ -55,6 +55,7 @@ import OrderNotesEditModal from &#39;./components/OrderNotesEditModal&#39;;
55 55 import ProcureCheckModal from './components/ProcureCheckModal';
56 56 import SubOrderComfirmReceiptImagesModal from './components/SubOrderComfirmReceiptImagesModal';
57 57 import {
  58 + AFTER_INVOICING_STATUS,
58 59 AFTE_SALES_PLAN_OPTIONS,
59 60 CHECK_TYPE,
60 61 LOGISTICS_STATUS_OPTIONS,
... ... @@ -392,6 +393,20 @@ const OrderPage = () =&gt; {
392 393 }}
393 394 />
394 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 410 </Flex>
396 411 <Flex className="w-[16%]" vertical gap="small">
397 412 <div
... ... @@ -456,18 +471,25 @@ const OrderPage = () =&gt; {
456 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 486 <span className="text-slate-700">
468   - 部分开票
  487 + {enumValueToLabel(
  488 + optRecord.afterInvoicingStatus,
  489 + AFTER_INVOICING_STATUS,
  490 + )}
469 491 </span>
470   - </div> */}
  492 + </div>
471 493 </Flex>
472 494  
473 495 <Flex className="w-[10%]" vertical gap="small">
... ...