@fe-free/file 1.4.1

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/.babelrc ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "modules": false,
7
+ "useBuiltIns": "usage",
8
+ "corejs": 3
9
+ }
10
+ ],
11
+ "@babel/preset-react",
12
+ "@babel/preset-typescript"
13
+ ],
14
+ "plugins": [
15
+ "@babel/plugin-proposal-export-default-from",
16
+ "@babel/plugin-transform-runtime"
17
+ ]
18
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,262 @@
1
+ # @ones-wb/file
2
+
3
+ ## 1.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - feat: file
8
+
9
+ ## 1.14.13
10
+
11
+ ### Patch Changes
12
+
13
+ - @ones-wb/tool@1.14.13
14
+
15
+ ## 1.14.12
16
+
17
+ ### Patch Changes
18
+
19
+ - @ones-wb/tool@1.14.12
20
+
21
+ ## 1.14.11
22
+
23
+ ### Patch Changes
24
+
25
+ - @ones-wb/tool@1.14.11
26
+
27
+ ## 1.14.10
28
+
29
+ ### Patch Changes
30
+
31
+ - @ones-wb/tool@1.14.10
32
+
33
+ ## 1.14.9
34
+
35
+ ### Patch Changes
36
+
37
+ - @ones-wb/tool@1.14.9
38
+
39
+ ## 1.14.8
40
+
41
+ ### Patch Changes
42
+
43
+ - f74c458: chore: 依赖梳理
44
+ - Updated dependencies [f74c458]
45
+ - @ones-wb/tool@1.14.8
46
+
47
+ ## 1.14.7
48
+
49
+ ### Patch Changes
50
+
51
+ - @ones-wb/tool@1.14.7
52
+
53
+ ## 1.14.6
54
+
55
+ ### Patch Changes
56
+
57
+ - @ones-wb/tool@1.14.6
58
+
59
+ ## 1.14.5
60
+
61
+ ### Patch Changes
62
+
63
+ - @ones-wb/tool@1.14.5
64
+
65
+ ## 1.14.4
66
+
67
+ ### Patch Changes
68
+
69
+ - @ones-wb/tool@1.14.4
70
+
71
+ ## 1.14.3
72
+
73
+ ### Patch Changes
74
+
75
+ - @ones-wb/tool@1.14.3
76
+
77
+ ## 1.14.2
78
+
79
+ ### Patch Changes
80
+
81
+ - @ones-wb/tool@1.14.2
82
+
83
+ ## 1.14.1
84
+
85
+ ### Patch Changes
86
+
87
+ - @ones-wb/tool@1.14.1
88
+
89
+ ## 1.14.0
90
+
91
+ ### Minor Changes
92
+
93
+ - feat: ids
94
+
95
+ ### Patch Changes
96
+
97
+ - Updated dependencies
98
+ - @ones-wb/tool@1.14.0
99
+
100
+ ## 1.13.0
101
+
102
+ ### Patch Changes
103
+
104
+ - @ones-wb/tool@1.13.0
105
+
106
+ ## 1.12.0
107
+
108
+ ### Patch Changes
109
+
110
+ - @ones-wb/tool@1.12.0
111
+
112
+ ## 1.11.0
113
+
114
+ ### Patch Changes
115
+
116
+ - @ones-wb/tool@1.11.0
117
+
118
+ ## 1.10.3
119
+
120
+ ### Patch Changes
121
+
122
+ - @ones-wb/tool@1.10.3
123
+
124
+ ## 1.10.2
125
+
126
+ ### Patch Changes
127
+
128
+ - @ones-wb/tool@1.10.2
129
+
130
+ ## 1.10.1
131
+
132
+ ### Patch Changes
133
+
134
+ - 8ca1be5: fix: xlsxToJSONAndValidate 调整返回值
135
+ - @ones-wb/tool@1.10.1
136
+
137
+ ## 1.10.0
138
+
139
+ ### Minor Changes
140
+
141
+ - 85bab00: fix: xlsx 用法变更
142
+
143
+ ### Patch Changes
144
+
145
+ - @ones-wb/tool@1.10.0
146
+
147
+ ## 1.9.0
148
+
149
+ ### Patch Changes
150
+
151
+ - Updated dependencies [7cf6c1e]
152
+ - @ones-wb/tool@1.9.0
153
+
154
+ ## 1.8.3
155
+
156
+ ### Patch Changes
157
+
158
+ - @ones-wb/tool@1.8.3
159
+
160
+ ## 1.8.2
161
+
162
+ ### Patch Changes
163
+
164
+ - @ones-wb/tool@1.8.2
165
+
166
+ ## 1.8.1
167
+
168
+ ### Patch Changes
169
+
170
+ - @ones-wb/tool@1.8.1
171
+
172
+ ## 1.8.0
173
+
174
+ ### Patch Changes
175
+
176
+ - Updated dependencies [9c69791]
177
+ - @ones-wb/tool@1.8.0
178
+
179
+ ## 1.7.2
180
+
181
+ ### Patch Changes
182
+
183
+ - @ones-wb/tool@1.7.2
184
+
185
+ ## 1.7.1
186
+
187
+ ### Patch Changes
188
+
189
+ - ec92357: fix: xlsx 导入文件解析,忽略空行
190
+ - @ones-wb/tool@1.7.1
191
+
192
+ ## 1.7.0
193
+
194
+ ### Patch Changes
195
+
196
+ - Updated dependencies [bab0a97]
197
+ - @ones-wb/tool@1.7.0
198
+
199
+ ## 1.6.0
200
+
201
+ ### Patch Changes
202
+
203
+ - @ones-wb/tool@1.6.0
204
+
205
+ ## 1.5.0
206
+
207
+ ### Patch Changes
208
+
209
+ - Updated dependencies [a5191b1]
210
+ - @ones-wb/tool@1.5.0
211
+
212
+ ## 1.4.1
213
+
214
+ ### Patch Changes
215
+
216
+ - @ones-wb/tool@1.4.1
217
+
218
+ ## 1.4.0
219
+
220
+ ### Patch Changes
221
+
222
+ - @ones-wb/tool@1.4.0
223
+
224
+ ## 1.3.3
225
+
226
+ ### Patch Changes
227
+
228
+ - @ones-wb/tool@1.3.3
229
+
230
+ ## 1.3.2
231
+
232
+ ### Patch Changes
233
+
234
+ - Updated dependencies [f71184e]
235
+ - @ones-wb/tool@1.3.2
236
+
237
+ ## 1.3.1
238
+
239
+ ### Patch Changes
240
+
241
+ - feat: dayjs => baseTime
242
+ - Updated dependencies
243
+ - @ones-wb/tool@1.3.1
244
+
245
+ ## 1.3.0
246
+
247
+ ### Minor Changes
248
+
249
+ - 1.3.0
250
+
251
+ ### Patch Changes
252
+
253
+ - Updated dependencies
254
+ - @ones-wb/tool@1.3.0
255
+
256
+ ## 1.2.1
257
+
258
+ ## 1.2.0
259
+
260
+ ### Minor Changes
261
+
262
+ - feat: add @one-wb/file. 增加前端处理 xlsx 库.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @ones-wb/file
2
+
3
+ @ones-wb/file 文件处理相关放这里,文件处理逻辑一般都比较重。
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@fe-free/file",
3
+ "version": "1.4.1",
4
+ "description": "",
5
+ "main": "./src/index.ts",
6
+ "author": "",
7
+ "license": "ISC",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
12
+ "dependencies": {
13
+ "exceljs": "^4.4.0",
14
+ "file-saver": "^2.0.5",
15
+ "lodash-es": "^4.17.21"
16
+ },
17
+ "peerDependencies": {
18
+ "dayjs": "~1.11.10"
19
+ },
20
+ "scripts": {
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ }
23
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { JSONToXlsx, xlsxToJSON, xlsxToJSONAndValidate } from './xlsx';
@@ -0,0 +1,154 @@
1
+ import type { Cell, Workbook, Worksheet } from 'exceljs';
2
+ import ExcelJS from 'exceljs';
3
+ import FileSaver from 'file-saver';
4
+ import { isNumber } from 'lodash-es';
5
+
6
+ type JSONSheetItem = {
7
+ name: string;
8
+ data: any[][];
9
+ };
10
+
11
+ function numberToDate(excelDate: number) {
12
+ if (!isNumber(excelDate)) {
13
+ return excelDate;
14
+ }
15
+ // let utc_days = Math.floor(excelDate - (25567 + 2))
16
+ // Excel 的 bug, 会把 1900 年当作闰年
17
+ // https://docs.microsoft.com/en-us/office/troubleshoot/excel/wrongly-assumes-1900-is-leap-year
18
+ // 东半球 & 0 时区 => 25567 + 2
19
+ // 西半球 => 25567 + 1
20
+ const step = new Date().getTimezoneOffset() <= 0 ? 25567 + 2 : 25567 + 1;
21
+ const utc_days = Math.floor(excelDate - step);
22
+ // 86400 => 24 * 60 * 60 => 一天的总秒数
23
+ const utc_value = utc_days * 86400;
24
+ // 一天的总毫秒数
25
+ const date_info = new Date(utc_value * 1000);
26
+
27
+ // 误差处理
28
+ const fractional_day = excelDate - Math.floor(excelDate) + 0.0000001;
29
+ // 自 1970 年至今的总秒数
30
+ let total_seconds = Math.floor(86400 * fractional_day);
31
+ const seconds = total_seconds % 60;
32
+ total_seconds -= seconds;
33
+ const hours = Math.floor(total_seconds / (60 * 60));
34
+ const minutes = Math.floor(total_seconds / 60) % 60;
35
+
36
+ return new Date(
37
+ date_info.getFullYear(),
38
+ date_info.getMonth(),
39
+ date_info.getDate(),
40
+ hours,
41
+ minutes,
42
+ seconds,
43
+ );
44
+ }
45
+
46
+ // 1 worksheet.eachRow 本身会跳过空行
47
+ // 2 row.eachCell 会跳过空列, includeEmpty: 试了也没用,不能使用,具体可看源码。
48
+ // 尝试 maxColIndex 解决
49
+ function eachRowAndCell(worksheet: Worksheet, callback) {
50
+ let maxColIndex = 0;
51
+
52
+ worksheet.eachRow(function (row, rowNumber) {
53
+ // @ts-ignore
54
+ maxColIndex = Math.max(maxColIndex, row._cells.length as number);
55
+
56
+ for (let colIndex = 0; colIndex < maxColIndex; colIndex++) {
57
+ const cell = row.getCell(colIndex + 1);
58
+ const colNumber = colIndex + 1;
59
+
60
+ callback({ cell, rowNumber, colNumber });
61
+ }
62
+ });
63
+ }
64
+
65
+ function worksheetToJSON(
66
+ worksheet: Worksheet,
67
+ processCell?: (info: { cell: Cell; colNumber: number; rowNumber: number }) => void,
68
+ ) {
69
+ const sheet: any[][] = [];
70
+
71
+ eachRowAndCell(worksheet, ({ cell, colNumber, rowNumber }) => {
72
+ if (processCell) {
73
+ processCell({ cell, colNumber, rowNumber });
74
+ }
75
+ sheet[rowNumber - 1] = sheet[rowNumber - 1] || [];
76
+ sheet[rowNumber - 1][colNumber - 1] = cell.value;
77
+ });
78
+
79
+ // 挪掉空行。 空行会显示 null
80
+ return sheet.filter(Boolean);
81
+ }
82
+
83
+ interface JSONToWorksheetOption {
84
+ processCell?: (info: { value: any; colNumber: number; rowNumber: number }) => object;
85
+ }
86
+
87
+ function JSONToWorksheet(json: any[][], worksheet: Worksheet, options?: JSONToWorksheetOption) {
88
+ const { processCell = (info) => ({ value: info.value }) } = options || {};
89
+
90
+ let maxColIndex = 0;
91
+ // 不能使用 Array.forEach,会忽略数组的 empty [,1,,]。 行不会 empty
92
+ for (let rowIndex = 0; rowIndex < json.length; rowIndex++) {
93
+ const row = json[rowIndex];
94
+ // 列需要取最大的,可能存在一系列因为 empty 导致 length 小。
95
+ maxColIndex = Math.max(maxColIndex, row.length);
96
+ for (let colIndex = 0; colIndex < maxColIndex; colIndex++) {
97
+ const value = row[colIndex];
98
+ const nCell = processCell({
99
+ value,
100
+ colNumber: colIndex + 1,
101
+ rowNumber: rowIndex + 1,
102
+ });
103
+
104
+ Object.assign(worksheet.getCell(rowIndex + 1, colIndex + 1), nCell);
105
+ }
106
+ }
107
+ }
108
+
109
+ // 复制 value note font
110
+ async function copyWorkbook(workbook: Workbook): Promise<Workbook> {
111
+ const newWorkbook = new ExcelJS.Workbook() as Workbook;
112
+
113
+ workbook.eachSheet((worksheet) => {
114
+ const newWorksheet = newWorkbook.addWorksheet(worksheet.name);
115
+
116
+ eachRowAndCell(worksheet, ({ cell, colNumber, rowNumber }) => {
117
+ newWorksheet.getCell(rowNumber, colNumber).value = cell.value;
118
+
119
+ if (cell.note) {
120
+ newWorksheet.getCell(rowNumber, colNumber).note = cell.note;
121
+ newWorksheet.getCell(rowNumber, colNumber).font = {
122
+ color: {
123
+ argb: 'FFFF0000',
124
+ },
125
+ };
126
+ }
127
+ });
128
+ });
129
+
130
+ return newWorkbook;
131
+ }
132
+
133
+ function readFile(file: File): Promise<Buffer> {
134
+ return new Promise((resolve) => {
135
+ const reader = new FileReader();
136
+
137
+ reader.onload = (data) => {
138
+ resolve(data.target?.result as Buffer);
139
+ };
140
+ reader.readAsArrayBuffer(file);
141
+ });
142
+ }
143
+
144
+ async function exportFile(workbook: Workbook, fileName?: string) {
145
+ const buffer = await workbook.xlsx.writeBuffer();
146
+ const blob = new window.Blob([buffer], {
147
+ type: 'application/octet-stream',
148
+ });
149
+ // fileName 中不能包含特殊字符
150
+ FileSaver.saveAs(blob, fileName ? fileName.replace(/[<>\\:;?/*|]/g, '-') : 'data.xlsx');
151
+ }
152
+
153
+ export { copyWorkbook, exportFile, JSONToWorksheet, numberToDate, readFile, worksheetToJSON };
154
+ export type { JSONSheetItem };
@@ -0,0 +1,3 @@
1
+ export { JSONToXlsx } from './json_to_xlsx';
2
+ export { xlsxToJSON } from './xlsx_to_json';
3
+ export { xlsxToJSONAndValidate } from './xlsx_to_json_and_validate';
@@ -0,0 +1,33 @@
1
+ import ExcelJS from 'exceljs';
2
+ import type { JSONSheetItem } from './helper';
3
+ import { exportFile, JSONToWorksheet } from './helper';
4
+
5
+ interface JSONToXlsxOptions {
6
+ /** 默认 data.xlsx */
7
+ fileName?: string;
8
+ /** 用法参考 exceljs。可做比如 标红、备注等 */
9
+ processCell?: (info: { value: any; colNumber: number; rowNumber: number }) => {
10
+ value: any;
11
+ font?: object;
12
+ note?: string;
13
+ };
14
+ }
15
+
16
+ /** xlsxJSON 转 xlsx,其中日期格式按字符串输出 */
17
+ async function JSONToXlsx(xlsxJSON: JSONSheetItem[], options?: JSONToXlsxOptions): Promise<void> {
18
+ const { fileName = 'data.xlsx', processCell } = options || {};
19
+ const workbook = new ExcelJS.Workbook();
20
+
21
+ // json 写入 workbook
22
+ xlsxJSON.forEach((sheet) => {
23
+ const worksheet = workbook.addWorksheet(sheet.name);
24
+ JSONToWorksheet(sheet.data, worksheet, {
25
+ processCell,
26
+ });
27
+ });
28
+
29
+ // export
30
+ await exportFile(workbook, fileName);
31
+ }
32
+
33
+ export { JSONToXlsx };
@@ -0,0 +1,205 @@
1
+ import { JSONToXlsx, xlsxToJSON, xlsxToJSONAndValidate } from '@fe-free/file';
2
+ import dayjs from 'dayjs';
3
+ import { isDate, isNumber, isString } from 'lodash-es';
4
+ import React from 'react';
5
+
6
+ export default {
7
+ title: '@fe-free/file/xlsx',
8
+ };
9
+
10
+ export const XlsxToJSON = () => (
11
+ <>
12
+ <div>
13
+ 导入 xlsx
14
+ <br />
15
+ <input
16
+ type="file"
17
+ onChange={(e) => {
18
+ const file = e.target.files?.[0];
19
+ if (file) {
20
+ xlsxToJSON(file).then((json) => {
21
+ const element = document.querySelector('.data1');
22
+ if (element) {
23
+ element.innerHTML = JSON.stringify(json, null, 2);
24
+ }
25
+ });
26
+ }
27
+ }}
28
+ />
29
+ </div>
30
+ <pre className="data1" />
31
+ </>
32
+ );
33
+
34
+ export const JSONToXlsxExample = () => (
35
+ <>
36
+ <pre>
37
+ 数据源:
38
+ {JSON.stringify(
39
+ [
40
+ {
41
+ name: 'sheet1',
42
+ data: [
43
+ ['日期', '必填项'],
44
+ ['2025/1/1', '是'],
45
+ ['2025/12/31', '否'],
46
+ ],
47
+ },
48
+ ],
49
+ null,
50
+ 2,
51
+ )}
52
+ </pre>
53
+ <div>
54
+ <button
55
+ onClick={() => {
56
+ JSONToXlsx([
57
+ {
58
+ name: 'sheet1',
59
+ data: [
60
+ ['日期', '必填项'],
61
+ ['2025/1/1', '是'],
62
+ ['2025/12/31', '否'],
63
+ ],
64
+ },
65
+ ]);
66
+ }}
67
+ >
68
+ export
69
+ </button>
70
+ </div>
71
+ <div>
72
+ <button
73
+ onClick={() => {
74
+ JSONToXlsx(
75
+ [
76
+ {
77
+ name: 'sheet1',
78
+ data: [
79
+ ['日期', '必填项'],
80
+ ['2025/1/1', '是'],
81
+ ['2024/12/31', '否'],
82
+ ],
83
+ },
84
+ ],
85
+ {
86
+ processCell: ({ rowNumber, colNumber, value }) => {
87
+ if (colNumber === 1 && rowNumber > 1) {
88
+ const isValid = dayjs(value).year() < dayjs().year();
89
+ if (isValid) {
90
+ return {
91
+ value,
92
+ font: {
93
+ color: {
94
+ argb: 'FFFF0000',
95
+ },
96
+ },
97
+ note: '日期需要当前年份',
98
+ };
99
+ }
100
+ }
101
+ return { value };
102
+ },
103
+ },
104
+ );
105
+ }}
106
+ >
107
+ export(标红&备注)
108
+ </button>
109
+ </div>
110
+ </>
111
+ );
112
+
113
+ export const XlsxToJSONAndValidate = () => {
114
+ const [validateResult, setValidateResult] = React.useState<any>(null);
115
+
116
+ const validate = ({ cell, colNumber }: { cell: any; colNumber: number }) => {
117
+ if (
118
+ isString(cell.value) &&
119
+ (cell.value?.startsWith('日期') || cell.value?.startsWith('必填项'))
120
+ ) {
121
+ return;
122
+ }
123
+
124
+ let message = '';
125
+
126
+ // 日期列
127
+ if (colNumber === 1) {
128
+ // 空值
129
+ if (!cell.value) {
130
+ message = '错误';
131
+ } else {
132
+ // 如果是数字
133
+ if (isNumber(cell.value)) {
134
+ message = '格式错误';
135
+ }
136
+ // 如果是日期类型
137
+ if (isDate(cell.value)) {
138
+ const d = dayjs(cell.value);
139
+ // 非法
140
+ if (!d.isValid()) {
141
+ message = '格式错误';
142
+ }
143
+ // 超出范围
144
+ if (Math.abs(d.year() - dayjs().year()) > 1) {
145
+ message = '范围错误';
146
+ }
147
+ }
148
+ // 如果是字符串
149
+ else if (isString(cell.value)) {
150
+ const reg = /^\d{4}([./-])\d{1,2}([./-])\d{1,2}$/g;
151
+
152
+ // 不合法
153
+ if (!reg.test(cell.value)) {
154
+ message = '格式错误';
155
+ }
156
+
157
+ // 超出范围
158
+ const d = dayjs(cell.value);
159
+ if (Math.abs(d.year() - dayjs().year()) > 1) {
160
+ message = '范围错误';
161
+ }
162
+ }
163
+ }
164
+ }
165
+ // 类型列
166
+ else if (colNumber === 2) {
167
+ // 空值
168
+ if (!cell.value) {
169
+ message = '不能为空';
170
+ } else {
171
+ const isValid = ['是', '否'].includes(cell.value);
172
+ if (!isValid) {
173
+ message = '格式错误';
174
+ }
175
+ }
176
+ }
177
+
178
+ if (message) {
179
+ return { message };
180
+ }
181
+ };
182
+
183
+ return (
184
+ <>
185
+ <div>
186
+ 导入 xlsx 和 校验(假如第一列为日期限制当前年份,第二列为[是、否])
187
+ <input
188
+ type="file"
189
+ onChange={(e) => {
190
+ const file = e.target.files?.[0];
191
+ if (file) {
192
+ xlsxToJSONAndValidate(file, validate).then((res) => {
193
+ setValidateResult(res);
194
+ });
195
+ }
196
+ }}
197
+ />
198
+ </div>
199
+ <pre>{JSON.stringify(validateResult, null, 2)}</pre>
200
+ <button onClick={() => validateResult?.exportXlsx()} disabled={!validateResult}>
201
+ export with validate
202
+ </button>
203
+ </>
204
+ );
205
+ };
@@ -0,0 +1,30 @@
1
+ import type { Workbook } from 'exceljs';
2
+ import ExcelJS from 'exceljs';
3
+ import type { JSONSheetItem } from './helper';
4
+ import { readFile, worksheetToJSON } from './helper';
5
+
6
+ /**
7
+ * xlsx 转 json [ {name: 'sheet1', data: [ [], [] ]} ]
8
+ */
9
+ async function xlsxToJSON(file: File): Promise<JSONSheetItem[]> {
10
+ const workbook = new ExcelJS.Workbook() as Workbook;
11
+
12
+ // 读 file
13
+ const buffer = await readFile(file);
14
+ await workbook.xlsx.load(buffer);
15
+
16
+ // 转换成 json
17
+ const xlsxJSON: JSONSheetItem[] = [];
18
+ workbook.eachSheet((worksheet) => {
19
+ const data = worksheetToJSON(worksheet);
20
+
21
+ xlsxJSON.push({
22
+ name: worksheet.name,
23
+ data,
24
+ });
25
+ });
26
+
27
+ return xlsxJSON;
28
+ }
29
+
30
+ export { xlsxToJSON };
@@ -0,0 +1,88 @@
1
+ import type { Cell, Workbook } from 'exceljs';
2
+ import ExcelJS from 'exceljs';
3
+ import type { JSONSheetItem } from './helper';
4
+ import { copyWorkbook, exportFile, readFile, worksheetToJSON } from './helper';
5
+
6
+ // 如果错误这返回错误信息,这将会修改 xlsx,且标红和批注.
7
+ // col row 从 1 开始
8
+ type xlsxToJSONAndValidateValidate = (info: {
9
+ cell: Cell;
10
+ colNumber: number;
11
+ rowNumber: number;
12
+ }) => undefined | { message };
13
+
14
+ interface xlsxToJSONAndValidateOptions {
15
+ fileName?: string;
16
+ }
17
+
18
+ interface xlsxToJSONAndValidateResult {
19
+ data: JSONSheetItem[];
20
+ // 每个 sheet 是否有错误
21
+ hasErrors: boolean[];
22
+ // 第一个 sheet 是否有错误
23
+ firstError: boolean;
24
+ exportXlsx: () => Promise<void>;
25
+ }
26
+
27
+ async function xlsxToJSONAndValidate(
28
+ file: File,
29
+ validate: xlsxToJSONAndValidateValidate,
30
+ options?: xlsxToJSONAndValidateOptions,
31
+ ): Promise<xlsxToJSONAndValidateResult> {
32
+ const { fileName = 'data.xlsx' } = options || {};
33
+
34
+ const workbook = new ExcelJS.Workbook() as Workbook;
35
+
36
+ // 读 file
37
+ const buffer = await readFile(file);
38
+ await workbook.xlsx.load(buffer);
39
+
40
+ // 错误处理
41
+ const hasErrors: boolean[] = [];
42
+ function doValidate(info, sheetIndex) {
43
+ const validateResult = validate(info);
44
+ if (validateResult) {
45
+ Object.assign(info.cell, {
46
+ note: validateResult.message,
47
+ font: {
48
+ color: {
49
+ argb: 'FFFF0000',
50
+ },
51
+ },
52
+ });
53
+
54
+ hasErrors[sheetIndex] = true;
55
+ }
56
+ }
57
+
58
+ let sheetIndex = 0;
59
+ // 转换成 json
60
+ const xlsxJSON: JSONSheetItem[] = [];
61
+ workbook.eachSheet((worksheet) => {
62
+ const data = worksheetToJSON(worksheet, (info) => {
63
+ doValidate(info, sheetIndex);
64
+ });
65
+
66
+ xlsxJSON.push({
67
+ name: worksheet.name,
68
+ data,
69
+ });
70
+
71
+ sheetIndex += 1;
72
+ });
73
+
74
+ // 导出错误的 xlsx
75
+ async function exportXlsx() {
76
+ const newWorkbook = await copyWorkbook(workbook);
77
+ await exportFile(newWorkbook, fileName);
78
+ }
79
+
80
+ return {
81
+ data: xlsxJSON,
82
+ hasErrors,
83
+ firstError: !!hasErrors[0],
84
+ exportXlsx,
85
+ };
86
+ }
87
+
88
+ export { xlsxToJSONAndValidate };