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 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 = () =&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 337 </PageContainer>
  338 +
  339 + {bankImportModalVisible ? (
  340 + <BankImportModal
  341 + setVisible={setBankImportModalVisible}
  342 + onClose={() => {}}
  343 + ></BankImportModal>
  344 + ) : (
  345 + ''
  346 + )}
170 347 </>
171 348 );
172 349 };
... ...