@skyfox2000/webui 1.3.5 → 1.3.7

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.
Files changed (46) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/lib/assets/modules/{file-upload-C0twqMV5.js → file-upload-CKliLHBF.js} +50 -50
  3. package/lib/assets/modules/{index-D1XAa1Uo.js → index-CTOX9glc.js} +2 -2
  4. package/lib/assets/modules/index-Cm_1mhHh.js +377 -0
  5. package/lib/assets/modules/{index-C4CryM-R.js → index-DTFrhJdN.js} +1 -1
  6. package/lib/assets/modules/{menuTabs-BrYQa4UO.js → menuTabs-ylnnzjNu.js} +2 -2
  7. package/lib/assets/modules/{toolIcon-B-g9pyE4.js → toolIcon-CNfvNkNh.js} +1 -1
  8. package/lib/assets/modules/{uploadList-DCWRIxPJ.js → uploadList-DroJGtBI.js} +4 -4
  9. package/lib/assets/modules/{uploadList-0f2FA_5s.js → uploadList-M21hxVQy.js} +129 -128
  10. package/lib/components/common/alert/index.vue.d.ts +13 -0
  11. package/lib/components/common/icon/helper.vue.d.ts +1 -0
  12. package/lib/components/common/index.d.ts +2 -0
  13. package/lib/components/content/form/formItem.vue.d.ts +1 -0
  14. package/lib/components/form/input/index.vue.d.ts +4 -1
  15. package/lib/components/form/select/index.vue.d.ts +2 -0
  16. package/lib/components/form/treeSelect/index.vue.d.ts +11 -2
  17. package/lib/components/index.d.ts +1 -1
  18. package/lib/es/AceEditor/index.js +3 -3
  19. package/lib/es/BasicLayout/index.js +3 -3
  20. package/lib/es/Error403/index.js +1 -1
  21. package/lib/es/Error404/index.js +1 -1
  22. package/lib/es/ExcelForm/index.js +309 -275
  23. package/lib/es/UploadForm/index.js +4 -4
  24. package/lib/index.d.ts +1 -1
  25. package/lib/typings/form.d.ts +2 -2
  26. package/lib/utils/excel-preview.d.ts +24 -0
  27. package/lib/utils/form-excel.d.ts +17 -4
  28. package/lib/webui.css +1 -1
  29. package/lib/webui.es.js +677 -662
  30. package/package.json +2 -2
  31. package/src/components/common/alert/index.vue +76 -0
  32. package/src/components/common/icon/helper.vue +7 -1
  33. package/src/components/common/index.ts +4 -1
  34. package/src/components/content/dialog/excelForm.vue +337 -311
  35. package/src/components/content/form/formItem.vue +6 -2
  36. package/src/components/form/input/index.vue +16 -3
  37. package/src/components/form/select/index.vue +5 -11
  38. package/src/components/form/treeSelect/index.vue +22 -17
  39. package/src/components/index.ts +1 -0
  40. package/src/index.ts +1 -0
  41. package/src/typings/form.d.ts +2 -2
  42. package/src/utils/data.ts +10 -1
  43. package/src/utils/excel-preview.ts +189 -0
  44. package/src/utils/file-upload.ts +0 -2
  45. package/src/utils/form-excel.ts +132 -126
  46. package/lib/assets/modules/index-CKJIxasX.js +0 -333
@@ -7,7 +7,7 @@ import message from 'vue-m-message';
7
7
  import { ValidateRule } from '@/typings/form';
8
8
  import { validMessages } from './form-validate';
9
9
  import { UploadFile } from '@/typings/upload';
10
- import { toExcel, type ExcelMarkInfo } from './excel-view';
10
+ import { toExcel, type ExcelMarkInfo, excelToNormalized } from './excel-view';
11
11
 
12
12
  // ExcelMarkCell 和 ExcelMarkInfo 类型已移至 excel-view.ts 统一管理
