Commit 3c481d514ac3c575fea1dfd6fe645838341af149

Authored by zhongnanhuang
1 parent 0c311287

feat: update 银行流水导入

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 export type InvoiceItem = { 1 export type InvoiceItem = {
4 id: number; //id 2 id: number; //id
5 invoiceStatus: string; //发票类型:专票/普票 3 invoiceStatus: string; //发票类型:专票/普票
@@ -82,34 +80,6 @@ export const INVOICE_COLUMNS = [ @@ -82,34 +80,6 @@ export const INVOICE_COLUMNS = [
82 valueType: 'text', 80 valueType: 'text',
83 width: 250, 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 export const INVOICE_STATUS = { 85 export const INVOICE_STATUS = {
src/pages/Invoice/index.tsx
1 import { INVOICE_COLUMNS, INVOICE_STATUS } from '@/pages/Invoice/constant'; 1 import { INVOICE_COLUMNS, INVOICE_STATUS } from '@/pages/Invoice/constant';
2 import { postServiceInvoiceQueryInvoice } from '@/services'; 2 import { postServiceInvoiceQueryInvoice } from '@/services';
3 -import { enumValueToLabel } from '@/utils'; 3 +import { enumValueToLabel, formatDateTime } from '@/utils';
  4 +import { formatDate } from '@/utils/time';
4 import { getUserInfo } from '@/utils/user'; 5 import { getUserInfo } from '@/utils/user';
5 -import { EllipsisOutlined } from '@ant-design/icons'; 6 +import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
6 import { 7 import {
7 ActionType, 8 ActionType,
8 PageContainer, 9 PageContainer,
9 ProTable, 10 ProTable,
10 } from '@ant-design/pro-components'; 11 } from '@ant-design/pro-components';
11 import { history } from '@umijs/max'; 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 import { INVOCING_STATUS, PAYEE_OPTIONS } from '../Order/constant'; 15 import { INVOCING_STATUS, PAYEE_OPTIONS } from '../Order/constant';
  16 +import BankImportModal from './components/BankImportModal';
15 import './index.less'; 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 return ( 298 return (
39 <> 299 <>
40 <PageContainer 300 <PageContainer
@@ -73,100 +333,17 @@ const InvoicePage = () =&gt; { @@ -73,100 +333,17 @@ const InvoicePage = () =&gt; {
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 </PageContainer> 337 </PageContainer>
  338 +
  339 + {bankImportModalVisible ? (
  340 + <BankImportModal
  341 + setVisible={setBankImportModalVisible}
  342 + onClose={() => {}}
  343 + ></BankImportModal>
  344 + ) : (
  345 + ''
  346 + )}
170 </> 347 </>
171 ); 348 );
172 }; 349 };