Commit b5fb40081227e20e5e5139aabd4e459ec4d57a66
Merge branch 'znh' into 'develop'
feat: update 申请开票,部分开票 See merge request !20
Showing
6 changed files
with
360 additions
and
103 deletions
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 './components/OrderNotesEditModal'; |
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 = () => { |
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 = () => { |
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"> | ... | ... |