13
13
 
@@ -17,105 +17,31 @@ import { toExcel, type ExcelMarkInfo } from './excel-view';
17
17
  * @returns 包含工作簿、工作表、表头和数据的对象
18
18
  */
19
19
  export const processExcelFile = async (excelBuffer: ArrayBuffer) => {
20
- // 动态导入 exceljs
21
- const ExcelJS = await import('exceljs');
22
- const workbook = new ExcelJS.default.Workbook();
23
- await workbook.xlsx.load(excelBuffer);
24
-
25
- // 获取第一个工作表
26
- const worksheet = workbook.worksheets[0];
27
- if (!worksheet) {
20
+ // 使用excel-view.ts中的统一方法
21
+ const normalized = await excelToNormalized(excelBuffer);
22
+
23
+ if (normalized.headers.length === 0) {
28
24
  message.error('Excel文件不包含工作表');
29
25
  return null;
30
26
  }
31
27
 
32
- // 提取表头和数据
33
- const headers: string[] = [];
34
- // 表头对象数据行
28
+ // 转换为原有格式以保持兼容性
35
29
  const excelData: Record<string, any>[] = [];
36
- // 纯数据行
37
- const excelRows: any[][] = [];
38
-
39
- // 提取表头(第一行)
40
- worksheet.getRow(1).eachCell((cell) => {
41
- // 处理不同类型的单元格值,确保对象类型值被正确转换为字符串
42
- let headerValue = '';
43
- if (cell.value !== null && cell.value !== undefined) {
44
- // 处理不同类型的单元格值
45
- if (typeof cell.value === 'object') {
46
- // 处理RichText类型
47
- if ('richText' in cell.value && Array.isArray(cell.value.richText)) {
48
- headerValue = cell.value.richText.map((rt: any) => rt.text || '').join('');
49
- }
50
- // 处理带有text属性的对象
51
- else if ('text' in cell.value && typeof cell.value.text === 'string') {
52
- headerValue = cell.value.text;
53
- }
54
- // 处理日期类型
55
- else if (cell.value instanceof Date) {
56
- headerValue = cell.value.toLocaleDateString();
57
- }
58
- // 其他对象类型,尝试获取有意义的字符串表示
59
- else {
60
- try {
61
- headerValue = JSON.stringify(cell.value);
62
- } catch {
63
- headerValue = String(cell.value);
64
- }
65
- }
66
- } else {
67
- // 非对象类型直接转字符串
68
- headerValue = String(cell.value);
30
+ normalized.rows.forEach((row) => {
31
+ const rowData: Record<string, any> = {};
32
+ normalized.headers.forEach((header, idx) => {
33
+ if (header) {
34
+ rowData[header] = row[idx];
69
35
  }
70
- }
71
- headers.push(headerValue);
72
- });
73
-
74
- // 提取数据行
75
- worksheet.eachRow((row, rowNumber) => {
76
- if (rowNumber > 1) {
77
- // 跳过表头
78
- const rowData: Record<string, any> = {};
79
- const rowDataArray: any[] = [];
80
- headers.forEach((header, idx) => {
81
- if (header) {
82
- const cell = row.getCell(idx + 1);
83
- const cellValue = cell.value;
84
-
85
- // 使用与表头处理相同的逻辑来提取单元格值
86
- if (cellValue !== null && cellValue !== undefined) {
87
- if (typeof cellValue === 'object') {
88
- // 处理RichText类型
89
- if ('richText' in cellValue && Array.isArray(cellValue.richText)) {
90
- rowData[header] = cellValue.richText.map((rt: any) => rt.text || '').join('');
91
- }
92
- // 处理带有text属性的对象
93
- else if ('text' in cellValue && typeof cellValue.text === 'string') {
94
- rowData[header] = cellValue.text;
95
- }
96
- // 日期类型保留原样,便于验证处理
97
- else if (cellValue instanceof Date) {
98
- rowData[header] = cellValue;
99
- }
100
- // 其他对象类型
101
- else {
102
- rowData[header] = cellValue;
103
- }
104
- } else {
105
- rowData[header] = cellValue;
106
- }
107
- } else {
108
- rowData[header] = null;
109
- }
110
- rowDataArray.push(rowData[header]);
111
- }
112
- });
113
- excelData.push(rowData);
114
- excelRows.push(rowDataArray);
115
- }
36
+ });
37
+ excelData.push(rowData);
116
38
  });
117
39
 
118
- return { workbook, worksheet, headers, excelData, excelRows };
40
+ return {
41
+ headers: normalized.headers,
42
+ excelData,
43
+ excelRows: normalized.rows,
44
+ };
119
45
  };
120
46
 
121
47
  /**
@@ -170,7 +96,8 @@ export const validateExcel = async (
170
96
  rules?: Record<string, ValidateRule>,
171
97
  ): Promise<{
172
98
  hasError: boolean;
173
- errBlob?: Blob;
99
+ markCells?: Array<{ row: number; col: number; color: string }>;
100
+ markHeaders?: string[];
174
101
  }> => {
175
102
  if (!rules || isEmpty(rules)) {
176
103
  return { hasError: false };
@@ -203,7 +130,7 @@ export const validateExcel = async (
203
130
  // 验证数据(仅验证非缺失字段)
204
131
  const validationErrors = await validateExcelData(headers, excelJson, validator);
205
132
 
206
- // 如果有验证错误或缺失字段,标记错误单元格和表头
133
+ // 如果有验证错误或缺失字段,返回标记信息
207
134
  if (validationErrors.length > 0 || missingFields.length > 0) {
208
135
  // 准备需要标记的单元格
209
136
  const markCells = validationErrors.map((error) => ({
@@ -212,27 +139,14 @@ export const validateExcel = async (
212
139
  color: 'FFFF0000', // 红色
213
140
  }));
214
141
 
215
- // 使用excel-view统一创建标记Excel
216
- const markResult = await createMarkedExcelView(
217
- excelBuffer,
218
- {
219
- markCells,
220
- markHeaders: missingFields,
221
- },
222
- 'validation_errors.xlsx',
223
- );
224
-
225
- if (markResult.success && markResult.blobUrl) {
226
- // 从blob URL创建blob对象
227
- const response = await fetch(markResult.blobUrl);
228
- const errBlob = await response.blob();
229
- return { hasError: true, errBlob };
230
- } else {
231
- return { hasError: true };
232
- }
142
+ return {
143
+ hasError: true,
144
+ markCells,
145
+ markHeaders: missingFields,
146
+ };
233
147
  }
234
148
 
235
- return { hasError: false }; // 没有错误时返回null
149
+ return { hasError: false };
236
150
  };
237
151
 
238
152
  type ExcelValidationError = ValidateError & {
@@ -296,7 +210,8 @@ export const checkExcelDuplicates = async (
296
210
  url?: IUrlInfo,
297
211
  ): Promise<{
298
212
  hasError: boolean;
299
- errBlob?: Blob;
213
+ markCells?: Array<{ row: number; col: number; color: string }>;
214
+ markHeaders?: string[];
300
215
  }> => {
301
216
  if (!duplicateRules || duplicateRules.length === 0) {
302
217
  return { hasError: false };
@@ -328,14 +243,18 @@ export const checkExcelDuplicates = async (
328
243
 
329
244
  rows.forEach((rowData, index) => {
330
245
  // 构建唯一键,将所有重复规则字段的值连接起来
331
- const uniqueKey = duplicateRules.map((field) => rowData[field]).join('|');
246
+ const keyValues = duplicateRules.map((field) => rowData[field]);
247
+ const uniqueKey = keyValues.join('|');
332
248
  allKeys.push(uniqueKey);
333
249
 
334
- if (uniqueValues.has(uniqueKey)) {
250
+ // 只检测非空值的重复,避免多个空行被误判为重复
251
+ const hasValidValue = keyValues.some((value) => value !== null && value !== undefined && value !== '');
252
+
253
+ if (hasValidValue && uniqueValues.has(uniqueKey)) {
335
254
  // 找到重复行,记录当前索引和第一次出现的索引
336
255
  duplicateIndices.add(index); // 添加当前重复行
337
256
  duplicateIndices.add(uniqueValues.get(uniqueKey)!); // 添加第一次出现的行
338
- } else {
257
+ } else if (hasValidValue) {
339
258
  uniqueValues.set(uniqueKey, index);
340
259
  }
341
260
  });
@@ -367,33 +286,120 @@ export const checkExcelDuplicates = async (
367
286
  markCells.push({
368
287
  row: rowIndex + 2, // Excel行号 = 数组索引 + 2(表头和1-based索引)
369
288
  col: colIndex + 1, // Excel列号 = 数组索引 + 1(1-based索引)
370
- color: 'FFFF0000', // 红色
289
+ color: 'FFA500', // 黄橙色
371
290
  });
372
291
  }
373
292
  });
374
293
  });
375
294
 
376
- // 使用excel-view统一创建标记Excel
295
+ return {
296
+ hasError: true,
297
+ markCells,
298
+ markHeaders: missingDuplicateFields,
299
+ };
300
+ }
301
+
302
+ return { hasError: false }; // 没有重复数据
303
+ };
304
+
305
+ /**
306
+ * 统一验证Excel数据(格式验证和重复验证)
307
+ * @param excelBuffer Excel文件的ArrayBuffer
308
+ * @param rules 验证规则
309
+ * @param duplicateRules 重复规则
310
+ * @param duplicateUrl 重复检查URL
311
+ * @returns 统一验证结果
312
+ */
313
+ export const validateExcelUnified = async (
314
+ excelBuffer: ArrayBuffer,
315
+ rules?: Record<string, ValidateRule>,
316
+ duplicateRules?: string[],
317
+ duplicateUrl?: IUrlInfo,
318
+ ): Promise<{
319
+ hasError: boolean;
320
+ errBlob?: Blob;
321
+ validationMsg: string;
322
+ duplicateMsg: string;
323
+ }> => {
324
+ const allMarkCells: Array<{ row: number; col: number; color: string }> = [];
325
+ const allMarkHeaders: string[] = [];
326
+ let hasValidationError = false;
327
+ let hasDuplicateError = false;
328
+ let validationMsg = '数据验证成功';
329
+ let duplicateMsg = '数据验证通过';
330
+
331
+ // 1. 格式验证
332
+ const validationResult = await validateExcel(excelBuffer, rules);
333
+ if (validationResult.hasError) {
334
+ hasValidationError = true;
335
+ validationMsg = '数据验证失败';
336
+ if (validationResult.markCells) {
337
+ allMarkCells.push(...validationResult.markCells);
338
+ }
339
+ if (validationResult.markHeaders) {
340
+ allMarkHeaders.push(...validationResult.markHeaders);
341
+ }
342
+ }
343
+
344
+ // 2. 重复验证
345
+ if (duplicateRules && duplicateRules.length > 0) {
346
+ const duplicateResult = await checkExcelDuplicates(excelBuffer, duplicateRules, duplicateUrl);
347
+ if (duplicateResult.hasError) {
348
+ hasDuplicateError = true;
349
+ duplicateMsg = '检测到重复数据';
350
+ if (duplicateResult.markCells) {
351
+ // 检查是否有重复标记的单元格,如果有则改为深红色
352
+ duplicateResult.markCells.forEach((duplicateCell) => {
353
+ const existingCell = allMarkCells.find(
354
+ (cell) => cell.row === duplicateCell.row && cell.col === duplicateCell.col,
355
+ );
356
+ if (existingCell) {
357
+ existingCell.color = '8B0000'; // 深红色
358
+ } else {
359
+ allMarkCells.push(duplicateCell);
360
+ }
361
+ });
362
+ }
363
+ if (duplicateResult.markHeaders) {
364
+ allMarkHeaders.push(...duplicateResult.markHeaders);
365
+ }
366
+ }
367
+ }
368
+
369
+ // 3. 如果有任何错误,创建统一的标记Excel
370
+ if (hasValidationError || hasDuplicateError) {
377
371
  const markResult = await createMarkedExcelView(
378
372
  excelBuffer,
379
373
  {
380
- markCells,
381
- markHeaders: missingDuplicateFields,
374
+ markCells: allMarkCells,
375
+ markHeaders: allMarkHeaders,
382
376
  },
383
- 'duplicate_errors.xlsx',
377
+ 'validation_errors.xlsx',
384
378
  );
385
379
 
386
380
  if (markResult.success && markResult.blobUrl) {
387
- // 从blob URL创建blob对象
388
381
  const response = await fetch(markResult.blobUrl);
389
382
  const errBlob = await response.blob();
390
- return { hasError: true, errBlob };
383
+ return {
384
+ hasError: true,
385
+ errBlob,
386
+ validationMsg,
387
+ duplicateMsg,
388
+ };
391
389
  } else {
392
- return { hasError: true };
390
+ return {
391
+ hasError: true,
392
+ validationMsg,
393
+ duplicateMsg,
394
+ };
393
395
  }
394
396
  }
395
397
 
396
- return { hasError: false }; // 没有重复数据
398
+ return {
399
+ hasError: false,
400
+ validationMsg,
401
+ duplicateMsg,
402
+ };
397
403
  };
398
404
 
399
405
  /**
@@ -1,333 +0,0 @@
1
- import D from "async-validator";
2
- import { httpPost as S, ResStatus as T } from "@skyfox2000/fapi";
3
- import { i as _, ag as j } from "./uploadList-0f2FA_5s.js";
4
- import d from "vue-m-message";
5
- import { defineComponent as C, useAttrs as V, createElementBlock as A, openBlock as U, createVNode as O, unref as F, mergeProps as B } from "vue";
6
- import { Spin as H } from "ant-design-vue";
7
- const k = (u) => {
8
- const o = u.split(`
9
- `).filter((i) => i.trim() !== "");
10
- if (o.length === 0)
11
- return { headers: [], rows: [] };
12
- const s = o.map((i) => i.split(",").map((r) => {
13
- const e = r.trim();
14
- if (e === "" || e === "null") return null;
15
- const t = Number(e);
16
- return isNaN(t) ? e : t;
17
- })), a = s[0].map((i) => String(i || "")), l = s.slice(1);
18
- return { headers: a, rows: l };
19
- }, z = async (u) => {
20
- try {
21
- const o = await import("exceljs"), s = new o.default.Workbook();
22
- await s.xlsx.load(u);
23
- const a = s.worksheets[0];
24
- if (!a)
25
- return { headers: [], rows: [] };
26
- const l = [], i = [];
27
- return a.getRow(1).eachCell((r) => {
28
- let e = "";
29
- r.value !== null && r.value !== void 0 && (typeof r.value == "object" ? "richText" in r.value && Array.isArray(r.value.richText) ? e = r.value.richText.map((t) => t.text || "").join("") : "text" in r.value && typeof r.value.text == "string" ? e = r.value.text : r.value instanceof Date ? e = r.value.toLocaleDateString() : e = String(r.value) : e = String(r.value)), l.push(e);
30
- }), a.eachRow((r, e) => {
31
- if (e > 1) {
32
- const t = [];
33
- l.forEach((n, c) => {
34
- const h = r.getCell(c + 1).value;
35
- h != null ? typeof h == "object" ? "richText" in h && Array.isArray(h.richText) ? t.push(h.richText.map((p) => p.text || "").join("")) : "text" in h && typeof h.text == "string" ? t.push(h.text) : (h instanceof Date, t.push(h)) : t.push(h) : t.push(null);
36
- }), i.push(t);
37
- }
38
- }), { headers: l, rows: i };
39
- } catch (o) {
40
- return console.error("Excel解析失败:", o), { headers: [], rows: [] };
41
- }
42
- }, m = async (u, o, s) => {
43
- try {
44
- const a = await import("exceljs"), l = new a.default.Workbook(), i = l.addWorksheet("Sheet1"), { headers: r, rows: e } = u;
45
- if (r.length === 0)
46
- throw new Error("数据为空");
47
- const t = /* @__PURE__ */ new Map();
48
- s != null && s.markCells && s.markCells.forEach(({ row: p, col: w, color: x }) => {
49
- const E = `${p}-${w}`;
50
- t.set(E, x || "FFFF0000");
51
- });
52
- const n = i.getRow(1);
53
- r.forEach((p, w) => {
54
- const x = n.getCell(w + 1);
55
- x.value = p, x.font = { bold: !0 }, x.fill = {
56
- type: "pattern",
57
- pattern: "solid",
58
- fgColor: { argb: "FFE0E0E0" }
59
- }, s != null && s.markHeaders && s.markHeaders.includes(p) && (x.fill = {
60
- type: "pattern",
61
- pattern: "solid",
62
- fgColor: { argb: "FFFF0000" }
63
- }, x.font = {
64
- name: "Arial",
65
- size: 10,
66
- bold: !0,
67
- color: { argb: "FFFFFFFF" }
68
- });
69
- }), e.forEach((p, w) => {
70
- const x = i.getRow(w + 2);
71
- p.forEach((E, b) => {
72
- const y = x.getCell(b + 1);
73
- y.value = E;
74
- const v = `${w + 2}-${b + 1}`;
75
- t.has(v) && (y.fill = {
76
- type: "pattern",
77
- pattern: "solid",
78
- fgColor: { argb: t.get(v) }
79
- });
80
- });
81
- }), i.columns.forEach((p) => {
82
- p.width = 15;
83
- });
84
- const c = await l.xlsx.writeBuffer(), f = new Blob([c], {
85
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
86
- });
87
- return {
88
- success: !0,
89
- blobUrl: URL.createObjectURL(f),
90
- fileName: o.replace(/\.(csv|xlsx?)$/i, ".xlsx")
91
- };
92
- } catch (a) {
93
- return console.error("转换Excel失败:", a), {
94
- success: !1,
95
- error: a instanceof Error ? a.message : "未知错误"
96
- };
97
- }
98
- }, N = async (u, o) => {
99
- try {
100
- const s = k(u);
101
- return await m(s, o);
102
- } catch (s) {
103
- return {
104
- success: !1,
105
- error: s instanceof Error ? s.message : "CSV处理失败"
106
- };
107
- }
108
- }, M = async (u, o) => {
109
- try {
110
- const s = new Blob([u], {
111
- type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
112
- });
113
- return {
114
- success: !0,
115
- blobUrl: URL.createObjectURL(s),
116
- fileName: o
117
- };
118
- } catch (s) {
119
- return {
120
- success: !1,
121
- error: s instanceof Error ? s.message : "Excel处理失败"
122
- };
123
- }
124
- }, $ = async (u, o) => await m(u, o), X = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
125
- __proto__: null,
126
- csvToExcelView: N,
127
- csvToNormalized: k,
128
- excelToExcelView: M,
129
- excelToNormalized: z,
130
- normalizedToExcelView: $,
131
- toExcel: m
132
- }, Symbol.toStringTag, { value: "Module" })), g = async (u) => {
133
- const o = await import("exceljs"), s = new o.default.Workbook();
134
- await s.xlsx.load(u);
135
- const a = s.worksheets[0];
136
- if (!a)
137
- return d.error("Excel文件不包含工作表"), null;
138
- const l = [], i = [], r = [];
139
- return a.getRow(1).eachCell((e) => {
140
- let t = "";
141
- if (e.value !== null && e.value !== void 0)
142
- if (typeof e.value == "object")
143
- if ("richText" in e.value && Array.isArray(e.value.richText))
144
- t = e.value.richText.map((n) => n.text || "").join("");
145
- else if ("text" in e.value && typeof e.value.text == "string")
146
- t = e.value.text;
147
- else if (e.value instanceof Date)
148
- t = e.value.toLocaleDateString();
149
- else
150
- try {
151
- t = JSON.stringify(e.value);
152
- } catch {
153
- t = String(e.value);
154
- }
155
- else
156
- t = String(e.value);
157
- l.push(t);
158
- }), a.eachRow((e, t) => {
159
- if (t > 1) {
160
- const n = {}, c = [];
161
- l.forEach((f, h) => {
162
- if (f) {
163
- const w = e.getCell(h + 1).value;
164
- w != null ? typeof w == "object" ? "richText" in w && Array.isArray(w.richText) ? n[f] = w.richText.map((x) => x.text || "").join("") : "text" in w && typeof w.text == "string" ? n[f] = w.text : (w instanceof Date, n[f] = w) : n[f] = w : n[f] = null, c.push(n[f]);
165
- }
166
- }), i.push(n), r.push(c);
167
- }
168
- }), { workbook: s, worksheet: a, headers: l, excelData: i, excelRows: r };
169
- }, R = async (u, o, s) => {
170
- const a = await g(u);
171
- if (!a)
172
- return { success: !1, error: "Excel文件处理失败" };
173
- const { headers: l, excelRows: i } = a, { markHeaders: r } = o, e = [...l];
174
- r && r.length > 0 && r.forEach((n) => {
175
- e.includes(n) || e.push(n);
176
- });
177
- const t = i.map((n) => {
178
- const c = [...n];
179
- for (; c.length < e.length; )
180
- c.push(null);
181
- return c;
182
- });
183
- return await m({ headers: e, rows: t }, s, o);
184
- }, Y = async (u, o) => {
185
- if (!o || _(o))
186
- return { hasError: !1 };
187
- const s = await g(u);
188
- if (!s) return { hasError: !0 };
189
- const { headers: a, excelData: l } = s, i = [];
190
- if (Object.keys(o).forEach((t) => {
191
- a.includes(t) || i.push(t);
192
- }), a.length === 0 || l.length === 0)
193
- return d.error("Excel文件不包含足够的数据"), { hasError: !0 };
194
- const r = new D({});
195
- r.messages(j.messages()), r.define(o);
196
- const e = await L(a, l, r);
197
- if (e.length > 0 || i.length > 0) {
198
- const t = e.map((c) => ({
199
- row: c.row + 2,
200
- // 转为Excel行号(+2是因为表头占一行,且是1-based索引)
201
- col: c.col + 1,
202
- // 转为Excel列号(+1是因为是1-based索引)
203
- color: "FFFF0000"
204
- // 红色
205
- })), n = await R(
206
- u,
207
- {
208
- markCells: t,
209
- markHeaders: i
210
- },
211
- "validation_errors.xlsx"
212
- );
213
- return n.success && n.blobUrl ? { hasError: !0, errBlob: await (await fetch(n.blobUrl)).blob() } : { hasError: !0 };
214
- }
215
- return { hasError: !1 };
216
- }, L = async (u, o, s) => {
217
- const a = [];
218
- for (let l = 0; l < o.length; l++) {
219
- const i = o[l];
220
- try {
221
- await s.validate(i).catch(({ errors: r }) => {
222
- const e = [];
223
- r.forEach((t) => {
224
- const n = u.indexOf(t.field);
225
- n >= 0 && (e.some((f) => f.row === l && f.col === n) || e.push({
226
- row: l,
227
- col: n,
228
- header: t.field,
229
- message: t.message.replace("${label}", u[n])
230
- }));
231
- }), a.push(...e);
232
- });
233
- } catch (r) {
234
- console.error("验证表格数据时发生错误:", r), d.error("验证表格数据时发生错误:" + r);
235
- }
236
- }
237
- return a;
238
- }, Z = async (u, o, s) => {
239
- if (!o || o.length === 0)
240
- return { hasError: !1 };
241
- const a = await g(u);
242
- if (!a) return { hasError: !0 };
243
- const { headers: l, excelData: i } = a, r = [];
244
- if (o.forEach((c) => {
245
- l.includes(c) || r.push(c);
246
- }), r.length > 0)
247
- return d.error(`表头缺少重复检测所需字段: ${r.join(", ")}`), { hasError: !0 };
248
- const e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Set(), n = new Array();
249
- if (i.forEach((c, f) => {
250
- const h = o.map((p) => c[p]).join("|");
251
- n.push(h), e.has(h) ? (t.add(f), t.add(e.get(h))) : e.set(h, f);
252
- }), s) {
253
- const c = await S(s, {
254
- Data: n
255
- });
256
- if (c != null && c.data && c.data.forEach((f) => {
257
- t.add(f);
258
- }), (c == null ? void 0 : c.status) === T.ERROR)
259
- throw new Error(c.msg);
260
- }
261
- if (t.size > 0) {
262
- const c = [];
263
- t.forEach((h) => {
264
- o.forEach((p) => {
265
- const w = l.indexOf(p);
266
- w >= 0 && c.push({
267
- row: h + 2,
268
- // Excel行号 = 数组索引 + 2(表头和1-based索引)
269
- col: w + 1,
270
- // Excel列号 = 数组索引 + 1(1-based索引)
271
- color: "FFFF0000"
272
- // 红色
273
- });
274
- });
275
- });
276
- const f = await R(
277
- u,
278
- {
279
- markCells: c,
280
- markHeaders: r
281
- },
282
- "duplicate_errors.xlsx"
283
- );
284
- return f.success && f.blobUrl ? { hasError: !0, errBlob: await (await fetch(f.blobUrl)).blob() } : { hasError: !0 };
285
- }
286
- return { hasError: !1 };
287
- }, I = async (u, o, s) => {
288
- const a = u.originFileObj;
289
- if (a) {
290
- const l = await a.arrayBuffer(), i = await g(l);
291
- if (!i) {
292
- d.error("上传的文件不是Excel文件");
293
- return;
294
- }
295
- const { headers: r, excelRows: e, excelData: t } = i;
296
- s && s.length > 0 ? s.forEach((n) => {
297
- switch (n) {
298
- case "Headers":
299
- o.Headers = r;
300
- break;
301
- case "RawRows":
302
- o.RawRows = e;
303
- break;
304
- case "Records":
305
- o.Records = t;
306
- break;
307
- }
308
- }) : (o.Headers = r, o.RawRows = e, o.Records = t);
309
- }
310
- }, J = { class: "absolute z-[9999] w-full h-full top-0 flex flex-flow row items-center justify-center" }, ee = /* @__PURE__ */ C({
311
- __name: "index",
312
- setup(u) {
313
- const o = V();
314
- return (s, a) => (U(), A("div", J, [
315
- O(F(H), B({ style: { "margin-top": "-10%" } }, F(o)), null, 16)
316
- ]));
317
- }
318
- });
319
- export {
320
- ee as _,
321
- Z as a,
322
- I as b,
323
- N as c,
324
- R as d,
325
- k as e,
326
- z as f,
327
- M as g,
328
- X as h,
329
- $ as n,
330
- g as p,
331
- m as t,
332
- Y as v
333
- };