@skyfox2000/webui 1.3.2 → 1.3.4

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 (38) hide show
  1. package/lib/assets/modules/{file-upload-BVB9c-eZ.js → file-upload-BBlFaIXB.js} +1 -1
  2. package/lib/assets/modules/index-4kDAt8nS.js +333 -0
  3. package/lib/assets/modules/{index-CaaMz5sz.js → index-BG1SqSVl.js} +1 -1
  4. package/lib/assets/modules/{index-k_AnrbPY.js → index-m5rogIyM.js} +2 -2
  5. package/lib/assets/modules/{menuTabs-_Ph7P8ES.js → menuTabs-tPIz4a89.js} +2 -2
  6. package/lib/assets/modules/{toolIcon-QMXCkImG.js → toolIcon-DwWoD9TN.js} +1 -1
  7. package/lib/assets/modules/{uploadList-D-FOtndj.js → uploadList-D_Z-Y2tw.js} +482 -508
  8. package/lib/assets/modules/uploadList-Da7mQUNK.js +382 -0
  9. package/lib/components/form/autoComplete/index.vue.d.ts +10 -48
  10. package/lib/components/form/upload/uploadList.vue.d.ts +1 -0
  11. package/lib/es/AceEditor/index.js +3 -3
  12. package/lib/es/BasicLayout/index.js +3 -3
  13. package/lib/es/Error403/index.js +1 -1
  14. package/lib/es/Error404/index.js +1 -1
  15. package/lib/es/ExcelForm/index.js +332 -202
  16. package/lib/es/UploadForm/index.js +4 -4
  17. package/lib/index.d.ts +4 -3
  18. package/lib/utils/download.d.ts +2 -0
  19. package/lib/utils/excel-view.d.ts +25 -0
  20. package/lib/utils/form-csv.d.ts +18 -0
  21. package/lib/utils/form-excel.d.ts +2 -13
  22. package/lib/webui.css +1 -1
  23. package/lib/webui.es.js +773 -742
  24. package/package.json +2 -2
  25. package/src/components/common/loading/index.vue +1 -1
  26. package/src/components/content/dialog/excelForm.vue +384 -106
  27. package/src/components/content/table/index.vue +22 -10
  28. package/src/components/form/autoComplete/index.vue +29 -60
  29. package/src/components/form/upload/uploadList.vue +50 -11
  30. package/src/index.ts +26 -3
  31. package/src/utils/download.ts +31 -0
  32. package/src/utils/excel-view.ts +340 -0
  33. package/src/utils/form-csv.ts +55 -0
  34. package/src/utils/form-excel.ts +59 -192
  35. package/src/utils/options.ts +0 -1
  36. package/vite.config.ts +0 -1
  37. package/lib/assets/modules/form-excel-CsQBtfkA.js +0 -235
  38. package/lib/assets/modules/uploadList-CXa3siDj.js +0 -327
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Excel视图预处理工具
3
+ * 统一处理Excel和CSV数据的显示预处理,支持错误标记
4
+ */
5
+
6
+ /**
7
+ * Excel错误标记单元格
8
+ */
9
+ export interface ExcelMarkCell {
10
+ /** 行号(从1开始) */
11
+ row: number;
12
+ /** 列号(从1开始) */
13
+ col: number;
14
+ /** 标记颜色,默认红色 */
15
+ color?: string;
16
+ }
17
+
18
+ /**
19
+ * Excel标记信息
20
+ */
21
+ export interface ExcelMarkInfo {
22
+ /** 要标记的单元格列表 */
23
+ markCells: ExcelMarkCell[];
24
+ /** 要标记的表头字段列表 */
25
+ markHeaders?: string[];
26
+ }
27
+
28
+ /**
29
+ * 规范数组数据格式
30
+ * @interface NormalizedData
31
+ * @property {string[]} headers - 表头数组,如: ["姓名", "年龄", "地址"]
32
+ * @property {(string|number|null)[][]} rows - 数据行数组,每行对应headers的值
33
+ *
34
+ * @example
35
+ * {
36
+ * headers: ["姓名", "年龄", "地址"],
37
+ * rows: [
38
+ * ["张三", 25, "北京市"],
39
+ * ["李四", 30, "上海市"],
40
+ * ["王五", null, "广州市"]
41
+ * ]
42
+ * }
43
+ */
44
+ export interface NormalizedData {
45
+ /** 表头数组 */
46
+ headers: string[];
47
+ /** 数据行数组,每行为一个数组,对应headers的顺序 */
48
+ rows: any[][];
49
+ }
50
+
51
+ /**
52
+ * Excel预览结果
53
+ */
54
+ export interface ExcelViewResult {
55
+ /** 是否成功 */
56
+ success: boolean;
57
+ /** blob URL用于预览 */
58
+ blobUrl?: string;
59
+ /** 处理后的文件名 */
60
+ fileName?: string;
61
+ /** 错误信息 */
62
+ error?: string;
63
+ }
64
+
65
+ /**
66
+ * 将CSV文本转换为规范数组格式
67
+ * @param csvContent CSV文本内容
68
+ * @returns 规范数组数据
69
+ */
70
+ export const csvToNormalized = (csvContent: string): NormalizedData => {
71
+ const lines = csvContent.split('\n').filter((line) => line.trim() !== '');
72
+
73
+ if (lines.length === 0) {
74
+ return { headers: [], rows: [] };
75
+ }
76
+
77
+ // 简单的CSV解析,处理逗号分隔
78
+ const allRows = lines.map((line) => {
79
+ return line.split(',').map((cell) => {
80
+ const trimmed = cell.trim();
81
+ // 尝试转换为数字
82
+ if (trimmed === '' || trimmed === 'null') return null;
83
+ const num = Number(trimmed);
84
+ return isNaN(num) ? trimmed : num;
85
+ });
86
+ });
87
+
88
+ const headers = allRows[0].map((h) => String(h || ''));
89
+ const rows = allRows.slice(1);
90
+
91
+ return { headers, rows };
92
+ };
93
+
94
+ /**
95
+ * 将Excel ArrayBuffer转换为规范数组格式
96
+ * @param excelBuffer Excel文件的ArrayBuffer
97
+ * @returns 规范数组数据
98
+ */
99
+ export const excelToNormalized = async (excelBuffer: ArrayBuffer): Promise<NormalizedData> => {
100
+ try {
101
+ // 动态导入 exceljs
102
+ const ExcelJS = await import('exceljs');
103
+ const workbook = new ExcelJS.default.Workbook();
104
+ await workbook.xlsx.load(excelBuffer);
105
+
106
+ // 获取第一个工作表
107
+ const worksheet = workbook.worksheets[0];
108
+ if (!worksheet) {
109
+ return { headers: [], rows: [] };
110
+ }
111
+
112
+ const headers: string[] = [];
113
+ const rows: any[][] = [];
114
+
115
+ // 提取表头(第一行)
116
+ worksheet.getRow(1).eachCell((cell) => {
117
+ let headerValue = '';
118
+ if (cell.value !== null && cell.value !== undefined) {
119
+ if (typeof cell.value === 'object') {
120
+ // 处理RichText类型
121
+ if ('richText' in cell.value && Array.isArray(cell.value.richText)) {
122
+ headerValue = cell.value.richText.map((rt: any) => rt.text || '').join('');
123
+ } else if ('text' in cell.value && typeof cell.value.text === 'string') {
124
+ headerValue = cell.value.text;
125
+ } else if (cell.value instanceof Date) {
126
+ headerValue = cell.value.toLocaleDateString();
127
+ } else {
128
+ headerValue = String(cell.value);
129
+ }
130
+ } else {
131
+ headerValue = String(cell.value);
132
+ }
133
+ }
134
+ headers.push(headerValue);
135
+ });
136
+
137
+ // 提取数据行
138
+ worksheet.eachRow((row, rowNumber) => {
139
+ if (rowNumber > 1) {
140
+ // 跳过表头
141
+ const rowData: any[] = [];
142
+ headers.forEach((_, idx) => {
143
+ const cell = row.getCell(idx + 1);
144
+ const cellValue = cell.value;
145
+
146
+ if (cellValue !== null && cellValue !== undefined) {
147
+ if (typeof cellValue === 'object') {
148
+ if ('richText' in cellValue && Array.isArray(cellValue.richText)) {
149
+ rowData.push(cellValue.richText.map((rt: any) => rt.text || '').join(''));
150
+ } else if ('text' in cellValue && typeof cellValue.text === 'string') {
151
+ rowData.push(cellValue.text);
152
+ } else if (cellValue instanceof Date) {
153
+ rowData.push(cellValue);
154
+ } else {
155
+ rowData.push(cellValue);
156
+ }
157
+ } else {
158
+ rowData.push(cellValue);
159
+ }
160
+ } else {
161
+ rowData.push(null);
162
+ }
163
+ });
164
+ rows.push(rowData);
165
+ }
166
+ });
167
+
168
+ return { headers, rows };
169
+ } catch (error) {
170
+ console.error('Excel解析失败:', error);
171
+ return { headers: [], rows: [] };
172
+ }
173
+ };
174
+
175
+ /**
176
+ * 将规范数组数据转换为Excel预览,支持错误标记
177
+ * @param data 规范数组数据
178
+ * @param fileName 文件名
179
+ * @param markInfo 可选的标记信息
180
+ * @returns Excel预览结果
181
+ */
182
+ export const toExcel = async (
183
+ data: NormalizedData,
184
+ fileName: string,
185
+ markInfo?: ExcelMarkInfo,
186
+ ): Promise<ExcelViewResult> => {
187
+ try {
188
+ // 动态导入 exceljs
189
+ const ExcelJS = await import('exceljs');
190
+ const workbook = new ExcelJS.default.Workbook();
191
+ const worksheet = workbook.addWorksheet('Sheet1');
192
+
193
+ const { headers, rows } = data;
194
+
195
+ if (headers.length === 0) {
196
+ throw new Error('数据为空');
197
+ }
198
+
199
+ // 创建错误单元格映射
200
+ const cellMarkMap = new Map<string, string>();
201
+ if (markInfo?.markCells) {
202
+ markInfo.markCells.forEach(({ row, col, color }) => {
203
+ const cellKey = `${row}-${col}`;
204
+ cellMarkMap.set(cellKey, color || 'FFFF0000');
205
+ });
206
+ }
207
+
208
+ // 添加表头
209
+ const headerRow = worksheet.getRow(1);
210
+ headers.forEach((header, index) => {
211
+ const headerCell = headerRow.getCell(index + 1);
212
+ headerCell.value = header;
213
+
214
+ // 设置默认表头样式
215
+ headerCell.font = { bold: true };
216
+ headerCell.fill = {
217
+ type: 'pattern',
218
+ pattern: 'solid',
219
+ fgColor: { argb: 'FFE0E0E0' },
220
+ };
221
+
222
+ // 标记缺失的表头字段
223
+ if (markInfo?.markHeaders && markInfo.markHeaders.includes(header)) {
224
+ headerCell.fill = {
225
+ type: 'pattern',
226
+ pattern: 'solid',
227
+ fgColor: { argb: 'FFFF0000' },
228
+ };
229
+ headerCell.font = {
230
+ name: 'Arial',
231
+ size: 10,
232
+ bold: true,
233
+ color: { argb: 'FFFFFFFF' },
234
+ };
235
+ }
236
+ });
237
+
238
+ // 添加数据行
239
+ rows.forEach((row, index) => {
240
+ const worksheetRow = worksheet.getRow(index + 2);
241
+ row.forEach((cell, cellIndex) => {
242
+ const worksheetCell = worksheetRow.getCell(cellIndex + 1);
243
+ worksheetCell.value = cell;
244
+
245
+ // 检查是否需要标记单元格
246
+ const cellKey = `${index + 2}-${cellIndex + 1}`;
247
+ if (cellMarkMap.has(cellKey)) {
248
+ worksheetCell.fill = {
249
+ type: 'pattern',
250
+ pattern: 'solid',
251
+ fgColor: { argb: cellMarkMap.get(cellKey) },
252
+ };
253
+ }
254
+ });
255
+ });
256
+
257
+ // 自动调整列宽
258
+ worksheet.columns.forEach((column) => {
259
+ column.width = 15;
260
+ });
261
+
262
+ // 生成Excel文件Buffer
263
+ const excelBuffer = await workbook.xlsx.writeBuffer();
264
+
265
+ // 创建Blob对象
266
+ const blob = new Blob([excelBuffer], {
267
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
268
+ });
269
+
270
+ // 创建blob URL
271
+ const blobUrl = URL.createObjectURL(blob);
272
+
273
+ return {
274
+ success: true,
275
+ blobUrl,
276
+ fileName: fileName.replace(/\.(csv|xlsx?)$/i, '.xlsx'),
277
+ };
278
+ } catch (error) {
279
+ console.error('转换Excel失败:', error);
280
+ return {
281
+ success: false,
282
+ error: error instanceof Error ? error.message : '未知错误',
283
+ };
284
+ }
285
+ };
286
+
287
+ /**
288
+ * CSV文本转Excel预览
289
+ * @param csvContent CSV文本内容
290
+ * @param fileName 文件名
291
+ * @returns Excel预览结果
292
+ */
293
+ export const csvToExcelView = async (csvContent: string, fileName: string): Promise<ExcelViewResult> => {
294
+ try {
295
+ const normalized = csvToNormalized(csvContent);
296
+ return await toExcel(normalized, fileName);
297
+ } catch (error) {
298
+ return {
299
+ success: false,
300
+ error: error instanceof Error ? error.message : 'CSV处理失败',
301
+ };
302
+ }
303
+ };
304
+
305
+ /**
306
+ * Excel ArrayBuffer转Excel预览
307
+ * @param excelBuffer Excel文件的ArrayBuffer
308
+ * @param fileName 文件名
309
+ * @returns Excel预览结果
310
+ */
311
+ export const excelToExcelView = async (excelBuffer: ArrayBuffer, fileName: string): Promise<ExcelViewResult> => {
312
+ try {
313
+ // Excel文件直接创建blob URL
314
+ const blob = new Blob([excelBuffer], {
315
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
316
+ });
317
+ const blobUrl = URL.createObjectURL(blob);
318
+
319
+ return {
320
+ success: true,
321
+ blobUrl,
322
+ fileName,
323
+ };
324
+ } catch (error) {
325
+ return {
326
+ success: false,
327
+ error: error instanceof Error ? error.message : 'Excel处理失败',
328
+ };
329
+ }
330
+ };
331
+
332
+ /**
333
+ * 规范数组转Excel预览
334
+ * @param data 规范数组数据
335
+ * @param fileName 文件名
336
+ * @returns Excel预览结果
337
+ */
338
+ export const normalizedToExcelView = async (data: NormalizedData, fileName: string): Promise<ExcelViewResult> => {
339
+ return await toExcel(data, fileName);
340
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * CSV数据处理工具 - 统一使用excel-view处理
3
+ */
4
+ import { csvToExcelView } from './excel-view';
5
+
6
+ /**
7
+ * 将CSV文本内容转换为Excel blob URL用于预览
8
+ * @param csvContent CSV文本内容
9
+ * @param fileName 文件名
10
+ * @returns blob URL和文件信息
11
+ * @deprecated 使用 csvToExcelView 替代
12
+ */
13
+ export const csvToExcelBlob = async (csvContent: string, fileName: string) => {
14
+ const result = await csvToExcelView(csvContent, fileName);
15
+ if (result.success) {
16
+ return {
17
+ blobUrl: result.blobUrl!,
18
+ fileName: result.fileName!,
19
+ blob: null, // 不再返回blob对象,使用URL即可
20
+ };
21
+ } else {
22
+ throw new Error(result.error || 'CSV转Excel失败');
23
+ }
24
+ };
25
+
26
+ /**
27
+ * 处理CSV文件上传前的预览
28
+ * @param csvBuffer CSV文件的ArrayBuffer
29
+ * @param fileName 文件名
30
+ * @returns Promise包含处理结果
31
+ */
32
+ export const processCsvFile = async (csvBuffer: ArrayBuffer, fileName: string) => {
33
+ try {
34
+ // 将ArrayBuffer转换为文本
35
+ const decoder = new TextDecoder('utf-8');
36
+ const text = decoder.decode(csvBuffer);
37
+
38
+ // 使用excel-view统一处理
39
+ const result = await csvToExcelView(text, fileName);
40
+
41
+ return {
42
+ success: result.success,
43
+ blobUrl: result.blobUrl,
44
+ fileName: result.fileName,
45
+ error: result.error,
46
+ csvContent: text, // 保留原始CSV内容
47
+ };
48
+ } catch (error) {
49
+ console.error('CSV文件处理失败:', error);
50
+ return {
51
+ success: false,
52
+ error: error instanceof Error ? error.message : '未知错误',
53
+ };
54
+ }
55
+ };
@@ -7,23 +7,9 @@ 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
11
 
11
- /**
12
- * Excel数据处理需要标记的单元格
13
- */
14
- export type ExcelMarkCell = {
15
- row: number;
16
- col: number;
17
- color?: string;
18
- };
19
-
20
- /**
21
- * Excel标记类型
22
- */
23
- export type ExcelMarkInfo = {
24
- markCells: ExcelMarkCell[];
25
- markHeaders?: string[];
26
- };
12
+ // ExcelMarkCell 和 ExcelMarkInfo 类型已移至 excel-view.ts 统一管理
27
13
 
28
14
  /**
29
15
  * 处理Excel文件的通用函数,用于提取表头和数据
@@ -133,31 +119,23 @@ export const processExcelFile = async (excelBuffer: ArrayBuffer) => {
133
119
  };
134
120
 
135
121
  /**
136
- * 生成标记了错误/重复数据的Excel文件
122
+ * 创建带错误标记的Excel预览
137
123
  * @param excelBuffer 原始Excel文件的ArrayBuffer
138
- * @param markInfo 标记信息,包含要标记的单元格和表头
139
- * @returns 标记后的Excel文件Blob
124
+ * @param markInfo 标记信息
125
+ * @param fileName 文件名
126
+ * @returns 标记后的Excel预览结果
140
127
  */
141
- export const createMarkedExcelBlob = async (
142
- excelBuffer: ArrayBuffer,
143
- markInfo: ExcelMarkInfo,
144
- ): Promise<{
145
- hasError: boolean;
146
- errBlob?: Blob;
147
- }> => {
148
- const ExcelJS = await import('exceljs');
149
- // 处理Excel文件
128
+ export const createMarkedExcelView = async (excelBuffer: ArrayBuffer, markInfo: ExcelMarkInfo, fileName: string) => {
129
+ // 处理Excel文件获取数据
150
130
  const excelData = await processExcelFile(excelBuffer);
151
- if (!excelData) return { hasError: true };
152
-
153
- const { worksheet, headers: originalHeaders } = excelData;
154
- const { markCells, markHeaders } = markInfo;
155
-
156
- if (markCells.length === 0 && (!markHeaders || markHeaders.length === 0)) {
157
- return { hasError: false }; // 没有需要标记的内容
131
+ if (!excelData) {
132
+ return { success: false, error: 'Excel文件处理失败' };
158
133
  }
159
134
 
160
- // 处理缺失字段,将缺失字段添加到headers
135
+ const { headers: originalHeaders, excelRows } = excelData;
136
+ const { markHeaders } = markInfo;
137
+
138
+ // 处理缺失字段,添加到表头
161
139
  const headers = [...originalHeaders];
162
140
  if (markHeaders && markHeaders.length > 0) {
163
141
  markHeaders.forEach((field) => {
@@ -167,158 +145,18 @@ export const createMarkedExcelBlob = async (
167
145
  });
168
146
  }
169
147
 
170
- // 创建一个新的工作簿和工作表
171
- const newWorkbook = new ExcelJS.default.Workbook();
172
- const newWorksheet = newWorkbook.addWorksheet('Sheet1');
173
-
174
- // 默认列宽
175
- const defaultColumnWidth = 15;
176
-
177
- // 创建错误单元格位置的查询表,用于快速检查
178
- const cellMarkMap = new Map<string, string>();
179
- markCells.forEach(({ row, col, color }) => {
180
- const cellKey = `${row}-${col}`;
181
- cellMarkMap.set(cellKey, color || 'FFFF0000'); // 默认红色
182
- });
183
-
184
- // 复制列宽并应用于新工作表
185
- for (let i = 0; i < headers.length; i++) {
186
- const column = newWorksheet.getColumn(i + 1);
187
-
188
- if (i < worksheet.columnCount && i < originalHeaders.length) {
189
- const originalCol = worksheet.getColumn(i + 1);
190
- if (originalCol && originalCol.width) {
191
- column.width = originalCol.width;
192
- } else {
193
- column.width = defaultColumnWidth;
194
- }
195
- } else {
196
- column.width = defaultColumnWidth;
197
- }
198
- }
199
-
200
- // 复制表头行
201
- const headerRow = newWorksheet.getRow(1);
202
- headers.forEach((header, index) => {
203
- const headerCell = headerRow.getCell(index + 1);
204
- headerCell.value = header;
205
-
206
- const isOriginalHeader = index < originalHeaders.length;
207
- if (isOriginalHeader && index < worksheet.columnCount) {
208
- // 复制原表头样式
209
- const originalCell = worksheet.getRow(1).getCell(index + 1);
210
-
211
- // 复制样式
212
- if (originalCell.style) headerCell.style = JSON.parse(JSON.stringify(originalCell.style));
213
- if (originalCell.font) headerCell.font = JSON.parse(JSON.stringify(originalCell.font));
214
- if (originalCell.alignment) headerCell.alignment = JSON.parse(JSON.stringify(originalCell.alignment));
215
- if (originalCell.border) headerCell.border = JSON.parse(JSON.stringify(originalCell.border));
216
- if (originalCell.numFmt) headerCell.numFmt = originalCell.numFmt;
217
- if (originalCell.fill) headerCell.fill = JSON.parse(JSON.stringify(originalCell.fill));
218
- }
219
-
220
- // 仅标记缺失的表头字段,不标记数据重复的表头
221
- if (markHeaders && markHeaders.includes(header) && !isOriginalHeader) {
222
- headerCell.fill = {
223
- type: 'pattern',
224
- pattern: 'solid',
225
- fgColor: { argb: 'FFFF0000' }, // 红色背景
226
- };
227
-
228
- // 添加白色文字
229
- headerCell.font = {
230
- name: 'Arial',
231
- size: 10,
232
- bold: true,
233
- color: { argb: 'FFFFFFFF' }, // 白色文字
234
- };
148
+ // 扩展数据行以匹配新的表头长度
149
+ const rows = excelRows.map((row) => {
150
+ const newRow = [...row];
151
+ // 为新增的表头字段添加空值
152
+ while (newRow.length < headers.length) {
153
+ newRow.push(null);
235
154
  }
155
+ return newRow;
236
156
  });
237
- headerRow.commit();
238
-
239
- // 处理数据行
240
- worksheet.eachRow((row, rowNumber) => {
241
- if (rowNumber > 1) {
242
- // 跳过表头行
243
- const newRow = newWorksheet.getRow(rowNumber);
244
-
245
- // 使用表头长度来循环每个单元格
246
- for (let colIndex = 0; colIndex < headers.length; colIndex++) {
247
- const newCell = newRow.getCell(colIndex + 1);
248
- const isOriginalHeader = colIndex < originalHeaders.length;
249
-
250
- if (isOriginalHeader && colIndex < worksheet.columnCount) {
251
- // 对原始数据中存在的列
252
- const originalCell = row.getCell(colIndex + 1);
253
-
254
- // 复制值
255
- newCell.value = originalCell.value;
256
-
257
- // 处理对象类型的单元格值,确保正确显示
258
- if (newCell.value !== null && newCell.value !== undefined && typeof newCell.value === 'object') {
259
- // 对于RichText,保留原格式
260
- if (
261
- !('richText' in newCell.value) &&
262
- !('formula' in newCell.value) &&
263
- !('hyperlink' in newCell.value) &&
264
- !(newCell.value instanceof Date)
265
- ) {
266
- // 尝试转换为字符串显示
267
- try {
268
- if ('text' in newCell.value && typeof newCell.value.text === 'string') {
269
- newCell.value = newCell.value.text;
270
- } else {
271
- // 其他对象类型尝试JSON序列化
272
- newCell.value = JSON.stringify(newCell.value);
273
- }
274
- } catch {
275
- // 如果转换失败,使用toString
276
- newCell.value = String(newCell.value);
277
- }
278
- }
279
- }
280
157
 
281
- // 复制样式
282
- if (originalCell.style) newCell.style = JSON.parse(JSON.stringify(originalCell.style));
283
- if (originalCell.font) newCell.font = JSON.parse(JSON.stringify(originalCell.font));
284
- if (originalCell.alignment) newCell.alignment = JSON.parse(JSON.stringify(originalCell.alignment));
285
- if (originalCell.border) newCell.border = JSON.parse(JSON.stringify(originalCell.border));
286
- if (originalCell.numFmt) newCell.numFmt = originalCell.numFmt;
287
- if (originalCell.fill) newCell.fill = JSON.parse(JSON.stringify(originalCell.fill));
288
-
289
- // 检查是否为需要标记的单元格
290
- const cellKey = `${rowNumber}-${colIndex + 1}`;
291
- if (cellMarkMap.has(cellKey)) {
292
- // 设置背景颜色
293
- newCell.fill = {
294
- type: 'pattern',
295
- pattern: 'solid',
296
- fgColor: { argb: cellMarkMap.get(cellKey) },
297
- };
298
- }
299
- } else {
300
- // 为新增列设置空值
301
- newCell.value = null;
302
- }
303
- }
304
-
305
- // 复制行属性
306
- if (row.height) newRow.height = row.height;
307
- if (row.outlineLevel) newRow.outlineLevel = row.outlineLevel;
308
-
309
- newRow.commit();
310
- }
311
- });
312
-
313
- // 生成Excel文件Buffer
314
- const excelOutput = await newWorkbook.xlsx.writeBuffer();
315
-
316
- // 创建Blob对象
317
- const excelBlob = new Blob([excelOutput], {
318
- type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
319
- });
320
-
321
- return { hasError: true, errBlob: excelBlob };
158
+ // 调用excel-view的方法生成带标记的Excel
159
+ return await toExcel({ headers, rows }, fileName, markInfo);
322
160
  };
323
161
 
324
162
  /**
@@ -374,11 +212,24 @@ export const validateExcel = async (
374
212
  color: 'FFFF0000', // 红色
375
213
  }));
376
214
 
377
- // 创建标记过的Excel文件,确保传入所有表头(包括缺失的)
378
- return createMarkedExcelBlob(excelBuffer, {
379
- markCells,
380
- markHeaders: missingFields,
381
- });
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
+ }
382
233
  }
383
234
 
384
235
  return { hasError: false }; // 没有错误时返回null
@@ -522,8 +373,24 @@ export const checkExcelDuplicates = async (
522
373
  });
523
374
  });
524
375
 
525
- // 创建标记过的Excel文件
526
- return createMarkedExcelBlob(excelBuffer, { markCells, markHeaders: missingDuplicateFields });
376
+ // 使用excel-view统一创建标记Excel
377
+ const markResult = await createMarkedExcelView(
378
+ excelBuffer,
379
+ {
380
+ markCells,
381
+ markHeaders: missingDuplicateFields,
382
+ },
383
+ 'duplicate_errors.xlsx',
384
+ );
385
+
386
+ if (markResult.success && markResult.blobUrl) {
387
+ // 从blob URL创建blob对象
388
+ const response = await fetch(markResult.blobUrl);
389
+ const errBlob = await response.blob();
390
+ return { hasError: true, errBlob };
391
+ } else {
392
+ return { hasError: true };
393
+ }
527
394
  }
528
395
 
529
396
  return { hasError: false }; // 没有重复数据
@@ -159,7 +159,6 @@ const queryOptions = <T>(
159
159
  ...rest,
160
160
  };
161
161
  optionUrl.loadingText = false;
162
- console.log(optionUrl.fieldMap, params);
163
162
  if (!params) params = {};
164
163
  if (!params.Query) params.Query = {};
165
164
  optionCtrl.optionQuery = params;