@yy-common/shared 1.0.0-beta.6

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/utils/excel.js ADDED
@@ -0,0 +1,789 @@
1
+ /* eslint-disable */
2
+
3
+ // const XlsxPopulate = require('xlsx-populate/browser/xlsx-populate')
4
+ import _ from "lodash";
5
+ import XLSX from "xlsx";
6
+
7
+ const titleStyle = {
8
+ // title style
9
+ bold: true,
10
+ fontSize: "14",
11
+ horizontalAlignment: "center",
12
+ verticalAlignment: "center",
13
+ };
14
+ const subTitleStyle = {
15
+ // title style
16
+ bold: false,
17
+ fontSize: "10",
18
+ horizontalAlignment: "center",
19
+ verticalAlignment: "center",
20
+ };
21
+ var subTitleHeight = 20; // 次标题行高设置为20
22
+ const paramLeftStyle = {
23
+ fontSize: "10",
24
+ horizontalAlignment: "left",
25
+ verticalAlignment: "center",
26
+ topBorder: true,
27
+ bottomBorder: true,
28
+ };
29
+
30
+ const paramRightStyle = {
31
+ fontSize: "10",
32
+ horizontalAlignment: "right",
33
+ verticalAlignment: "center",
34
+ topBorder: true,
35
+ bottomBorder: true,
36
+ };
37
+
38
+ const columnTitleStyle = {
39
+ bold: true,
40
+ fontSize: "10",
41
+ horizontalAlignment: "center",
42
+ verticalAlignment: "center",
43
+ fill: "bdc3c7",
44
+ topBorder: true,
45
+ bottomBorder: true,
46
+ leftBorder: true,
47
+ rightBorder: true,
48
+ };
49
+
50
+ const dataCellStyle = {
51
+ fontSize: "10",
52
+ horizontalAlignment: "center",
53
+ verticalAlignment: "center",
54
+ topBorder: true,
55
+ bottomBorder: true,
56
+ leftBorder: true,
57
+ rightBorder: true,
58
+ };
59
+
60
+ const titleHeight = 27;
61
+
62
+ const paramHeight = 18;
63
+
64
+ const columnTitleHeight = 18;
65
+
66
+ const dataRowHeight = 18;
67
+
68
+ /**
69
+ * 根据agGrid的参数,获取导出Excel所需要的参数
70
+ * @param {*} param0
71
+ */
72
+ function getAgColumnTitleAndData({ columns, datas }) {
73
+ // columns是个树形结构,要根据他的树形结构计算出表头的数据,然后通过这个表头的数据计算出columnTitle和data
74
+ var columnsClo = _.cloneDeep(columns);
75
+ var datasClo = _.cloneDeep(datas);
76
+
77
+ // 删掉编辑列
78
+ _.remove(columnsClo, (d) => {
79
+ return d.cellClass && d.cellClass.indexOf("button") != -1;
80
+ });
81
+
82
+ // 增加索引列
83
+ datasClo.forEach((dd, idx) => {
84
+ dd["__seq"] = idx + 1;
85
+ });
86
+
87
+ _.forEach(columnsClo, (d) => {
88
+ if (
89
+ d.type == "enum" ||
90
+ (d.cellClass && d.cellClass.indexOf("enum") != -1)
91
+ ) {
92
+ // 处理枚举值
93
+ datasClo.forEach((dd) => {
94
+ dd[d.field || d.key] =
95
+ _.find(
96
+ d.cellRendererParams.datas || d.cellRendererParams.enumList || [],
97
+ (item) => item.K == dd[d.field || d.key],
98
+ )?.V || dd[d.field || d.key];
99
+ });
100
+ }
101
+ });
102
+
103
+ // 根据列定义,取列定义的树深
104
+ const getDeepthOfCol = (col) => {
105
+ if (!col.children) {
106
+ return 1;
107
+ } else {
108
+ var deepthSingle =
109
+ 1 +
110
+ Math.max(
111
+ ...col.children.map((d) => {
112
+ return getDeepthOfCol(d);
113
+ }),
114
+ );
115
+ col.deepth = deepthSingle;
116
+ return deepthSingle;
117
+ }
118
+ };
119
+ var deepth = Math.max(
120
+ ...columnsClo.map((d) => {
121
+ var deepthSingle = getDeepthOfCol(d);
122
+ d.deepth = deepthSingle;
123
+ return deepthSingle;
124
+ }),
125
+ );
126
+ console.log("calc deepth = ", deepth);
127
+
128
+ // 根据列定义,取列定义的宽度
129
+ const getWidthOfCol = (col) => {
130
+ if (!col.children) {
131
+ return 1;
132
+ } else {
133
+ var widthSingle = _.sum(
134
+ col.children.map((d) => {
135
+ return getWidthOfCol(d);
136
+ }),
137
+ );
138
+ col.width = widthSingle;
139
+ return widthSingle;
140
+ }
141
+ };
142
+ var width = _.sum(
143
+ columnsClo.map((d) => {
144
+ var widthSingle = getWidthOfCol(d);
145
+ d.width = widthSingle;
146
+ return widthSingle;
147
+ }),
148
+ );
149
+ console.log("calc width = ", width);
150
+
151
+ console.log("now columnsClo is ", columnsClo);
152
+
153
+ // 遍历树形数据,组织真正的待导出的表头数据
154
+ var columnTitle = [];
155
+ var curCol = 1; // 用于辅助定位单元格开始结束位置,即当前遍历到的列的位置
156
+ var curDeep = 0; // 用于辅助定位单元格开始结束位置,即递归调用深度
157
+ function calcTitle(src, dist) {
158
+ curDeep += 1;
159
+ src.reduce((rlt, col, idx) => {
160
+ if (col.children) {
161
+ rlt.push({
162
+ text: col.excelName || col.headerName || col.title, // 单元格数据
163
+ srow: curDeep, // 在excel开始行
164
+ scol: curCol, // 在excel开始列
165
+ erow: curDeep /* + 1*/, // 在excel结束行
166
+ ecol: curCol + col.width - 1, // 在excel结束列
167
+ });
168
+ calcTitle(col.children, rlt);
169
+ } else {
170
+ rlt.push({
171
+ text: col.excelName || col.headerName || col.title, // 单元格数据
172
+ srow: curDeep, // 在excel开始行
173
+ scol: curCol, // 在excel开始列
174
+ erow: deepth /* + 1*/, // 在excel结束行
175
+ ecol: curCol /* + 1*/, // 在excel结束列
176
+ });
177
+ curCol += 1;
178
+ }
179
+ return rlt;
180
+ }, dist);
181
+ curDeep -= 1;
182
+ }
183
+ calcTitle(columnsClo, columnTitle);
184
+
185
+ // 组织真正的列定义,即所有的叶子结点
186
+ var columnsCalc = [];
187
+ function calcColumns(src, dist) {
188
+ src.reduce((rlt, col) => {
189
+ if (col.children) {
190
+ calcColumns(col.children, rlt);
191
+ } else {
192
+ rlt.push(col);
193
+ }
194
+ return rlt;
195
+ }, dist);
196
+ }
197
+ calcColumns(columnsClo, columnsCalc);
198
+
199
+ var columnAlign = columnsCalc.map((d) => d.align || "left"); // 对齐方式
200
+ var columnType = columnsCalc.map((d) =>
201
+ d.align == "right" || d.align == "rightNum" ? "n" : "s",
202
+ ); // 数据类型
203
+ var columnWidth = columnsCalc.map((d) =>
204
+ d.excelWidth ? d.excelWidth : d.width ? Math.floor(d.width / 10) : 10,
205
+ ); // 列宽
206
+ var numberFormat = columnsCalc.map((d) => {
207
+ return d.numberFormat === undefined
208
+ ? d.align == "rightNum"
209
+ ? "#,##0.0000"
210
+ : d.align == "right"
211
+ ? "#,##0.00"
212
+ : ""
213
+ : d.numberFormat;
214
+ }); // 格式化信息
215
+ columnsCalc.map((d, idx) => {
216
+ d.field = d.field ? d.field : d.key;
217
+ var t = d.excelName || d.headerName || d.title;
218
+ t = t.replace(/<br\/>/g, "");
219
+ if (d.columnWidth !== undefined) {
220
+ columnWidth[idx] = d.columnWidth;
221
+ } else {
222
+ if (t && columnWidth[idx] < t.length * 2) {
223
+ columnWidth[idx] = t.length * 2;
224
+ }
225
+ }
226
+ });
227
+ var exportData = datasClo.map((d) => {
228
+ return columnsCalc.map((dd, idx) => {
229
+ if (dd.columnWidth !== undefined) {
230
+ columnWidth[idx] = dd.columnWidth;
231
+ } else {
232
+ if (d[dd.field] && columnWidth[idx] < ("" + d[dd.field]).length * 2) {
233
+ columnWidth[idx] = ("" + d[dd.field]).length * 2;
234
+ }
235
+ }
236
+ let cellStyle = null;
237
+ if (dd.cellStyle) {
238
+ if (dd.cellStyle instanceof Function) {
239
+ cellStyle = dd.cellStyle(d[dd.field], d, datasClo);
240
+ } else {
241
+ cellStyle = dd.cellStyle;
242
+ }
243
+ }
244
+
245
+ let formula = null;
246
+ if (dd.formula) {
247
+ if (dd.formula instanceof Function) {
248
+ formula = dd.formula(d[dd.field], d, datasClo);
249
+ } else {
250
+ formula = dd.formula;
251
+ }
252
+ }
253
+
254
+ return {
255
+ cellStyle,
256
+ formula,
257
+ value: d[dd.field],
258
+ };
259
+ });
260
+ });
261
+
262
+ return {
263
+ deepth,
264
+ columnAlign,
265
+ columnType,
266
+ columnWidth,
267
+ exportData,
268
+ numberFormat,
269
+ columnTitle,
270
+ columnsCalc,
271
+ };
272
+ }
273
+
274
+ var rowSpanExcelInfos = [];
275
+ // 初始化表格跨行
276
+ function initRowSpanInfos(options) {
277
+ // 配置了单元格表格跨行的情况才执行以下计算
278
+ if (!!options.rowSpanColumns) {
279
+ if (!!options.rowSpanIndexCol) {
280
+ options.rowSpanIndexCol = options.rowSpanColumns[0];
281
+ }
282
+
283
+ // 按列计算的合并结果
284
+ var rowSpanRlt = {};
285
+
286
+ var rowTmpMapping = {};
287
+
288
+ // 计算每一列的合并数据
289
+ function calcRowSpan(colId, followCol) {
290
+ // 用于excel导出计算
291
+ var colIdx = _.findIndex(options.columns, { field: colId });
292
+
293
+ // 已经计算过了
294
+ if (!!rowSpanRlt[colId]) {
295
+ return;
296
+ }
297
+
298
+ // 每一行数据的计算结果缓存变量
299
+ var rowSpansTmp = [];
300
+
301
+ // 遍历数据的临时变量
302
+ var curVal = "CUR_VAL";
303
+
304
+ // 当前值对应的缓存变量的第一行数据
305
+ var firstCurRow = {};
306
+
307
+ _.forEach(options.datas, function (d, idx) {
308
+ // 如果是首次计算跨行的主列,正常判断首行,否则要参考主列计算的值进行判断
309
+ var isFirstRow = !followCol
310
+ ? d[colId] != curVal
311
+ : d[colId] != curVal || idx == (rowTmpMapping["" + idx] || {}).first;
312
+ if (isFirstRow) {
313
+ // 如果是首次计算跨行的主列,需要缓存数据每一行的计算信息
314
+ if (!followCol && firstCurRow.first !== undefined) {
315
+ for (var j = firstCurRow.first; j <= firstCurRow.last; j++) {
316
+ rowTmpMapping["" + j] = {
317
+ first: firstCurRow.first,
318
+ last: firstCurRow.last,
319
+ };
320
+ }
321
+ }
322
+ curVal = d[colId];
323
+ firstCurRow = {
324
+ col: colId,
325
+ val: curVal,
326
+ first: idx,
327
+ last: idx,
328
+ rowSpan: 1,
329
+ };
330
+ rowSpansTmp.push(firstCurRow);
331
+ } else {
332
+ // 合并行
333
+ firstCurRow.last = idx;
334
+ // rowSpan大于1表示此单元格要跨行
335
+ firstCurRow.rowSpan = firstCurRow.last - firstCurRow.first + 1;
336
+ // rowspan 0表示要隐藏的cell
337
+ rowSpansTmp.push({ rowSpan: 0 });
338
+
339
+ // 最后一行没有放入firstCurRow数据里面,导致最后一个值的合并出了问题
340
+ if (!followCol && idx == options.datas.length - 1) {
341
+ for (var j = firstCurRow.first; j <= firstCurRow.last; j++) {
342
+ rowTmpMapping["" + j] = {
343
+ first: firstCurRow.first,
344
+ last: firstCurRow.last,
345
+ };
346
+ }
347
+ }
348
+ }
349
+ });
350
+ rowSpanRlt[colId] = rowSpansTmp;
351
+ _.forEach(rowSpansTmp, function (row, idx) {
352
+ if (row.rowSpan > 1) {
353
+ rowSpanExcelInfos.push({
354
+ mergeRowS: idx,
355
+ mergeRowE: idx + row.rowSpan - 1,
356
+ megerColS: colIdx,
357
+ megerColE: colIdx,
358
+ });
359
+ }
360
+ });
361
+ }
362
+
363
+ calcRowSpan(options.rowSpanIndexCol);
364
+
365
+ // console.log(rowTmpMapping)
366
+
367
+ _.forEach(options.rowSpanColumns, function (col) {
368
+ calcRowSpan(col, true);
369
+ });
370
+ }
371
+ // console.log(rowSpanInfos)
372
+ }
373
+
374
+ /**
375
+ * 根据参数导出excel,目前系统仅支持agGrid和iviewTable,这两个表格的columns定义都是树形的,所以这里仅支持树形的列定义即可
376
+ * 根据每个column的树形信息,可以计算出每个column定义的单元格跨行跨列信息,直接得出excel待导出的表头
377
+ * 再根据列定义树形的叶子结点,可以得出每一列对应的字段名,根据这个信息组织待导出的数据
378
+ * 合并单元格需要单独配置,哪一列需要传参数进来,应该有一个主合并列,否则后面值全0的合并成一个了(直接拷贝slickgrid的吧)
379
+ * @param {
380
+ * title: '导出的excel名字,并excel内容的名字',
381
+ * columnAlign: ['center', 'left', 'right'], //'对齐方式'
382
+ * columnType: ['s', 'n', 'n'], // s String n Number
383
+ * columns: [] //'表头,列定义的树形结构',
384
+ * columnWidth: ['20','10'], //宽度,数组
385
+ * datas: [], // 数据,表格的原始数据
386
+ * paramLeft: '查询时间:2022年01月-2022年05月',
387
+ * paramRight: '箱、万元',
388
+ * numberFormat: ['', '0.00', '0.00'], // 数组,设置数据格式
389
+ * rowSpanColumns:[column1,column2……], // 需要跨行的列
390
+ * rowSpanIndexCol:'', // 根据哪一列进行跨行计算,如根据卷烟ITEM_NAME
391
+ * zeroValueFormat: '', // 导出时0值处理成何种格式的字符串
392
+ * } excelData
393
+ */
394
+ export function exportJsonToExcel(excelData) {
395
+ rowSpanExcelInfos = []; // 每次导出都重置该对象为初始值
396
+ var calcExportDatas = getAgColumnTitleAndData(excelData);
397
+
398
+ console.log("calcExportDatas", calcExportDatas);
399
+
400
+ var title = excelData.title;
401
+ var subTitle = excelData.subTitle || "";
402
+
403
+ var titleDeepth = calcExportDatas.deepth;
404
+ var columnAlign = excelData.columnAlign || calcExportDatas.columnAlign;
405
+ var columnType = excelData.columnType || calcExportDatas.columnType;
406
+
407
+ var columnTitle = calcExportDatas.columnTitle;
408
+
409
+ var columnWidth = excelData.columnWidth || calcExportDatas.columnWidth;
410
+
411
+ var columnCount = columnAlign.length;
412
+
413
+ var paramLeft = excelData.paramLeft;
414
+
415
+ var paramRight = excelData.paramRight;
416
+
417
+ var jsonData = calcExportDatas.exportData;
418
+
419
+ var numberFormat = excelData.numberFormat || calcExportDatas.numberFormat;
420
+
421
+ XlsxPopulate.fromBlankAsync()
422
+ .then((workbook) => {
423
+ // Modify the workbook.
424
+ var sheet = workbook.sheet("Sheet1"); // 初始化的时候不能传中文进去
425
+ sheet.name(title);
426
+ // 设置列宽
427
+ for (var i = 0; i < columnCount; i++) {
428
+ sheet.column(i + 1).width(columnWidth[i] == 0 ? 10 : columnWidth[i]);
429
+ }
430
+
431
+ var titleRange = sheet.range(1, 1, 1, columnCount);
432
+ titleRange.merged(true);
433
+ titleRange.style(excelData.titleStyle || titleStyle);
434
+ titleRange.cell(0, 0).value(title);
435
+ sheet.row(1).height(titleHeight);
436
+
437
+ var rowsNow = 2;
438
+ // 添加次标题
439
+ if (subTitle) {
440
+ var subTitleRow = 2; // 次标题位于标题下方,所以行号为2
441
+ var subTitleRange = sheet.range(
442
+ subTitleRow,
443
+ 1,
444
+ subTitleRow,
445
+ columnCount,
446
+ );
447
+ subTitleRange.merged(true); // 合并次标题单元格
448
+ subTitleRange.style(subTitleStyle || {}); // 应用次标题样式,需预先定义
449
+ subTitleRange.cell(0, 0).value(subTitle); // 设置次标题文本
450
+ sheet.row(subTitleRow).height(subTitleHeight); // 设置次标题行高,需预先定义
451
+ rowsNow = subTitleRow + 1; // 更新当前处理的行号
452
+ }
453
+ //自定义n多个副标题
454
+ if (excelData.subTitleList) {
455
+ excelData.subTitleList.forEach((d) => {
456
+ var subTitleRow = rowsNow; // 次标题位于标题下方,所以行号为2
457
+ var subTitleRange = sheet.range(
458
+ subTitleRow,
459
+ 1,
460
+ subTitleRow,
461
+ columnCount,
462
+ );
463
+ subTitleRange.merged(true); // 合并次标题单元格
464
+ subTitleRange.style(excelData.subTitleListStyle || subTitleStyle); // 应用次标题样式,需预先定义
465
+ subTitleRange.cell(0, 0).value(d); // 设置次标题文本
466
+ sheet.row(subTitleRow).height(subTitleHeight); // 设置次标题行高,需预先定义
467
+ rowsNow = subTitleRow + 1; // 更新当前处理的行号
468
+ });
469
+ }
470
+
471
+ if (!!excelData.paramLeft || !!excelData.paramRight) {
472
+ var paramLeftRange = sheet.range(2, 1, 2, Math.round(columnCount / 2));
473
+ paramLeftRange.cell(0, 0).value(paramLeft || "");
474
+ paramLeftRange.merged(true);
475
+ paramLeftRange.style(paramLeftStyle);
476
+ var paramRightRange = sheet.range(
477
+ 2,
478
+ Math.round(columnCount / 2) + 1,
479
+ 2,
480
+ columnCount,
481
+ );
482
+ paramRightRange.cell(0, 0).value(paramRight || "");
483
+ paramRightRange.merged(true);
484
+ paramRightRange.style(paramRightStyle);
485
+ sheet.row(2).height(paramHeight);
486
+ rowsNow += 1;
487
+ }
488
+
489
+ columnTitle.forEach((d) => {
490
+ var columnTitleRange = sheet.range(
491
+ d.srow + rowsNow - 1,
492
+ d.scol,
493
+ d.erow + rowsNow - 1,
494
+ d.ecol,
495
+ );
496
+ columnTitleRange.merged(true);
497
+ columnTitleRange.style(columnTitleStyle);
498
+ columnTitleRange.cell(0, 0).value(d.text.replace(/<br\/>/g, ""));
499
+ });
500
+ for (let i = 0; i < titleDeepth; i++) {
501
+ sheet.row(rowsNow + i).height(columnTitleHeight);
502
+ }
503
+
504
+ // 处理表格里的数据,挨家挨户搜一下
505
+ rowsNow += titleDeepth;
506
+
507
+ for (var i = 0; i < jsonData.length; i++) {
508
+ var lineData = jsonData[i];
509
+ for (var j = 0; j < lineData.length; j++) {
510
+ if (columnType[j] === "s" && lineData[j]) {
511
+ if (lineData[j].value === "0") {
512
+ // 销售业务需要在一些业务场景中,将零值导出为配置的zeroValueFormat格式
513
+ sheet
514
+ .cell(rowsNow + i, j + 1)
515
+ .value(
516
+ excelData.zeroValueFormat === undefined
517
+ ? "0"
518
+ : excelData.zeroValueFormat,
519
+ );
520
+ } else {
521
+ sheet.cell(rowsNow + i, j + 1).value(lineData[j].value);
522
+ }
523
+ dataCellStyle.numberFormat = "";
524
+ } else if (columnType[j] === "n" && lineData[j]) {
525
+ // 销售业务需要在一些业务场景中,将零值导出为配置的zeroValueFormat格式
526
+ if (lineData[j].value === 0) {
527
+ sheet
528
+ .cell(rowsNow + i, j + 1)
529
+ .value(
530
+ excelData.zeroValueFormat === undefined
531
+ ? 0
532
+ : excelData.zeroValueFormat,
533
+ );
534
+ } else {
535
+ sheet
536
+ .cell(rowsNow + i, j + 1)
537
+ .value(
538
+ isNaN(lineData[j].value)
539
+ ? excelData.zeroValueFormat === undefined
540
+ ? 0
541
+ : excelData.zeroValueFormat
542
+ : lineData[j].value === null ? '' : parseFloat(lineData[j].value),
543
+ );
544
+ }
545
+ dataCellStyle.numberFormat = numberFormat[j]; // 数字保留精度
546
+ } else {
547
+ // 预留一下,将来可能加别的样式
548
+ sheet.cell(rowsNow + i, j + 1).value("");
549
+ }
550
+
551
+ // 公式
552
+ if (lineData[j].formula) {
553
+ sheet.cell(rowsNow + i, j + 1).formula(lineData[j].formula);
554
+ }
555
+
556
+ // 样式
557
+ dataCellStyle.horizontalAlignment = columnAlign[j];
558
+ if (lineData[j].cellStyle) {
559
+ let cellStyle = _.merge({}, dataCellStyle, lineData[j].cellStyle);
560
+ sheet.cell(rowsNow + i, j + 1).style(cellStyle);
561
+ } else {
562
+ sheet.cell(rowsNow + i, j + 1).style(dataCellStyle);
563
+ }
564
+ }
565
+ if (excelData.rowColor && i % 2 != 0) {
566
+ var row = sheet.range(rowsNow + i, 1, rowsNow + i, columnCount);
567
+ row.style({ fill: "f8f8f9" });
568
+ }
569
+ sheet.row(rowsNow + i).height(dataRowHeight);
570
+ }
571
+
572
+ if (!!excelData.rowSpanColumns) {
573
+ initRowSpanInfos({
574
+ ...excelData,
575
+ columns: calcExportDatas.columnsCalc,
576
+ });
577
+ _.forEach(rowSpanExcelInfos, function (rowSpanInfo) {
578
+ var columnTitleRange = sheet.range(
579
+ rowSpanInfo.mergeRowS + rowsNow,
580
+ rowSpanInfo.megerColS + 1,
581
+ rowSpanInfo.mergeRowE + rowsNow,
582
+ rowSpanInfo.megerColE + 1,
583
+ );
584
+ columnTitleRange.merged(true);
585
+ });
586
+ }
587
+
588
+ // 表头无论如何都会有,都要固定,所以不需要加if判断
589
+ // if (excelData.leftColumns || excelData.topRows) {
590
+ sheet.freezePanes(
591
+ excelData.leftColumns || 0,
592
+ (excelData.topRows || 0) + rowsNow - 1,
593
+ );
594
+ // }
595
+
596
+ // Write to file.
597
+ workbook.outputAsync().then(function (blob) {
598
+ if (window.navigator && window.navigator.msSaveOrOpenBlob) {
599
+ // If IE, you must uses a different method.
600
+ window.navigator.msSaveOrOpenBlob(blob, title + ".xlsx");
601
+ } else {
602
+ var url = window.URL.createObjectURL(blob);
603
+ var a = document.createElement("a");
604
+ document.body.appendChild(a);
605
+ a.href = url;
606
+ a.download = title + ".xlsx";
607
+ a.click();
608
+ window.URL.revokeObjectURL(url);
609
+ document.body.removeChild(a);
610
+ }
611
+ });
612
+ })
613
+ .catch((err) => console.log(err));
614
+ }
615
+
616
+ /**
617
+ * 根据参数导出excel,目前系统仅支持agGrid和iviewTable,这两个表格的columns定义都是树形的,所以这里仅支持树形的列定义即可
618
+ * 根据每个column的树形信息,可以计算出每个column定义的单元格跨行跨列信息,直接得出excel待导出的表头
619
+ * 再根据列定义树形的叶子结点,可以得出每一列对应的字段名,根据这个信息组织待导出的数据
620
+ * 合并单元格需要单独配置,哪一列需要传参数进来,应该有一个主合并列,否则后面值全0的合并成一个了(直接拷贝slickgrid的吧)
621
+ * @param {
622
+ * title: '导出的excel名字,并excel内容的名字',
623
+ * noTitle: Boolean, // 读取数据的时候,忽略标题行
624
+ * columnAlign: ['center', 'left', 'right'], //'对齐方式'
625
+ * columnType: ['s', 'n', 'n'], // s String n Number
626
+ * columns: [] //'表头,列定义的树形结构',
627
+ * columnWidth: ['20','10'], //宽度,数组
628
+ * datas: [], // 数据,表格的原始数据
629
+ * paramLeft: '查询时间:2022年01月-2022年05月',
630
+ * paramRight: '箱、万元',
631
+ * numberFormat: ['', '0.00', '0.00'], // 数组,设置数据格式
632
+ * rowSpanColumns:[column1,column2……], // 需要跨行的列
633
+ * rowSpanIndexCol:'', // 根据哪一列进行跨行计算,如根据卷烟ITEM_NAME
634
+ * } excelData
635
+ */
636
+ export function importJsonFromExcel(excelData) {
637
+ return new Promise((resolve, reject) => {
638
+ var calcExportDatas = getAgColumnTitleAndData(excelData);
639
+
640
+ var titleDeepth = calcExportDatas.deepth;
641
+
642
+ var jsonData = calcExportDatas.exportData;
643
+
644
+ const reader = new FileReader();
645
+ reader.onload = async (event) => {
646
+ XlsxPopulate.fromDataAsync(event.target.result)
647
+ .then((workbook) => {
648
+ // Modify the workbook.
649
+ var sheet = workbook.sheet(0);
650
+
651
+ var rowsNow = 2; // 默认有标题行
652
+ if (excelData.noTitle) {
653
+ // 支持不要标题行
654
+ rowsNow = 1;
655
+ }
656
+
657
+ if (!!excelData.paramLeft || !!excelData.paramRight) {
658
+ rowsNow += 1;
659
+ }
660
+
661
+ // 处理表格里的数据,挨家挨户搜一下
662
+ rowsNow += titleDeepth;
663
+
664
+ let resutJson = [];
665
+ // for (var i = 0; i < jsonData.length; i++) {
666
+ for (let i = rowsNow; i < sheet._rows.length; i++) {
667
+ var lineData = sheet.row(i);
668
+ let tmpRow = {};
669
+ resutJson.push(tmpRow);
670
+ // excel列数据 这个索引从1开始
671
+ for (let j = 1; j < lineData._cells.length; j++) {
672
+ let val = lineData._cells[j] && lineData._cells[j]._value;
673
+ if (j > calcExportDatas.columnsCalc.length) {
674
+ continue;
675
+ }
676
+ let col = calcExportDatas.columnsCalc[j - 1];
677
+ tmpRow[col.field] = val;
678
+ // excelData.datas[i][col.field] = val
679
+ }
680
+ }
681
+
682
+ resolve(resutJson);
683
+ })
684
+ .catch((err) => console.log(err));
685
+ };
686
+
687
+ reader.readAsArrayBuffer(excelData.file);
688
+ });
689
+ }
690
+
691
+ export function importJsonFromLegacyExcel(excelData) {
692
+ console.log("正使用兼容模式处理 Excel 文件");
693
+ return new Promise((resolve, reject) => {
694
+ var calcExportDatas = getAgColumnTitleAndData(excelData);
695
+
696
+ var titleDeepth = calcExportDatas.deepth;
697
+
698
+ var jsonData = calcExportDatas.exportData;
699
+
700
+ const reader = new FileReader();
701
+ reader.onload = async (event) => {
702
+ try {
703
+ // 1. 使用 xlsx 库读取数据
704
+ const workbook = XLSX.read(event.target.result, { type: "buffer" });
705
+ const firstSheetName = workbook.SheetNames[0];
706
+ const worksheet = workbook.Sheets[firstSheetName];
707
+
708
+ // 2. 计算数据起始行(兼容原逻辑)
709
+ let dataStartRow = excelData.noTitle ? 0 : 1; // xlsx 行号从0开始
710
+ if (!!excelData.paramLeft || !!excelData.paramRight) {
711
+ dataStartRow += 1;
712
+ }
713
+ dataStartRow += calcExportDatas.deepth;
714
+
715
+ // 3. 转换数据为JSON格式
716
+ const jsonData = XLSX.utils.sheet_to_json(worksheet, {
717
+ header: 1, // 获取原始数组数据
718
+ range: dataStartRow, // 从数据区开始读取
719
+ raw: true, // 保持原始数据格式
720
+ });
721
+
722
+ // 4. 映射列字段
723
+ const result = jsonData.map((row) => {
724
+ return calcExportDatas.columnsCalc.reduce((obj, col, index) => {
725
+ obj[col.field] = row[index] || null;
726
+ return obj;
727
+ }, {});
728
+ });
729
+
730
+ resolve(result);
731
+ } catch (err) {
732
+ console.log("reader.onload= ~ err:", err);
733
+ reject(err);
734
+ }
735
+ };
736
+
737
+ reader.readAsArrayBuffer(excelData.file);
738
+ });
739
+ }
740
+
741
+ const ENGLISH_LETTER = [
742
+ "A",
743
+ "B",
744
+ "C",
745
+ "D",
746
+ "E",
747
+ "F",
748
+ "G",
749
+ "H",
750
+ "I",
751
+ "J",
752
+ "K",
753
+ "L",
754
+ "M",
755
+ "N",
756
+ "O",
757
+ "P",
758
+ "Q",
759
+ "R",
760
+ "S",
761
+ "T",
762
+ "U",
763
+ "V",
764
+ "W",
765
+ "X",
766
+ "Y",
767
+ "Z",
768
+ ];
769
+
770
+ /**
771
+ * 根据列的数组下标返回excel对应的列号 (目前是按照.xlsx的定义计算的)
772
+ *
773
+ * @param {number} number 列的下标,从0开始计数
774
+ * @returns excel里的列号
775
+ */
776
+ export function getExcelColumnIdx(number) {
777
+ if (!number) return "";
778
+
779
+ let rlt = "";
780
+ const length = ENGLISH_LETTER.length;
781
+
782
+ while (number >= 0) {
783
+ const idx = number % length;
784
+ rlt = ENGLISH_LETTER[idx] + rlt;
785
+
786
+ number = parseInt((number - idx) / length) - 1;
787
+ }
788
+ return rlt;
789
+ }