@fe-free/file 4.0.4 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/i18next.config.ts +11 -0
- package/package.json +10 -5
- package/todo/import_xlsx/import_xlsx.tsx +0 -159
- package/todo/import_xlsx/import_xlsx_file.tsx +0 -89
- package/todo/import_xlsx/index.tsx +0 -2
- package/todo/import_xlsx/step_import.tsx +0 -120
- package/todo/import_xlsx/step_preview.tsx +0 -106
- package/todo/import_xlsx/step_process.tsx +0 -75
- package/todo/import_xlsx/style.scss +0 -12
- package/todo/import_xlsx/types.ts +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from 'i18next-cli';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
locales: ['zh-CN', 'en-US'],
|
|
5
|
+
extract: {
|
|
6
|
+
primaryLanguage: 'zh-CN',
|
|
7
|
+
secondaryLanguages: ['en-US'],
|
|
8
|
+
input: ['./src/**/*.{js,jsx,ts,tsx}', './todo/**/*.{js,jsx,ts,tsx}'],
|
|
9
|
+
output: './src/locales/{{language}}/{{namespace}}.json',
|
|
10
|
+
},
|
|
11
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/file",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -10,17 +10,22 @@
|
|
|
10
10
|
"registry": "https://registry.npmjs.org/"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@ant-design/icons": "^5.4.0",
|
|
14
|
-
"@ant-design/x": "^1.4.0",
|
|
15
13
|
"classnames": "^2.5.1",
|
|
16
14
|
"exceljs": "^4.4.0",
|
|
17
15
|
"file-saver": "^2.0.5",
|
|
18
16
|
"lodash-es": "^4.17.21"
|
|
19
17
|
},
|
|
20
18
|
"peerDependencies": {
|
|
21
|
-
"
|
|
19
|
+
"@ant-design/x": "^1.4.0",
|
|
20
|
+
"dayjs": "~1.11.10",
|
|
21
|
+
"i18next": "^25.7.2",
|
|
22
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
23
|
+
"i18next-icu": "^2.4.1",
|
|
24
|
+
"react": "^19.2.0",
|
|
25
|
+
"react-i18next": "^16.4.0"
|
|
22
26
|
},
|
|
23
27
|
"scripts": {
|
|
24
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
28
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
29
|
+
"i18n-extract": "rm -rf ./src/locales/zh-CN && npx i18next-cli extract"
|
|
25
30
|
}
|
|
26
31
|
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import type { StepsProps, UploadFile } from 'antd';
|
|
2
|
-
import { Button, Modal, Steps } from 'antd';
|
|
3
|
-
import React, { useCallback, useMemo, useState } from 'react';
|
|
4
|
-
import { StepImport } from './step_import';
|
|
5
|
-
import { StepPreview } from './step_preview';
|
|
6
|
-
import { StepProcess } from './step_process';
|
|
7
|
-
import './style.scss';
|
|
8
|
-
import type { ImportXlsxProps, XlsxDataItem } from './types';
|
|
9
|
-
|
|
10
|
-
function ImportXlsx(props: ImportXlsxProps) {
|
|
11
|
-
const { onTemplateDownload, size, onImport, onOk } = props;
|
|
12
|
-
|
|
13
|
-
const [current, setCurrent] = useState(0);
|
|
14
|
-
|
|
15
|
-
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
|
16
|
-
const [xlsxData, setXlsxData] = useState<XlsxDataItem[]>([]);
|
|
17
|
-
|
|
18
|
-
const [sheetIndex, setSheetIndex] = useState(0);
|
|
19
|
-
const [headerIndex, setHeaderIndex] = useState(0);
|
|
20
|
-
const [dataStartIndex, setDataStartIndex] = useState(1);
|
|
21
|
-
|
|
22
|
-
const [status, setStatus] = useState<StepsProps['status']>(undefined);
|
|
23
|
-
|
|
24
|
-
const sheetData = xlsxData[sheetIndex];
|
|
25
|
-
|
|
26
|
-
const uploadData = useMemo(() => {
|
|
27
|
-
if (!sheetData) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return [sheetData.data[headerIndex], ...sheetData.data.slice(dataStartIndex)];
|
|
32
|
-
}, [dataStartIndex, headerIndex, sheetData]);
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="fec-import-xlsx flex h-full flex-col gap-4">
|
|
36
|
-
<Steps
|
|
37
|
-
items={[
|
|
38
|
-
{
|
|
39
|
-
title: '上传',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
title: '预览',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
title: '数据导入',
|
|
46
|
-
status,
|
|
47
|
-
},
|
|
48
|
-
]}
|
|
49
|
-
current={current}
|
|
50
|
-
/>
|
|
51
|
-
<div className="flex-1 overflow-y-auto">
|
|
52
|
-
{current === 0 && (
|
|
53
|
-
<StepImport
|
|
54
|
-
fileList={fileList}
|
|
55
|
-
setFileList={setFileList}
|
|
56
|
-
setXlsxData={setXlsxData}
|
|
57
|
-
onTemplateDownload={onTemplateDownload}
|
|
58
|
-
size={size}
|
|
59
|
-
/>
|
|
60
|
-
)}
|
|
61
|
-
{current === 1 && (
|
|
62
|
-
<StepPreview
|
|
63
|
-
xlsxData={xlsxData}
|
|
64
|
-
sheetIndex={sheetIndex}
|
|
65
|
-
setSheetIndex={setSheetIndex}
|
|
66
|
-
headerIndex={headerIndex}
|
|
67
|
-
setHeaderIndex={setHeaderIndex}
|
|
68
|
-
dataStartIndex={dataStartIndex}
|
|
69
|
-
setDataStartIndex={setDataStartIndex}
|
|
70
|
-
/>
|
|
71
|
-
)}
|
|
72
|
-
{current === 2 && (
|
|
73
|
-
<StepProcess
|
|
74
|
-
fileList={fileList}
|
|
75
|
-
status={status}
|
|
76
|
-
setStatus={setStatus}
|
|
77
|
-
uploadData={uploadData!}
|
|
78
|
-
onImport={onImport}
|
|
79
|
-
/>
|
|
80
|
-
)}
|
|
81
|
-
</div>
|
|
82
|
-
<div className="flex justify-end gap-2">
|
|
83
|
-
{current === 0 && (
|
|
84
|
-
<>
|
|
85
|
-
<Button disabled onClick={() => setCurrent(current - 1)}>
|
|
86
|
-
上一步
|
|
87
|
-
</Button>
|
|
88
|
-
|
|
89
|
-
<Button
|
|
90
|
-
type="primary"
|
|
91
|
-
disabled={!fileList.length}
|
|
92
|
-
onClick={() => setCurrent(current + 1)}
|
|
93
|
-
>
|
|
94
|
-
下一步
|
|
95
|
-
</Button>
|
|
96
|
-
</>
|
|
97
|
-
)}
|
|
98
|
-
{current === 1 && (
|
|
99
|
-
<>
|
|
100
|
-
<Button onClick={() => setCurrent(current - 1)}>上一步</Button>
|
|
101
|
-
|
|
102
|
-
<Button type="primary" onClick={() => setCurrent(current + 1)}>
|
|
103
|
-
下一步
|
|
104
|
-
</Button>
|
|
105
|
-
</>
|
|
106
|
-
)}
|
|
107
|
-
{current === 2 && (
|
|
108
|
-
<>
|
|
109
|
-
<Button onClick={() => setCurrent(current - 1)}>上一步</Button>
|
|
110
|
-
|
|
111
|
-
<Button disabled={status !== 'finish'} type="primary" onClick={onOk}>
|
|
112
|
-
导入
|
|
113
|
-
</Button>
|
|
114
|
-
</>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
interface ModalImportXlsxProps extends Omit<ImportXlsxProps, 'onOk'> {
|
|
122
|
-
children: React.ReactElement;
|
|
123
|
-
}
|
|
124
|
-
function ModalImportXlsx(props: ModalImportXlsxProps) {
|
|
125
|
-
const [open, setOpen] = useState(false);
|
|
126
|
-
const { children, ...rest } = props;
|
|
127
|
-
|
|
128
|
-
const handleOk = useCallback(() => {
|
|
129
|
-
setOpen(false);
|
|
130
|
-
}, []);
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<>
|
|
134
|
-
{open && (
|
|
135
|
-
<Modal title="批量导入" onCancel={() => setOpen(false)} footer={null} open width={850}>
|
|
136
|
-
<div className="h-[450px]">
|
|
137
|
-
<ImportXlsx {...rest} onOk={handleOk} />
|
|
138
|
-
</div>
|
|
139
|
-
</Modal>
|
|
140
|
-
)}
|
|
141
|
-
{React.cloneElement(children, {
|
|
142
|
-
onClick: () => {
|
|
143
|
-
console.log('click');
|
|
144
|
-
setOpen(true);
|
|
145
|
-
},
|
|
146
|
-
})}
|
|
147
|
-
</>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function ImportXlsxButton(props: Omit<ModalImportXlsxProps, 'children'>) {
|
|
152
|
-
return (
|
|
153
|
-
<ModalImportXlsx {...props}>
|
|
154
|
-
<Button>批量导入</Button>
|
|
155
|
-
</ModalImportXlsx>
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export { ImportXlsxButton, ModalImportXlsx };
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { LoadingButton } from '@fe-free/core';
|
|
2
|
-
import type { UploadFile } from 'antd';
|
|
3
|
-
import { Button, Modal } from 'antd';
|
|
4
|
-
import React, { useState } from 'react';
|
|
5
|
-
import { StepImport } from './step_import';
|
|
6
|
-
import './style.scss';
|
|
7
|
-
import type { ImportXlsxFileProps } from './types';
|
|
8
|
-
import { EnumImportType } from './types';
|
|
9
|
-
|
|
10
|
-
function ImportXlsxFile(props: ImportXlsxFileProps) {
|
|
11
|
-
const { onTemplateDownload, size, onCancel, onOk, enableImportType } = props;
|
|
12
|
-
|
|
13
|
-
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
|
14
|
-
|
|
15
|
-
const [importType, setImportType] = useState<EnumImportType>(EnumImportType.FULL);
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<div className="fec-import-xlsx flex h-full flex-col gap-4">
|
|
19
|
-
<div className="flex-1 overflow-y-auto">
|
|
20
|
-
<StepImport
|
|
21
|
-
fileList={fileList}
|
|
22
|
-
setFileList={setFileList}
|
|
23
|
-
onTemplateDownload={onTemplateDownload}
|
|
24
|
-
size={size}
|
|
25
|
-
onImportType={enableImportType ? setImportType : undefined}
|
|
26
|
-
/>
|
|
27
|
-
</div>
|
|
28
|
-
<div className="flex justify-end gap-2">
|
|
29
|
-
<Button onClick={() => onCancel?.()}>取消</Button>
|
|
30
|
-
<LoadingButton
|
|
31
|
-
disabled={!fileList.length}
|
|
32
|
-
type="primary"
|
|
33
|
-
onClick={() => {
|
|
34
|
-
if (enableImportType) {
|
|
35
|
-
onOk({ uploadFile: fileList[0], importType });
|
|
36
|
-
} else {
|
|
37
|
-
onOk({ uploadFile: fileList[0] });
|
|
38
|
-
}
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
导入
|
|
42
|
-
</LoadingButton>
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface ModalImportXlsxFileProps extends ImportXlsxFileProps {
|
|
49
|
-
children: React.ReactElement;
|
|
50
|
-
}
|
|
51
|
-
function ModalImportXlsxFile(props: ModalImportXlsxFileProps) {
|
|
52
|
-
const [open, setOpen] = useState(false);
|
|
53
|
-
const { children, onOk, ...rest } = props;
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<>
|
|
57
|
-
{open && (
|
|
58
|
-
<Modal title="批量导入" onCancel={() => setOpen(false)} footer={null} open>
|
|
59
|
-
<div>
|
|
60
|
-
<ImportXlsxFile
|
|
61
|
-
{...rest}
|
|
62
|
-
onOk={async (data) => {
|
|
63
|
-
await onOk(data);
|
|
64
|
-
setOpen(false);
|
|
65
|
-
}}
|
|
66
|
-
onCancel={() => setOpen(false)}
|
|
67
|
-
/>
|
|
68
|
-
</div>
|
|
69
|
-
</Modal>
|
|
70
|
-
)}
|
|
71
|
-
{React.cloneElement(children, {
|
|
72
|
-
onClick: () => {
|
|
73
|
-
console.log('click');
|
|
74
|
-
setOpen(true);
|
|
75
|
-
},
|
|
76
|
-
})}
|
|
77
|
-
</>
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function ImportXlsxFileButton(props: Omit<ModalImportXlsxFileProps, 'children'>) {
|
|
82
|
-
return (
|
|
83
|
-
<ModalImportXlsxFile {...props}>
|
|
84
|
-
<Button>批量导入</Button>
|
|
85
|
-
</ModalImportXlsxFile>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export { ImportXlsxFileButton, ModalImportXlsxFile };
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { Attachments } from '@ant-design/x';
|
|
2
|
-
import { xlsxToJSON } from '@fe-free/file';
|
|
3
|
-
import { DeleteOutlined, InboxOutlined } from '@fe-free/icons';
|
|
4
|
-
import type { UploadFile } from 'antd';
|
|
5
|
-
import { App, Button, Radio, Upload } from 'antd';
|
|
6
|
-
import classNames from 'classnames';
|
|
7
|
-
import './style.scss';
|
|
8
|
-
import type { ImportXlsxProps, XlsxDataItem } from './types';
|
|
9
|
-
import { EnumImportType } from './types';
|
|
10
|
-
|
|
11
|
-
function ItemRender({ file, right }: { file: UploadFile; right?: React.ReactNode }) {
|
|
12
|
-
return (
|
|
13
|
-
<div className="flex items-center gap-2 rounded-md border border-01">
|
|
14
|
-
<div className="flex-1">
|
|
15
|
-
<Attachments.FileCard key={file.name} item={file} />
|
|
16
|
-
</div>
|
|
17
|
-
{right}
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function StepImport({
|
|
23
|
-
fileList,
|
|
24
|
-
setFileList,
|
|
25
|
-
setXlsxData,
|
|
26
|
-
onTemplateDownload,
|
|
27
|
-
onImportType,
|
|
28
|
-
size,
|
|
29
|
-
}: {
|
|
30
|
-
fileList: UploadFile[];
|
|
31
|
-
setFileList: (fileList: UploadFile[]) => void;
|
|
32
|
-
setXlsxData?: (xlsxData: XlsxDataItem[]) => void;
|
|
33
|
-
onTemplateDownload?: ImportXlsxProps['onTemplateDownload'];
|
|
34
|
-
onImportType?: (importType: EnumImportType) => void;
|
|
35
|
-
size: ImportXlsxProps['size'];
|
|
36
|
-
}) {
|
|
37
|
-
const { message } = App.useApp();
|
|
38
|
-
const mbSize = size && (size / 1024 / 1024).toFixed(1);
|
|
39
|
-
|
|
40
|
-
const handleBeforeUpload = async (file: File) => {
|
|
41
|
-
if (size) {
|
|
42
|
-
if (file.size > size) {
|
|
43
|
-
message.error(`文件大小不能超过${mbSize}MB`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (setXlsxData) {
|
|
48
|
-
const xlsxData = await xlsxToJSON(file);
|
|
49
|
-
setXlsxData(xlsxData);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return false;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<div
|
|
57
|
-
className={classNames('flex flex-col gap-2', 'fec-import-xlsx', {
|
|
58
|
-
'fec-import-xlsx-has-file': fileList.length > 0,
|
|
59
|
-
})}
|
|
60
|
-
>
|
|
61
|
-
{onImportType && (
|
|
62
|
-
<div className="py-4">
|
|
63
|
-
<Radio.Group
|
|
64
|
-
defaultValue={EnumImportType.FULL}
|
|
65
|
-
onChange={(event) => onImportType(event.target.value)}
|
|
66
|
-
>
|
|
67
|
-
<Radio value={EnumImportType.FULL}>全量覆盖</Radio>
|
|
68
|
-
<Radio value={EnumImportType.APPEND}>追加数据</Radio>
|
|
69
|
-
</Radio.Group>
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
<Upload.Dragger
|
|
73
|
-
accept=".xlsx,.xls,.csv"
|
|
74
|
-
multiple={false}
|
|
75
|
-
pastable
|
|
76
|
-
fileList={fileList}
|
|
77
|
-
itemRender={(_, file) => (
|
|
78
|
-
<ItemRender
|
|
79
|
-
file={file}
|
|
80
|
-
right={
|
|
81
|
-
<Button
|
|
82
|
-
icon={<DeleteOutlined />}
|
|
83
|
-
type="text"
|
|
84
|
-
onClick={() => {
|
|
85
|
-
setFileList([]);
|
|
86
|
-
}}
|
|
87
|
-
/>
|
|
88
|
-
}
|
|
89
|
-
/>
|
|
90
|
-
)}
|
|
91
|
-
onChange={(info) => {
|
|
92
|
-
setFileList(info.fileList);
|
|
93
|
-
}}
|
|
94
|
-
beforeUpload={handleBeforeUpload}
|
|
95
|
-
>
|
|
96
|
-
<>
|
|
97
|
-
<p className="ant-upload-drag-icon">
|
|
98
|
-
<InboxOutlined />
|
|
99
|
-
</p>
|
|
100
|
-
<p className="ant-upload-text">点击上传或拖拽文档到这里</p>
|
|
101
|
-
<p className="ant-upload-hint">
|
|
102
|
-
上传一份 Excel 格式或者 CSV 格式的文档{mbSize && `,文件大小限制 ${mbSize}MB 以内`}
|
|
103
|
-
</p>
|
|
104
|
-
</>
|
|
105
|
-
</Upload.Dragger>
|
|
106
|
-
|
|
107
|
-
{onTemplateDownload && (
|
|
108
|
-
<div className="flex items-center gap-1">
|
|
109
|
-
<span>可下载模板,根据指引在模板中编辑</span>
|
|
110
|
-
<Button type="link" className="px-0" onClick={onTemplateDownload}>
|
|
111
|
-
下载模板
|
|
112
|
-
</Button>
|
|
113
|
-
<span>。</span>
|
|
114
|
-
</div>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export { ItemRender, StepImport };
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { Table } from '@fe-free/core';
|
|
2
|
-
import { Select } from 'antd';
|
|
3
|
-
import { isDate, range } from 'lodash-es';
|
|
4
|
-
import type { XlsxDataItem } from './types';
|
|
5
|
-
|
|
6
|
-
function StepPreview({
|
|
7
|
-
xlsxData,
|
|
8
|
-
sheetIndex,
|
|
9
|
-
setSheetIndex,
|
|
10
|
-
headerIndex,
|
|
11
|
-
setHeaderIndex,
|
|
12
|
-
dataStartIndex,
|
|
13
|
-
setDataStartIndex,
|
|
14
|
-
}: {
|
|
15
|
-
xlsxData: XlsxDataItem[];
|
|
16
|
-
sheetIndex: number;
|
|
17
|
-
setSheetIndex: (index: number) => void;
|
|
18
|
-
headerIndex: number;
|
|
19
|
-
setHeaderIndex: (index: number) => void;
|
|
20
|
-
dataStartIndex: number;
|
|
21
|
-
setDataStartIndex: (index: number) => void;
|
|
22
|
-
}) {
|
|
23
|
-
const sheetData = xlsxData[sheetIndex] || [];
|
|
24
|
-
|
|
25
|
-
const sheetOptions = xlsxData.map((item, index) => ({ label: item.name, value: index }));
|
|
26
|
-
const lineOptions = range(sheetData.data.length).map((line) => ({
|
|
27
|
-
label: `第 ${line + 1} 行`,
|
|
28
|
-
value: line,
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
const columns = sheetData.data[headerIndex].map((v, index) => {
|
|
32
|
-
if (isDate(v)) {
|
|
33
|
-
return {
|
|
34
|
-
title: v.toString(),
|
|
35
|
-
dataIndex: index,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
title: v,
|
|
41
|
-
dataIndex: index,
|
|
42
|
-
};
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const dataSource = sheetData.data.slice(dataStartIndex).map((item, line) => {
|
|
46
|
-
const result = {
|
|
47
|
-
id: line,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
item.forEach((v, index) => {
|
|
51
|
-
if (isDate(v)) {
|
|
52
|
-
result[index] = v.toString();
|
|
53
|
-
} else {
|
|
54
|
-
result[index] = v;
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return result;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div className="flex flex-col gap-2">
|
|
63
|
-
<div className="flex gap-5">
|
|
64
|
-
<div className="flex items-center gap-2">
|
|
65
|
-
导入表
|
|
66
|
-
<Select
|
|
67
|
-
options={sheetOptions}
|
|
68
|
-
value={sheetIndex}
|
|
69
|
-
onChange={(index) => {
|
|
70
|
-
setSheetIndex(index);
|
|
71
|
-
setHeaderIndex(0);
|
|
72
|
-
setDataStartIndex(1);
|
|
73
|
-
}}
|
|
74
|
-
className="w-[150px]"
|
|
75
|
-
/>
|
|
76
|
-
</div>
|
|
77
|
-
<div className="flex items-center gap-2">
|
|
78
|
-
表头
|
|
79
|
-
<Select
|
|
80
|
-
options={lineOptions}
|
|
81
|
-
value={headerIndex}
|
|
82
|
-
onChange={(index) => {
|
|
83
|
-
setHeaderIndex(index);
|
|
84
|
-
setDataStartIndex(index + 1);
|
|
85
|
-
}}
|
|
86
|
-
className="w-[150px]"
|
|
87
|
-
/>
|
|
88
|
-
</div>
|
|
89
|
-
<div className="flex items-center gap-2">
|
|
90
|
-
数据起始行
|
|
91
|
-
<Select
|
|
92
|
-
options={lineOptions}
|
|
93
|
-
value={dataStartIndex}
|
|
94
|
-
onChange={(index) => {
|
|
95
|
-
setDataStartIndex(index);
|
|
96
|
-
}}
|
|
97
|
-
className="w-[150px]"
|
|
98
|
-
/>
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
<Table columns={columns} dataSource={dataSource} pagination={false} />
|
|
102
|
-
</div>
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export { StepPreview };
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { LoadingButton } from '@fe-free/core';
|
|
2
|
-
import { CheckOutlined, LoadingOutlined } from '@fe-free/icons';
|
|
3
|
-
import type { StepsProps, UploadFile } from 'antd';
|
|
4
|
-
import { Button } from 'antd';
|
|
5
|
-
import { useEffect } from 'react';
|
|
6
|
-
import { ItemRender } from './step_import';
|
|
7
|
-
import type { ImportXlsxProps } from './types';
|
|
8
|
-
|
|
9
|
-
function StepProcess({
|
|
10
|
-
fileList,
|
|
11
|
-
status,
|
|
12
|
-
setStatus,
|
|
13
|
-
uploadData,
|
|
14
|
-
onImport,
|
|
15
|
-
}: {
|
|
16
|
-
fileList: UploadFile[];
|
|
17
|
-
status: StepsProps['status'];
|
|
18
|
-
setStatus: (status: StepsProps['status']) => void;
|
|
19
|
-
uploadData: any[][];
|
|
20
|
-
onImport: ImportXlsxProps['onImport'];
|
|
21
|
-
}) {
|
|
22
|
-
const handleImport = async () => {
|
|
23
|
-
setStatus('process');
|
|
24
|
-
try {
|
|
25
|
-
const records: Record<string, any>[] = [];
|
|
26
|
-
const fields = uploadData[0];
|
|
27
|
-
uploadData.slice(1).forEach((record) => {
|
|
28
|
-
const recordObj: Record<string, any> = {};
|
|
29
|
-
record.forEach((cell, index) => {
|
|
30
|
-
recordObj[fields[index]] = cell;
|
|
31
|
-
});
|
|
32
|
-
records.push(recordObj);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
await onImport(records);
|
|
36
|
-
setStatus('finish');
|
|
37
|
-
} catch (error) {
|
|
38
|
-
setStatus('error');
|
|
39
|
-
throw error;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
let right;
|
|
44
|
-
if (status === 'process') {
|
|
45
|
-
right = (
|
|
46
|
-
<Button icon={<LoadingOutlined />} type="text">
|
|
47
|
-
导入中
|
|
48
|
-
</Button>
|
|
49
|
-
);
|
|
50
|
-
} else if (status === 'finish') {
|
|
51
|
-
right = (
|
|
52
|
-
<Button icon={<CheckOutlined />} type="text">
|
|
53
|
-
导入完成
|
|
54
|
-
</Button>
|
|
55
|
-
);
|
|
56
|
-
} else if (status === 'error') {
|
|
57
|
-
right = (
|
|
58
|
-
<LoadingButton type="text" danger onClick={() => handleImport()}>
|
|
59
|
-
重新导入
|
|
60
|
-
</LoadingButton>
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
handleImport();
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<div>
|
|
70
|
-
<ItemRender file={fileList[0]} right={right} />
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export { StepProcess };
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { UploadFile } from 'antd';
|
|
2
|
-
|
|
3
|
-
export enum EnumImportType {
|
|
4
|
-
FULL = 'Full',
|
|
5
|
-
APPEND = 'Append',
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
interface XlsxDataItem {
|
|
9
|
-
name: string;
|
|
10
|
-
data: any[][];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface ImportXlsxProps {
|
|
14
|
-
/** 点击 下载模板 */
|
|
15
|
-
onTemplateDownload?: () => void;
|
|
16
|
-
/** 文件大小限制 */
|
|
17
|
-
size?: number;
|
|
18
|
-
/** 导入数据 API */
|
|
19
|
-
onImport: (data: Record<string, any>[]) => Promise<void>;
|
|
20
|
-
/** 最后的确认 */
|
|
21
|
-
onOk: () => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface ImportXlsxFileProps {
|
|
25
|
-
/** 点击 下载模板 */
|
|
26
|
-
onTemplateDownload?: () => void;
|
|
27
|
-
/** 文件大小限制 */
|
|
28
|
-
size?: number;
|
|
29
|
-
/** 导入文件 */
|
|
30
|
-
onOk: (data: { uploadFile: UploadFile; importType?: EnumImportType }) => Promise<void>;
|
|
31
|
-
/** 取消 */
|
|
32
|
-
onCancel?: () => void;
|
|
33
|
-
/** 是否显示导入类型 */
|
|
34
|
-
enableImportType?: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type { ImportXlsxFileProps, ImportXlsxProps, XlsxDataItem };
|