Commit 3c481d514ac3c575fea1dfd6fe645838341af149
1 parent
0c311287
feat: update 银行流水导入
Showing
3 changed files
with
429 additions
and
142 deletions
src/pages/Invoice/components/BankImportModal.tsx
0 → 100644
1 | +import { RESPONSE_CODE } from '@/constants/enum'; | |
2 | +import { postServiceBankStatementImportBankStatementForm } from '@/services'; | |
3 | +import { UploadOutlined } from '@ant-design/icons'; | |
4 | +import { ModalForm } from '@ant-design/pro-components'; | |
5 | +import { Button, Form, Upload, UploadFile, UploadProps, message } from 'antd'; | |
6 | +import { RcFile } from 'antd/es/upload'; | |
7 | +import axios from 'axios'; | |
8 | +import { useState } from 'react'; | |
9 | + | |
10 | +// import { cloneDeep } from 'lodash'; | |
11 | +export default ({ setVisible, onClose }) => { | |
12 | + const [form] = Form.useForm<{ name: string; company: string }>(); | |
13 | + const [fileList, setFileList] = useState<UploadFile[]>([]); | |
14 | + const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => | |
15 | + setFileList(newFileList); | |
16 | + | |
17 | + const [messageApi] = message.useMessage(); | |
18 | + const [setUploading] = useState(false); | |
19 | + | |
20 | + const exportLoading = (content: string) => { | |
21 | + messageApi.open({ | |
22 | + type: 'loading', | |
23 | + content: content, | |
24 | + duration: 0, | |
25 | + }); | |
26 | + }; | |
27 | + | |
28 | + const downloadImportTemplate = async () => { | |
29 | + axios({ | |
30 | + url: '/api/service/bankStatement/exportTemplate', | |
31 | + method: 'post', | |
32 | + responseType: 'blob', | |
33 | + headers: { Authorization: localStorage.getItem('token') }, | |
34 | + }) | |
35 | + .then((response) => { | |
36 | + // 创建一个新的 Blob 对象,它包含了服务器响应的数据(即你的 Excel 文件) | |
37 | + const blob = new Blob([response.data]); // Excel 的 MIME 类型 | |
38 | + const downloadUrl = window.URL.createObjectURL(blob); | |
39 | + const a = document.createElement('a'); | |
40 | + a.href = downloadUrl; | |
41 | + a.download = '银行流水导入模板.xlsx'; // 你可以为文件命名 | |
42 | + document.body.appendChild(a); | |
43 | + a.click(); // 模拟点击操作来下载文件 | |
44 | + URL.revokeObjectURL(downloadUrl); // 释放掉 blob 对象所占用的内存 | |
45 | + document.body.removeChild(a); | |
46 | + }) | |
47 | + .catch((error) => { | |
48 | + // 处理错误 | |
49 | + console.error('导出错误', error); | |
50 | + }); | |
51 | + }; | |
52 | + const handleUpload = async () => { | |
53 | + const formData = new FormData(); | |
54 | + fileList.forEach((file) => { | |
55 | + //originFileObj二进制文件 | |
56 | + formData.append('file', file.originFileObj as RcFile); | |
57 | + }); | |
58 | + // console.log(fileList[0] as RcFile) | |
59 | + // formData.append('file', fileList[0] as RcFile); | |
60 | + setUploading(true); | |
61 | + // You can use any AJAX library you like | |
62 | + const res = await postServiceBankStatementImportBankStatementForm({ | |
63 | + data: formData, | |
64 | + headers: { | |
65 | + 'Content-Type': | |
66 | + 'multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq', | |
67 | + }, | |
68 | + }); | |
69 | + | |
70 | + if (res.result === RESPONSE_CODE.SUCCESS) { | |
71 | + message.success(res.message); | |
72 | + onClose(); | |
73 | + } else { | |
74 | + if (res.message === '表格中没有数据') { | |
75 | + setUploading(false); | |
76 | + return; | |
77 | + } | |
78 | + //存在错误信息,下载错误信息模板 | |
79 | + exportLoading('正在下载错误信息...'); | |
80 | + } | |
81 | + | |
82 | + setUploading(false); | |
83 | + }; | |
84 | + const props: UploadProps = { | |
85 | + onRemove: (file) => { | |
86 | + const index = fileList.indexOf(file); | |
87 | + const newFileList = fileList.slice(); | |
88 | + newFileList.splice(index, 1); | |
89 | + setFileList(newFileList); | |
90 | + }, | |
91 | + beforeUpload: (file) => { | |
92 | + setFileList([...fileList, file]); | |
93 | + | |
94 | + return false; | |
95 | + }, | |
96 | + fileList, | |
97 | + onChange: handleChange, | |
98 | + accept: '.xlsx', | |
99 | + }; | |
100 | + | |
101 | + return ( | |
102 | + <> | |
103 | + <ModalForm<{ | |
104 | + name: string; | |
105 | + company: string; | |
106 | + }> | |
107 | + width={500} | |
108 | + open | |
109 | + title="标题" | |
110 | + form={form} | |
111 | + autoFocusFirstInput | |
112 | + modalProps={{ | |
113 | + okText: '确定', | |
114 | + cancelText: '取消', | |
115 | + destroyOnClose: true, | |
116 | + onCancel: () => { | |
117 | + setVisible(false); | |
118 | + }, | |
119 | + }} | |
120 | + onFinish={async () => { | |
121 | + handleUpload(); | |
122 | + onClose(); | |
123 | + }} | |
124 | + onOpenChange={setVisible} | |
125 | + > | |
126 | + <div className="py-4 font-semibold"> | |
127 | + 导入银行流水 | |
128 | + <Button type="link" onClick={downloadImportTemplate}> | |
129 | + 下载导入模板 | |
130 | + </Button> | |
131 | + </div> | |
132 | + <Upload {...props}> | |
133 | + <Button icon={<UploadOutlined />} disabled={fileList.length > 0}> | |
134 | + 点击选择文件 | |
135 | + </Button> | |
136 | + </Upload> | |
137 | + </ModalForm> | |
138 | + </> | |
139 | + ); | |
140 | +}; | |
... | ... |
src/pages/Invoice/constant.tsx
1 | -import { TableDropdown } from '@ant-design/pro-components'; | |
2 | - | |
3 | 1 | export type InvoiceItem = { |
4 | 2 | id: number; //id |
5 | 3 | invoiceStatus: string; //发票类型:专票/普票 |
... | ... | @@ -82,34 +80,6 @@ export const INVOICE_COLUMNS = [ |
82 | 80 | valueType: 'text', |
83 | 81 | width: 250, |
84 | 82 | }, |
85 | - { | |
86 | - title: '操作', | |
87 | - valueType: 'option', | |
88 | - key: 'option', | |
89 | - fixed: 'right', | |
90 | - width: 120, | |
91 | - render: (text, record, _, action) => [ | |
92 | - <a | |
93 | - key="editable" | |
94 | - onClick={() => { | |
95 | - action?.startEditable?.(record.id); | |
96 | - }} | |
97 | - > | |
98 | - 编辑 | |
99 | - </a>, | |
100 | - <a href={record.url} target="_blank" rel="noopener noreferrer" key="view"> | |
101 | - 查看 | |
102 | - </a>, | |
103 | - <TableDropdown | |
104 | - key="actionGroup" | |
105 | - onSelect={() => action?.reload()} | |
106 | - menus={[ | |
107 | - { key: 'copy', name: '复制' }, | |
108 | - { key: 'delete', name: '删除' }, | |
109 | - ]} | |
110 | - />, | |
111 | - ], | |
112 | - }, | |
113 | 83 | ]; |
114 | 84 | |
115 | 85 | export const INVOICE_STATUS = { |
... | ... |
src/pages/Invoice/index.tsx
1 | 1 | import { INVOICE_COLUMNS, INVOICE_STATUS } from '@/pages/Invoice/constant'; |
2 | 2 | import { postServiceInvoiceQueryInvoice } from '@/services'; |
3 | -import { enumValueToLabel } from '@/utils'; | |
3 | +import { enumValueToLabel, formatDateTime } from '@/utils'; | |
4 | +import { formatDate } from '@/utils/time'; | |
4 | 5 | import { getUserInfo } from '@/utils/user'; |
5 | -import { EllipsisOutlined } from '@ant-design/icons'; | |
6 | +import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons'; | |
6 | 7 | import { |
7 | 8 | ActionType, |
8 | 9 | PageContainer, |
9 | 10 | ProTable, |
10 | 11 | } from '@ant-design/pro-components'; |
11 | 12 | import { history } from '@umijs/max'; |
12 | -import { Avatar, Button, Dropdown, Tag } from 'antd'; | |
13 | -import { useRef } from 'react'; | |
13 | +import { Avatar, Button, Dropdown, Tabs, Tag } from 'antd'; | |
14 | +import { useRef, useState } from 'react'; | |
14 | 15 | import { INVOCING_STATUS, PAYEE_OPTIONS } from '../Order/constant'; |
16 | +import BankImportModal from './components/BankImportModal'; | |
15 | 17 | import './index.less'; |
18 | +const InvoicePage = () => { | |
19 | + const invoiceActionRef = useRef<ActionType>(); | |
20 | + const bankActionRef = useRef<ActionType>(); | |
21 | + const [bankImportModalVisible, setBankImportModalVisible] = useState(false); | |
16 | 22 | |
17 | -const userInfo = getUserInfo(); | |
23 | + const userInfo = getUserInfo(); | |
18 | 24 | |
19 | -const BreakWordDiv = ({ text = '暂无内容' }) => { | |
20 | - return <div className="overflow-wrap-break-word">{text}</div>; | |
21 | -}; | |
25 | + const BreakWordDiv = ({ text = '暂无内容' }) => { | |
26 | + return ( | |
27 | + <div | |
28 | + title={text} | |
29 | + className="overflow-hidden whitespace-no-wrap overflow-ellipsis whitespace-nowrap" | |
30 | + > | |
31 | + {text} | |
32 | + </div> | |
33 | + ); | |
34 | + }; | |
22 | 35 | |
23 | -const getTableCellText = (target: any) => { | |
24 | - if (!target) { | |
25 | - return ''; | |
26 | - } | |
36 | + const getTableCellText = (target: any) => { | |
37 | + if (!target) { | |
38 | + return ''; | |
39 | + } | |
27 | 40 | |
28 | - if (target.props) { | |
29 | - return target.props.text; | |
30 | - } | |
41 | + if (target.props) { | |
42 | + return target.props.text; | |
43 | + } | |
31 | 44 | |
32 | - return target; | |
33 | -}; | |
45 | + return target; | |
46 | + }; | |
34 | 47 | |
35 | -const InvoicePage = () => { | |
36 | - const actionRef = useRef<ActionType>(); | |
48 | + /** | |
49 | + * 加载发票列表表格的各个列格式 | |
50 | + */ | |
51 | + const invoicecColumnsInit = () => { | |
52 | + let columns = INVOICE_COLUMNS.map((item) => { | |
53 | + let newItem = { ...item }; | |
54 | + let dataIndex = item.dataIndex; | |
55 | + let dataType = item.valueType; | |
56 | + | |
57 | + newItem.render = (text, record) => { | |
58 | + let textValue = record[dataIndex]; | |
59 | + | |
60 | + if (dataType === 'date') { | |
61 | + textValue = formatDate(textValue); | |
62 | + } | |
63 | + | |
64 | + if (dataType === 'dateTime') { | |
65 | + textValue = formatDateTime(textValue); | |
66 | + } | |
67 | + | |
68 | + if (dataType === 'money') { | |
69 | + textValue = '¥' + textValue; | |
70 | + } | |
71 | + | |
72 | + switch (dataIndex) { | |
73 | + case 'invoiceStatus': | |
74 | + return ( | |
75 | + <BreakWordDiv | |
76 | + text={enumValueToLabel( | |
77 | + getTableCellText(textValue), | |
78 | + INVOCING_STATUS, | |
79 | + )} | |
80 | + /> | |
81 | + ); | |
82 | + | |
83 | + case 'status': | |
84 | + return ( | |
85 | + <BreakWordDiv | |
86 | + text={enumValueToLabel( | |
87 | + getTableCellText(textValue), | |
88 | + INVOICE_STATUS, | |
89 | + )} | |
90 | + /> | |
91 | + ); | |
92 | + | |
93 | + case 'payee': | |
94 | + return ( | |
95 | + <BreakWordDiv | |
96 | + text={enumValueToLabel( | |
97 | + getTableCellText(textValue), | |
98 | + PAYEE_OPTIONS, | |
99 | + )} | |
100 | + /> | |
101 | + ); | |
102 | + | |
103 | + default: | |
104 | + return <BreakWordDiv text={getTableCellText(textValue)} />; | |
105 | + } | |
106 | + }; | |
107 | + | |
108 | + return newItem; | |
109 | + }); | |
110 | + | |
111 | + columns.push({ | |
112 | + title: '操作', | |
113 | + valueType: 'option', | |
114 | + key: 'option', | |
115 | + fixed: 'right', | |
116 | + width: 120, | |
117 | + render: (text, record, _, action) => [ | |
118 | + <a | |
119 | + key="editable" | |
120 | + onClick={() => { | |
121 | + action?.startEditable?.(record.id); | |
122 | + }} | |
123 | + > | |
124 | + 核销 | |
125 | + </a>, | |
126 | + <a | |
127 | + href={record.url} | |
128 | + target="_blank" | |
129 | + rel="noopener noreferrer" | |
130 | + key="view" | |
131 | + > | |
132 | + 查看 | |
133 | + </a>, | |
134 | + <a | |
135 | + href={record.url} | |
136 | + target="_blank" | |
137 | + rel="noopener noreferrer" | |
138 | + key="view2" | |
139 | + > | |
140 | + 删除 | |
141 | + </a>, | |
142 | + ], | |
143 | + }); | |
144 | + | |
145 | + return columns; | |
146 | + }; | |
147 | + | |
148 | + /** | |
149 | + * 发票表 | |
150 | + * @param param0 | |
151 | + * @returns | |
152 | + */ | |
153 | + const InvoiceTable = ({ actionRef = undefined }) => { | |
154 | + return ( | |
155 | + <ProTable | |
156 | + columns={invoicecColumnsInit()} | |
157 | + actionRef={actionRef} | |
158 | + cardBordered | |
159 | + pagination={{ | |
160 | + pageSize: 10, | |
161 | + }} | |
162 | + request={async (params) => { | |
163 | + const res = await postServiceInvoiceQueryInvoice({ | |
164 | + data: { ...params }, | |
165 | + }); | |
166 | + if (res) { | |
167 | + return { | |
168 | + data: res?.data?.data || [], | |
169 | + total: res?.data?.total || 0, | |
170 | + }; | |
171 | + } | |
172 | + }} | |
173 | + columnsState={{ | |
174 | + persistenceKey: 'pro-table-singe-demos', | |
175 | + persistenceType: 'localStorage', | |
176 | + defaultValue: { | |
177 | + option: { fixed: 'right', disable: true }, | |
178 | + }, | |
179 | + onChange(value) { | |
180 | + console.log('value: ', value); | |
181 | + }, | |
182 | + }} | |
183 | + rowKey="id" | |
184 | + search={{ | |
185 | + labelWidth: 'auto', | |
186 | + }} | |
187 | + options={{ | |
188 | + setting: { | |
189 | + listsHeight: 400, | |
190 | + }, | |
191 | + }} | |
192 | + form={{ | |
193 | + // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下 | |
194 | + syncToUrl: (values, type) => { | |
195 | + if (type === 'get') { | |
196 | + return { | |
197 | + ...values, | |
198 | + created_at: [values.startTime, values.endTime], | |
199 | + }; | |
200 | + } | |
201 | + return values; | |
202 | + }, | |
203 | + }} | |
204 | + dateFormatter="string" | |
205 | + headerTitle="发票列表" | |
206 | + scroll={{ x: 1400, y: 360 }} | |
207 | + /> | |
208 | + ); | |
209 | + }; | |
37 | 210 | |
211 | + /** | |
212 | + * 银行流水表 | |
213 | + * @param param0 | |
214 | + * @returns | |
215 | + */ | |
216 | + const BankTable = ({ actionRef = undefined }) => { | |
217 | + return ( | |
218 | + <ProTable | |
219 | + columns={invoicecColumnsInit()} | |
220 | + actionRef={actionRef} | |
221 | + cardBordered | |
222 | + pagination={{ | |
223 | + pageSize: 10, | |
224 | + }} | |
225 | + request={async (params) => { | |
226 | + const res = await postServiceInvoiceQueryInvoice({ | |
227 | + data: { ...params }, | |
228 | + }); | |
229 | + if (res) { | |
230 | + return { | |
231 | + data: res?.data?.data || [], | |
232 | + total: res?.data?.total || 0, | |
233 | + }; | |
234 | + } | |
235 | + }} | |
236 | + columnsState={{ | |
237 | + persistenceKey: 'pro-table-singe-demos', | |
238 | + persistenceType: 'localStorage', | |
239 | + defaultValue: { | |
240 | + option: { fixed: 'right', disable: true }, | |
241 | + }, | |
242 | + onChange(value) { | |
243 | + console.log('value: ', value); | |
244 | + }, | |
245 | + }} | |
246 | + rowKey="id" | |
247 | + search={{ | |
248 | + labelWidth: 'auto', | |
249 | + }} | |
250 | + options={{ | |
251 | + setting: { | |
252 | + listsHeight: 400, | |
253 | + }, | |
254 | + }} | |
255 | + form={{ | |
256 | + // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下 | |
257 | + syncToUrl: (values, type) => { | |
258 | + if (type === 'get') { | |
259 | + return { | |
260 | + ...values, | |
261 | + created_at: [values.startTime, values.endTime], | |
262 | + }; | |
263 | + } | |
264 | + return values; | |
265 | + }, | |
266 | + }} | |
267 | + dateFormatter="string" | |
268 | + headerTitle="银行流水列表" | |
269 | + scroll={{ x: 1400, y: 360 }} | |
270 | + toolBarRender={() => [ | |
271 | + <Button | |
272 | + key="button" | |
273 | + icon={<PlusOutlined />} | |
274 | + onClick={() => { | |
275 | + setBankImportModalVisible(true); | |
276 | + }} | |
277 | + type="primary" | |
278 | + > | |
279 | + 导入 | |
280 | + </Button>, | |
281 | + ]} | |
282 | + /> | |
283 | + ); | |
284 | + }; | |
285 | + | |
286 | + const tabsItems = [ | |
287 | + { | |
288 | + key: 1, | |
289 | + label: '发票管理', | |
290 | + children: <InvoiceTable actionRef={invoiceActionRef} />, | |
291 | + }, | |
292 | + { | |
293 | + key: 2, | |
294 | + label: '银行流水', | |
295 | + children: <BankTable actionRef={bankActionRef} />, | |
296 | + }, | |
297 | + ]; | |
38 | 298 | return ( |
39 | 299 | <> |
40 | 300 | <PageContainer |
... | ... | @@ -73,100 +333,17 @@ const InvoicePage = () => { |
73 | 333 | ], |
74 | 334 | }} |
75 | 335 | > |
76 | - <ProTable | |
77 | - columns={INVOICE_COLUMNS.map((item) => { | |
78 | - let newItem = { ...item }; | |
79 | - if (item.dataIndex === 'invoiceStatus') { | |
80 | - newItem.render = (text) => { | |
81 | - return ( | |
82 | - <BreakWordDiv | |
83 | - text={enumValueToLabel( | |
84 | - getTableCellText(text), | |
85 | - INVOCING_STATUS, | |
86 | - )} | |
87 | - /> | |
88 | - ); | |
89 | - }; | |
90 | - } | |
91 | - if (item.dataIndex === 'status') { | |
92 | - newItem.render = (text) => { | |
93 | - console.log(text); | |
94 | - return ( | |
95 | - <BreakWordDiv | |
96 | - text={enumValueToLabel( | |
97 | - getTableCellText(text), | |
98 | - INVOICE_STATUS, | |
99 | - )} | |
100 | - /> | |
101 | - ); | |
102 | - }; | |
103 | - } | |
104 | - if (item.dataIndex === 'payee') { | |
105 | - newItem.render = (text) => { | |
106 | - return ( | |
107 | - <BreakWordDiv | |
108 | - text={enumValueToLabel( | |
109 | - getTableCellText(text), | |
110 | - PAYEE_OPTIONS, | |
111 | - )} | |
112 | - /> | |
113 | - ); | |
114 | - }; | |
115 | - } | |
116 | - return newItem; | |
117 | - })} | |
118 | - actionRef={actionRef} | |
119 | - cardBordered | |
120 | - pagination={{ | |
121 | - pageSize: 10, | |
122 | - }} | |
123 | - request={async (params) => { | |
124 | - const res = await postServiceInvoiceQueryInvoice({ | |
125 | - data: { ...params }, | |
126 | - }); | |
127 | - if (res) { | |
128 | - return { | |
129 | - data: res?.data?.data || [], | |
130 | - total: res?.data?.total || 0, | |
131 | - }; | |
132 | - } | |
133 | - }} | |
134 | - columnsState={{ | |
135 | - persistenceKey: 'pro-table-singe-demos', | |
136 | - persistenceType: 'localStorage', | |
137 | - defaultValue: { | |
138 | - option: { fixed: 'right', disable: true }, | |
139 | - }, | |
140 | - onChange(value) { | |
141 | - console.log('value: ', value); | |
142 | - }, | |
143 | - }} | |
144 | - rowKey="id" | |
145 | - search={{ | |
146 | - labelWidth: 'auto', | |
147 | - }} | |
148 | - options={{ | |
149 | - setting: { | |
150 | - listsHeight: 400, | |
151 | - }, | |
152 | - }} | |
153 | - form={{ | |
154 | - // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下 | |
155 | - syncToUrl: (values, type) => { | |
156 | - if (type === 'get') { | |
157 | - return { | |
158 | - ...values, | |
159 | - created_at: [values.startTime, values.endTime], | |
160 | - }; | |
161 | - } | |
162 | - return values; | |
163 | - }, | |
164 | - }} | |
165 | - dateFormatter="string" | |
166 | - headerTitle="发票列表" | |
167 | - scroll={{ x: 1400, y: 420 }} | |
168 | - /> | |
336 | + <Tabs defaultActiveKey="1" items={tabsItems} /> | |
169 | 337 | </PageContainer> |
338 | + | |
339 | + {bankImportModalVisible ? ( | |
340 | + <BankImportModal | |
341 | + setVisible={setBankImportModalVisible} | |
342 | + onClose={() => {}} | |
343 | + ></BankImportModal> | |
344 | + ) : ( | |
345 | + '' | |
346 | + )} | |
170 | 347 | </> |
171 | 348 | ); |
172 | 349 | }; |
... | ... |