cloud-web-corejs 1.1.0-dev.20 → 1.1.0-dev.22

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 (27) hide show
  1. package/package.json +1 -1
  2. package/src/components/excelExport/conditionExportConfig.js +112 -0
  3. package/src/components/excelExport/exportType.js +8 -0
  4. package/src/components/excelExport/index.vue +7 -1
  5. package/src/components/excelExport/mixins.js +410 -259
  6. package/src/components/xform/form-designer/designer.js +0 -15
  7. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +2 -2
  8. package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +2 -1
  9. package/src/components/xform/form-designer/form-widget/field-widget/table-export-button-widget.vue +2 -1
  10. package/src/components/xform/form-designer/setting-panel/form-dynamicField-setting.vue +163 -136
  11. package/src/components/xform/form-designer/setting-panel/form-setting.vue +74 -9
  12. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/empty-number-input.vue +51 -0
  13. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +27 -2
  14. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/table-export-button-editor.vue +33 -3
  15. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +6 -0
  16. package/src/components/xform/form-render/container-item/data-table-mixin.js +6 -6
  17. package/src/components/xform/form-render/container-item/dynamicFieldMixin.js +4 -4
  18. package/src/components/xform/form-render/container-item/tab-item.vue +1 -1
  19. package/src/components/xform/form-render/fieldControlEngine.js +18 -0
  20. package/src/components/xform/form-render/fieldControlMixin.js +39 -29
  21. package/src/components/xform/form-render/index.vue +7 -4
  22. package/src/components/xform/form-render/indexMixin.js +26 -15
  23. package/src/components/xform/utils/util.js +0 -1
  24. package/src/views/user/home/default.vue +6 -3
  25. package/src/views/user/home/wjl/content.vue +3 -2
  26. package/src/components/xform/form-render/container-item/dynamicFieldEngine.js +0 -285
  27. package/src/components/xform/form-render/formDynamicFieldMixin.js +0 -133
@@ -5,9 +5,19 @@ import { getToken } from "../../utils/auth";
5
5
  import indexUtil from "../../utils/index.js";
6
6
  import exportFieldDialog from "./exportFieldDialog.vue";
7
7
  import { getCellValue } from "@base/components/table/util/index";
8
- import { isItemExport, isRowExport, requiresCheckedRows } from "./exportType";
9
- import { getExportItemColumns } from "./exportItemConfigUtil";
10
- import { viewKey } from "@base/utils/repeatOpen";
8
+ import {
9
+ isConditionExport,
10
+ isItemExport,
11
+ isRowExport,
12
+ requiresCheckedRows,
13
+ } from "./exportType";
14
+ import { getExportItemColumns } from "./exportItemConfigUtil";
15
+ import { viewKey } from "@base/utils/repeatOpen";
16
+ import {
17
+ DEFAULT_CONDITION_EXPORT_CONFIG,
18
+ loadConditionExportConfig,
19
+ resolveConditionExportConfig,
20
+ } from "./conditionExportConfig";
11
21
 
12
22
  let configUtil = {
13
23
  baseUrl: process.env.VUE_APP_BASE_API,
@@ -130,8 +140,14 @@ function getGrid(that, tableRef) {
130
140
  showExportFieldDialog: false,
131
141
  leafColumns: [],
132
142
  columns: [],
133
- queryParam: {},
134
- isMinimize: false,
143
+ queryParam: {},
144
+ isMinimize: false,
145
+ // 条件导出在本次任务内使用的配置快照。逻辑参数只在开始时读取一次,避免中途变更
146
+ // 导致前后页使用不同的分页/分卷口径。
147
+ conditionExportMode: false,
148
+ conditionExportConfig: { ...DEFAULT_CONDITION_EXPORT_CONFIG },
149
+ // 不查总数时,处理过程中分母显示「...」;文件合并成功后才回填实际完成数。
150
+ totalSizeKnown: true,
135
151
  // 定时器必须放实例上:模块级共享会导致并发导出互相清除对方的定时器
136
152
  exportTimer: null,
137
153
  countTimer: null,
@@ -146,9 +162,14 @@ function getGrid(that, tableRef) {
146
162
  // $route.path」现查,用户在导出期间切了页签(弹框可最小化,切走完全合法)
147
163
  // 解的就是另一把锁 —— 原页签会永久 affix,而 tagsView 的 delOthersViews /
148
164
  // delAllViews 都保留 affix,「关闭其他」「全部关闭」也删不掉它,只能刷新页面。
149
- lockedView: null,
150
- };
151
- },
165
+ lockedView: null,
166
+ };
167
+ },
168
+ computed: {
169
+ exportTotalText() {
170
+ return this.totalSizeKnown ? this.tTotalSize : "...";
171
+ },
172
+ },
152
173
  created() {},
153
174
  mounted() {
154
175
  // this.exc();
@@ -256,13 +277,29 @@ function getGrid(that, tableRef) {
256
277
  this.param.destroyComponent(loadingObj);
257
278
  }, 0);
258
279
  },
259
- dialogPrimary2() {
280
+ dialogPrimary2() {
260
281
  // this.showUserDialog=false
261
282
  this.$emit("confirm");
262
283
  this.$emit("close");
263
- this.clearExportTimer();
264
- },
265
- startProcess() {
284
+ this.clearExportTimer();
285
+ },
286
+ /**
287
+ * 读取并固化本次条件导出的全局配置。单次按钮/脚本参数已经在 option 中完成合并,
288
+ * resolveConditionExportConfig 会让它们覆盖同名的全局配置。
289
+ */
290
+ prepareConditionExportConfig() {
291
+ this.conditionExportMode = isConditionExport(this.option.type);
292
+ this.totalSizeKnown = true;
293
+ if (!this.conditionExportMode) return Promise.resolve();
294
+ return loadConditionExportConfig().then((globalConfig) => {
295
+ this.conditionExportConfig = resolveConditionExportConfig(
296
+ globalConfig,
297
+ this.option
298
+ );
299
+ this.totalSizeKnown = this.conditionExportConfig.searchCount;
300
+ });
301
+ },
302
+ startProcess() {
266
303
  this.lockMenu();
267
304
  this.createCountTimer();
268
305
  this.tableTarget = getGrid(this.option.vue, this.option.targetRef);
@@ -296,47 +333,62 @@ function getGrid(that, tableRef) {
296
333
  this.$t1("无法确定导出服务前缀,请在导出配置中指定 prefix")
297
334
  );
298
335
  return;
299
- }
300
- this.CURRENT_PREFIX = options.prefix;
301
- // 取数走 originOption.exportAjax 时请求由 xform 侧构造,前端拿不到那个 config,
302
- // 只能把令牌顺着导出参数带过去(data-table-mixin 的 exportAjax 会塞进
303
- // customParam.config,最终由 formHttp 的 ...opts 透传给 axios)
304
- options.cancelToken = this.getCancelToken();
305
-
306
- this.title = title;
307
- that.tTotalPage = 0;
308
- that.tTotalSize = 0;
309
- that
310
- .createExcelFile()
311
- .then((resultMsg) => {
312
- if (that.isExportStopped()) return;
313
- if (resultMsg.type === "success") {
314
- let aObj = resultMsg.objx;
315
- that.uuid = aObj.uuid;
316
- that.nowDate = aObj.nowDate;
317
- that.ippaaapp = aObj.ippaaapp;
318
- setTimeout(function () {
319
- /*num = 1;
320
- loopToDo(0, num);*/
321
- if (that.isExportStopped()) return;
322
- that.handleLoopToDo();
323
- }, 300);
324
- } else {
325
- that.abortExport();
326
- that.showImportDialog = false;
327
- that.showImportDialog2 = false;
328
- }
329
- })
330
- .catch((error) => {
331
- if (that.isExportStopped()) return;
332
- console.error(error);
333
- that.abortExport(that.$t1("创建导出文件失败,导出已中止"));
334
- that.showImportDialog = false;
335
- that.showImportDialog2 = false;
336
- });
337
- },
338
- getExportPageSize() {
339
- let pageSize = Math.min(this.option.pageSize || 1000, 1000);
336
+ }
337
+ this.CURRENT_PREFIX = options.prefix;
338
+ this.title = title;
339
+ that.tTotalPage = 0;
340
+ that.tTotalSize = 0;
341
+ that.prepareConditionExportConfig().then(() => {
342
+ if (that.isExportStopped()) return;
343
+ // 取数走 originOption.exportAjax 时请求由 xform 侧构造,前端拿不到那个 config,
344
+ // 只能把令牌顺着导出参数带过去(data-table-mixin 的 exportAjax 会塞进
345
+ // customParam.config,最终由 formHttp 的 ...opts 透传给 axios)
346
+ options.cancelToken = that.getCancelToken();
347
+ that
348
+ .createExcelFile()
349
+ .then((resultMsg) => {
350
+ if (that.isExportStopped()) return;
351
+ if (resultMsg.type === "success") {
352
+ let aObj = resultMsg.objx;
353
+ that.uuid = aObj.uuid;
354
+ that.nowDate = aObj.nowDate;
355
+ that.ippaaapp = aObj.ippaaapp;
356
+ setTimeout(function () {
357
+ if (that.isExportStopped()) return;
358
+ that.handleLoopToDo();
359
+ }, 300);
360
+ } else {
361
+ that.abortExport();
362
+ that.showImportDialog = false;
363
+ that.showImportDialog2 = false;
364
+ }
365
+ })
366
+ .catch((error) => {
367
+ if (that.isExportStopped()) return;
368
+ console.error(error);
369
+ that.abortExport(that.$t1("创建导出文件失败,导出已中止"));
370
+ that.showImportDialog = false;
371
+ that.showImportDialog2 = false;
372
+ });
373
+ });
374
+ },
375
+ getExportPageSize() {
376
+ let configuredPageSize = this.conditionExportMode
377
+ ? this.conditionExportConfig.pageSize
378
+ : this.option.pageSize || 1000;
379
+ // 条件导出的按钮/脚本值已经按优先级写进配置快照,不再用全局默认值 1000
380
+ // 反向封顶;非条件导出保持原有最多 1000 的行为。
381
+ let pageSize = this.conditionExportMode
382
+ ? configuredPageSize
383
+ : Math.min(configuredPageSize, 1000);
384
+ if (this.conditionExportMode) {
385
+ // 单页本身不能突破总量或单文件上限,否则无法在页级合并时兑现硬上限。
386
+ pageSize = Math.min(
387
+ pageSize,
388
+ this.conditionExportConfig.maxQueryTotal,
389
+ this.conditionExportConfig.limitFileSize
390
+ );
391
+ }
340
392
  let showImageAtTable = this.option.showImageAtTable || false;
341
393
  let hasExportImage = !!this.leafColumns.find((item) => {
342
394
  let exportType = item?.params?.exportType;
@@ -359,17 +411,19 @@ function getGrid(that, tableRef) {
359
411
  }
360
412
  return pageSize;
361
413
  },
362
- hadleMergeExcel(pPageSize) {
414
+ hadleMergeExcel(pPageSize) {
363
415
  if (this.isExportStopped()) return;
364
416
  let that = this;
365
417
  let title = this.title;
366
418
  let options = this.option;
367
419
  // let pPageSize = this.getExportPageSize();
368
- let limitFileSize = options.limitFileSize || 100000;
420
+ let limitFileSize = this.conditionExportMode
421
+ ? this.conditionExportConfig.limitFileSize
422
+ : options.limitFileSize || 100000;
369
423
  let limitThreadNum = options.limitThreadNum || 5;
370
424
  let totalPages = this.tTotalPage || 1;
371
425
 
372
- let kTotalPages = parseInt(limitFileSize / pPageSize);
426
+ let kTotalPages = Math.max(parseInt(limitFileSize / pPageSize), 1);
373
427
 
374
428
  let fileNum = parseInt(totalPages / kTotalPages);
375
429
  if (totalPages % kTotalPages !== 0) {
@@ -438,20 +492,195 @@ function getGrid(that, tableRef) {
438
492
  });
439
493
  // 注意:此处不能同步 clearExportTimer,否则计时统计会在合并完成前被提前停掉
440
494
  }
441
- };
442
- loopDo();
443
- },
444
- handleLoopToDo() {
445
- let that = this;
446
- let uuid;
447
- let num = 0;
448
- let title = this.title;
449
-
450
- let options = this.option;
451
-
452
- let pPageSize = this.getExportPageSize();
453
- let limitFileSize = options.limitFileSize || 100000;
454
- let limitThreadNum = options.limitThreadNum || 5;
495
+ };
496
+ loopDo();
497
+ },
498
+ /** 统一组装一页条件导出的取数请求,保证两种查总数模式使用完全相同的查询条件。 */
499
+ async requestExportPage(pageNumber, pageSize, searchCount) {
500
+ let options = this.option;
501
+ let originOption = this.tableTarget.params.originOption;
502
+ let url = originOption.exportPath || originOption.path;
503
+ let params = this.$baseLodash.cloneDeep(this.queryParam);
504
+ let aParam = {
505
+ current: pageNumber,
506
+ size: pageSize,
507
+ nowDate: this.nowDate ?? options.nowDate,
508
+ searchCount,
509
+ };
510
+ let cpParam;
511
+ if (originOption.vform === true) {
512
+ params.conditions = params.conditions || {};
513
+ cpParam = params.conditions;
514
+ } else {
515
+ cpParam = params;
516
+ }
517
+ Object.assign(cpParam, aParam);
518
+
519
+ if (originOption.exportAjax) {
520
+ return originOption.exportAjax(aParam, {
521
+ ...options,
522
+ exportQuerySnapshot: this.$baseLodash.cloneDeep(this.queryParam),
523
+ });
524
+ }
525
+ // URL 上若写死 searchCount,会与本次模式冲突;实际值统一由请求体控制。
526
+ let resolvedUrl = typeof url === "function" ? url() : url;
527
+ if (typeof resolvedUrl !== "string" || !resolvedUrl) {
528
+ throw new Error("无效的导出查询地址");
529
+ }
530
+ let requestUrl = resolvedUrl
531
+ .replace("searchCount=false", "1=1")
532
+ .replace("searchCount=true", "1=1");
533
+ let finalParams = params;
534
+ if (originOption.paramHandle) {
535
+ finalParams = originOption.paramHandle(params) || {};
536
+ }
537
+ return this.$commonHttp({
538
+ aes: originOption.aes || false,
539
+ url: requestUrl,
540
+ method: "post",
541
+ data: finalParams,
542
+ modal: false,
543
+ queryCreateInfo: originOption.queryCreateInfo,
544
+ addCreateInfo: originOption.addCreateInfo,
545
+ cancelToken: this.getCancelToken(),
546
+ });
547
+ },
548
+ /** 导出尚在取数/合并时最多显示 99%,100% 只由 completeExport 设置。 */
549
+ updateExportProgress(doneSize, progressTotal) {
550
+ this.doneSize = parseInt(doneSize);
551
+ let percentage = progressTotal > 0 ? (doneSize * 100) / progressTotal : 99;
552
+ if (percentage > 0 && percentage < 1) percentage = 1;
553
+ if (percentage >= 100) percentage = 99;
554
+ this.percentageNum = parseInt(percentage);
555
+ },
556
+ /**
557
+ * 不查询总数:每页都发 searchCount=false,以 records.length < pageSize 判定末页。
558
+ * 取数与写入按页串行,确保发现末页或达到最大查询总数后不会继续派发多余请求。
559
+ */
560
+ handleLoopWithoutCount(pageSize) {
561
+ let that = this;
562
+ let maxQueryTotal = this.conditionExportConfig.maxQueryTotal;
563
+ let maxPages = Math.max(Math.ceil(maxQueryTotal / pageSize), 1);
564
+ let doneSize = 0;
565
+ this.tTotalPage = 0;
566
+ this.tTotalSize = 0;
567
+ this.totalSizeKnown = false;
568
+
569
+ let finish = function (lastPage) {
570
+ if (that.isExportStopped()) return;
571
+ that.tTotalPage = Math.max(lastPage, 1);
572
+ that.hadleMergeExcel(pageSize);
573
+ };
574
+
575
+ let fetchPage = function (pageNumber, retryCount = 0) {
576
+ if (that.isExportStopped()) return Promise.resolve(null);
577
+ return that
578
+ .requestExportPage(pageNumber, pageSize, false)
579
+ .then((resultMsg) => {
580
+ if (resultMsg && resultMsg.type === "success") return resultMsg;
581
+ if (retryCount < 1) {
582
+ return new Promise((resolve) => {
583
+ setTimeout(
584
+ () => resolve(fetchPage(pageNumber, retryCount + 1)),
585
+ 500
586
+ );
587
+ });
588
+ }
589
+ that.abortExport();
590
+ return null;
591
+ })
592
+ .catch((error) => {
593
+ if (that.isExportStopped()) return null;
594
+ if (retryCount < 1) {
595
+ return new Promise((resolve) => {
596
+ setTimeout(
597
+ () => resolve(fetchPage(pageNumber, retryCount + 1)),
598
+ 500
599
+ );
600
+ });
601
+ }
602
+ console.error(error);
603
+ that.abortExport(
604
+ that.$t1("第{n}页数据拉取失败,导出已中止", {
605
+ n: pageNumber,
606
+ })
607
+ );
608
+ return null;
609
+ });
610
+ };
611
+
612
+ let appendPage = function (pageNumber) {
613
+ fetchPage(pageNumber).then((resultMsg) => {
614
+ if (!resultMsg || that.isExportStopped()) return;
615
+ let page = resultMsg.objx || {};
616
+ let actualSize = Number(page.size || page.pageSize || pageSize);
617
+ if (!Number.isSafeInteger(actualSize) || actualSize <= 0
618
+ || actualSize > pageSize || (pageNumber > 1 && actualSize !== pageSize)) {
619
+ that.abortExport(that.$t1("服务端分页大小发生变化,导出已中止"));
620
+ return;
621
+ }
622
+ pageSize = actualSize;
623
+ maxPages = Math.max(Math.ceil(maxQueryTotal / pageSize), 1);
624
+ let rawRows = Array.isArray(page.records) ? page.records : [];
625
+ let hasNext = rawRows.length >= pageSize;
626
+
627
+ // 整页数据需要再探测一页;探测到的空页不写入,也不参与合并。
628
+ if (pageNumber > 1 && rawRows.length === 0) {
629
+ finish(pageNumber - 1);
630
+ return;
631
+ }
632
+
633
+ let remaining = Math.max(maxQueryTotal - doneSize, 0);
634
+ let rows = rawRows.slice(0, remaining);
635
+ let reachedLimit = rows.length >= remaining;
636
+ page.current = pageNumber;
637
+ page.pageNumber = pageNumber;
638
+ page.size = pageSize;
639
+ page.pageSize = pageSize;
640
+ page.records = rows;
641
+
642
+ that
643
+ .addExcelData({ rows, pageNumber })
644
+ .then((appendResult) => {
645
+ if (that.isExportStopped()) return;
646
+ if (!appendResult || appendResult.type !== "success") {
647
+ if (appendResult && appendResult.type === "abort") return;
648
+ that.abortExport();
649
+ return;
650
+ }
651
+ doneSize += rows.length;
652
+ that.updateExportProgress(doneSize, maxQueryTotal);
653
+ if (reachedLimit || !hasNext || pageNumber >= maxPages) {
654
+ finish(pageNumber);
655
+ return;
656
+ }
657
+ appendPage(pageNumber + 1);
658
+ })
659
+ .catch((error) => {
660
+ if (that.isExportStopped()) return;
661
+ console.error(error);
662
+ that.abortExport(that.$t1("导出数据写入失败,导出已中止"));
663
+ });
664
+ });
665
+ };
666
+
667
+ appendPage(1);
668
+ },
669
+ handleLoopToDo() {
670
+ let that = this;
671
+ let num = 0;
672
+ let options = this.option;
673
+
674
+ let pPageSize = this.getExportPageSize();
675
+ let limitThreadNum = options.limitThreadNum || 5;
676
+
677
+ if (
678
+ this.conditionExportMode
679
+ && this.conditionExportConfig.searchCount === false
680
+ ) {
681
+ this.handleLoopWithoutCount(pPageSize);
682
+ return;
683
+ }
455
684
 
456
685
  /**处理返回值*/
457
686
  this.doneSize = 0;
@@ -478,39 +707,15 @@ function getGrid(that, tableRef) {
478
707
  rows: resultMsg.objx.records,
479
708
  pageNumber: pageNumber,
480
709
  })
481
- .then(function (resultMsg2) {
482
- releaseWindow();
483
- let data = resultMsg.objx;
484
- let total = data.total;
485
-
486
- let pageNumber = data.pageNumber || 1;
487
- let totalPages = that.tTotalPage || 1;
488
- if (resultMsg2.type === "success") {
489
- excedNum++;
490
- let pageNumber = data.pageNumber || 1;
491
- let k = (excedNum * 100) / totalPages;
492
- if (k > 0 && k < 1) {
493
- k = 1;
494
- }
495
- if (k > 100) k = 100;
496
- let cTotal = that.tTotalSize;
497
- let pageSize = data.pageSize;
498
- // let datas = data.records;
499
- let size = data.records.length;
500
- cSize = cSize + size;
501
-
502
- let ttNum = cSize + "/" + cTotal;
503
- let percentageStr = parseInt(k) + "%";
504
-
505
- let percentageNum = k;
506
- if (percentageNum >= 100) {
507
- percentageNum = 99;
508
- }
509
- // that.$set(that, "doneSize", parseInt(cSize));
510
- // that.$set(that, "percentageNum", parseInt(percentageNum));
511
-
512
- that.doneSize = parseInt(cSize);
513
- that.percentageNum = parseInt(percentageNum);
710
+ .then(function (resultMsg2) {
711
+ releaseWindow();
712
+ let data = resultMsg.objx;
713
+ let totalPages = that.tTotalPage || 1;
714
+ if (resultMsg2.type === "success") {
715
+ excedNum++;
716
+ let size = data.records.length;
717
+ cSize = cSize + size;
718
+ that.updateExportProgress(cSize, that.tTotalSize);
514
719
 
515
720
  if (totalPages === 0 || excedNum >= totalPages) {
516
721
  that.hadleMergeExcel(data.pageSize);
@@ -605,113 +810,49 @@ function getGrid(that, tableRef) {
605
810
  });
606
811
  };
607
812
 
608
- let waitReqNum = 0;
609
- let promise;
610
- let loopToDo = function (flag, pageNumber, retryCount = 0) {
611
- // 取消后不再发起新页,也不再重试(重试是从 .catch 里递归回来的)
612
- if (that.isExportStopped()) return;
613
- let maxWaitNum = limitThreadNum;
614
- // 分页取数的时间快照:createExcelTemplate 返回后存在 this.nowDate(见
615
- // startProcess),此前这里读的是 options.nowDate —— 那是调用方入参,全库
616
- // 没有任何调用方维护它,于是每页请求的 nowDate 恒为 undefined、被
617
- // JSON.stringify 丢掉,后端拿不到快照点。导出期间数据发生增删时,分页会
618
- // 整体前后错位,合出来的文件既漏行又重行,且全程不报错。
619
- // 仍保留 options.nowDate 兜底:服务端未返回快照时,调用方若显式指定了一个,
620
- // 用它总好过不带。
621
- // 两条取数通道都要带:通用表格随请求体平铺下发;xform exportAjax,快照
622
- // aParam 进去后由 data-table-mixin.loadDefaultQueryList 提到信封层(与
623
- // formCode 同级),不能留在内层 data —— 那是业务查询条件的地盘。
624
- let nowDate = that.nowDate ?? options.nowDate;
625
- if (flag === 1) {
626
- /*if (num > that.tTotalPage) {
627
- let size = page.records.length;
628
- if (size === 0 || page.totalPages === 0 || (page.pageNumber >= page.totalPages)) {
629
- that.clearExportTimer();
630
- return;
631
- }
632
- }*/
633
- } else {
634
- waitReqNum = 0;
635
- }
636
-
637
- let target = that.tableTarget;
638
- let originOption = target.params.originOption;
639
- let url = originOption.exportPath || originOption.path;
640
-
641
- // let params = originOption.param() || {};
642
- // 每页必须独立参数对象。此前所有页共享 that.queryParam 这一个对象,下面的
643
- // Object.assign current 写进去后,靠"派发间隔 100ms、微任务已 flush"侥幸
644
- // 不冲突;请求拦截器一旦变成异步(token 续期等),并发的两页会带上同一个
645
- // current —— 合并出的文件重复页 + 缺页,且全程不报错。
646
- let params = that.$baseLodash.cloneDeep(that.queryParam);
647
- let aParam = {
648
- current: pageNumber,
649
- size: pPageSize,
650
- nowDate: nowDate,
651
- searchCount: flag !== 1,
652
- };
653
- let cpParam;
654
- if (originOption.vform === true) {
655
- params.conditions = params.conditions || {};
656
- cpParam = params.conditions;
657
- } else {
658
- cpParam = params;
659
- }
660
- Object.assign(cpParam, aParam);
661
-
662
- if (!originOption.exportAjax) {
663
- let turl = url.replace("searchCount=false", "1=1");
664
- let finaParams = params;
665
- if (originOption.paramHandle) {
666
- finaParams = originOption.paramHandle
667
- ? originOption.paramHandle(params) || {}
668
- : {};
669
- }
670
- promise = that.$commonHttp({
671
- aes: originOption.aes || false,
672
- url: turl,
673
- method: "post",
674
- data: finaParams,
675
- modal: false,
676
- queryCreateInfo: originOption.queryCreateInfo,
677
- addCreateInfo: originOption.addCreateInfo,
678
- cancelToken: that.getCancelToken(),
679
- });
680
- } else {
681
- promise = originOption.exportAjax(aParam, options);
682
- }
683
-
684
- promise
685
- .then(function (resultMsg) {
686
- if (resultMsg.type === "success") {
687
- let objx = resultMsg.objx;
688
- objx.pageNumber = objx.current || 1;
689
- objx.totalPages = objx.pages || 1;
690
- objx.pageSize = objx.size;
691
- let size = objx.records.length;
692
- if (flag === 0) {
693
- that.tTotalPage = objx.pages;
694
- // that.tTotalSize = objx.total;
695
- setTimeout(function () {
696
- that.$set(that, "tTotalSize", objx.total);
697
- }, 0);
698
- let tTotalPage = objx.pages;
699
- let tTotalSize = objx.total;
700
- let ttNum = 0 + "/" + objx.total;
701
-
702
- let opNum = limitFileSize;
703
- let kTotalPages = parseInt(opNum / objx.size);
704
-
705
- let fileNum = parseInt(tTotalPage / kTotalPages);
706
- if (tTotalPage % kTotalPages !== 0) {
707
- fileNum = fileNum + 1;
708
- }
709
- if (
710
- size > 0 ||
711
- objx.totalPages > 0 ||
712
- objx.pageNumber < objx.totalPages
713
- ) {
714
- that.exportTimer = setInterval(function () {
813
+ let waitReqNum = 0;
814
+ let loopToDo = function (flag, pageNumber, retryCount = 0) {
815
+ // 取消后不再发起新页,也不再重试(重试是从 .catch 里递归回来的)
816
+ if (that.isExportStopped()) return;
817
+ let maxWaitNum = limitThreadNum;
818
+ if (flag !== 1) waitReqNum = 0;
819
+
820
+ that
821
+ .requestExportPage(pageNumber, pPageSize, flag !== 1)
822
+ .then(function (resultMsg) {
823
+ if (resultMsg.type === "success") {
824
+ let objx = resultMsg.objx || {};
825
+ objx.records = Array.isArray(objx.records) ? objx.records : [];
826
+ objx.pageNumber = objx.current || pageNumber;
827
+ objx.pageSize = objx.size || pPageSize;
828
+ let actualSize = Number(objx.pageSize);
829
+ if (!Number.isSafeInteger(actualSize) || actualSize <= 0
830
+ || actualSize > pPageSize || (flag !== 0 && actualSize !== pPageSize)) {
831
+ that.abortExport(that.$t1("服务端分页大小发生变化,导出已中止"));
832
+ return;
833
+ }
834
+ pPageSize = actualSize;
835
+ objx.pageSize = actualSize;
836
+ let size = objx.records.length;
837
+ if (flag === 0) {
838
+ let rawTotal = Number(objx.total);
839
+ if (!Number.isFinite(rawTotal) || rawTotal < 0) {
840
+ rawTotal = size;
841
+ }
842
+ let exportTotal = that.conditionExportMode
843
+ ? Math.min(
844
+ rawTotal,
845
+ that.conditionExportConfig.maxQueryTotal
846
+ )
847
+ : rawTotal;
848
+ that.tTotalPage = that.conditionExportMode
849
+ ? Math.max(Math.ceil(exportTotal / pPageSize), 1)
850
+ : objx.pages;
851
+ objx.totalPages = that.tTotalPage || 1;
852
+ that.tTotalSize = exportTotal;
853
+ let tTotalPage = that.tTotalPage || 1;
854
+ if (tTotalPage > 1) {
855
+ that.exportTimer = setInterval(function () {
715
856
  if (waitReqNum < maxWaitNum && num < tTotalPage) {
716
857
  waitReqNum++;
717
858
  num++;
@@ -726,9 +867,15 @@ function getGrid(that, tableRef) {
726
867
  }, 100);
727
868
  } else {
728
869
  clearInterval(that.exportTimer);
729
- that.exportTimer = null;
730
- }
731
- }
870
+ that.exportTimer = null;
871
+ }
872
+ }
873
+ if (that.conditionExportMode) {
874
+ // 总数超过有效上限时,最后一页只写允许范围内的数据。
875
+ let rowOffset = (pageNumber - 1) * pPageSize;
876
+ let allowedRows = Math.max(that.tTotalSize - rowOffset, 0);
877
+ objx.records = objx.records.slice(0, allowedRows);
878
+ }
732
879
  // 注意:此处不再释放并发窗口。拉取完成只是这一页做完了一半,
733
880
  // 窗口由 handleData 在上送(addExcelData)落地后释放,见那里的注释。
734
881
 
@@ -898,14 +1045,14 @@ function getGrid(that, tableRef) {
898
1045
  }
899
1046
  let cUUid = this.uuid;
900
1047
  let ippaaapp = this.ippaaapp;
901
- let url1 =
902
- this.CURRENT_PREFIX +
903
- "/excel/appendExcelData?uuid=" +
904
- cUUid +
905
- "&ippaaapp=" +
906
- ippaaapp +
907
- "&pageNumber=" +
908
- pageNumber;
1048
+ let url1
1049
+ = this.CURRENT_PREFIX
1050
+ + "/excel/appendExcelData?uuid="
1051
+ + cUUid
1052
+ + "&ippaaapp="
1053
+ + ippaaapp
1054
+ + "&pageNumber="
1055
+ + pageNumber;
909
1056
  return this.$http({
910
1057
  url: url1,
911
1058
  method: "post",
@@ -919,12 +1066,12 @@ function getGrid(that, tableRef) {
919
1066
  });
920
1067
  },
921
1068
  mergeExcel(startPage, endPage, loading) {
922
- let url1 =
923
- this.CURRENT_PREFIX +
924
- "/excel/mergeExcel?uuid=" +
925
- this.uuid +
926
- "&ippaaapp=" +
927
- this.ippaaapp;
1069
+ let url1
1070
+ = this.CURRENT_PREFIX
1071
+ + "/excel/mergeExcel?uuid="
1072
+ + this.uuid
1073
+ + "&ippaaapp="
1074
+ + this.ippaaapp;
928
1075
  return this.$http({
929
1076
  url: url1 + "&startPage=" + startPage + "&endPage=" + endPage,
930
1077
  method: "post",
@@ -954,8 +1101,12 @@ function getGrid(that, tableRef) {
954
1101
  * 合法的状态,那时页签必须能正常关闭 —— 解锁此前只挂在 beforeDestroy 上,
955
1102
  * 用户不点「取消/确定」就永远解不开,正是「导出完成后页签关不掉」的来源。
956
1103
  */
957
- completeExport() {
958
- setTimeout(() => {
1104
+ completeExport() {
1105
+ if (!this.totalSizeKnown) {
1106
+ this.tTotalSize = this.doneSize;
1107
+ this.totalSizeKnown = true;
1108
+ }
1109
+ setTimeout(() => {
959
1110
  if (this.isExportStopped()) return;
960
1111
  this.percentageNum = parseInt(100);
961
1112
  }, 100);
@@ -1011,10 +1162,10 @@ function getGrid(that, tableRef) {
1011
1162
  return column.slots.filterVal(params);
1012
1163
  }
1013
1164
  let slotDefault = column.slots && column.slots.default;
1014
- let hasRenderFn =
1015
- !!column.renderCell ||
1016
- typeof slotDefault === "function" ||
1017
- typeof slotDefault === "string";
1165
+ let hasRenderFn
1166
+ = !!column.renderCell
1167
+ || typeof slotDefault === "function"
1168
+ || typeof slotDefault === "string";
1018
1169
  if (!hasRenderFn) {
1019
1170
  let cellValue = params.row[column.field];
1020
1171
  if (typeof column.formatter === "function") {
@@ -1072,8 +1223,8 @@ function getGrid(that, tableRef) {
1072
1223
  // _columnIndex
1073
1224
  };
1074
1225
  let exportType = column.params?.exportType;
1075
- let exportVal =
1076
- column.params && column.params.exportVal
1226
+ let exportVal
1227
+ = column.params && column.params.exportVal
1077
1228
  ? column.params.exportVal
1078
1229
  : null;
1079
1230
  if (exportVal) {
@@ -1089,16 +1240,16 @@ function getGrid(that, tableRef) {
1089
1240
  } else if (exportType === "Number") {
1090
1241
  resultStr = that.getExportCellValue(params);
1091
1242
  if (
1092
- resultStr !== null &&
1093
- resultStr !== undefined &&
1094
- resultStr !== "" &&
1095
- !(resultStr + "").startsWith("[EXPNUM]")
1243
+ resultStr !== null
1244
+ && resultStr !== undefined
1245
+ && resultStr !== ""
1246
+ && !(resultStr + "").startsWith("[EXPNUM]")
1096
1247
  ) {
1097
1248
  resultStr = "[EXPNUM]" + resultStr;
1098
1249
  }
1099
1250
  } else if (
1100
- exportType === "Image" ||
1101
- (showImageAtTable && exportType === "Image2")
1251
+ exportType === "Image"
1252
+ || (showImageAtTable && exportType === "Image2")
1102
1253
  ) {
1103
1254
  // 取值刻意延到"确实用得上"的那条分支才做:附件的正常形态是数组,
1104
1255
  // 那条分支会用 row[column.field] 自己拼 [EXPIMG],把取值结果整个覆盖;
@@ -1111,9 +1262,9 @@ function getGrid(that, tableRef) {
1111
1262
  that.isPicture(item.extension)
1112
1263
  );
1113
1264
  if (items.length > 0) {
1114
- resultStr =
1115
- "[EXPIMG]" +
1116
- items
1265
+ resultStr
1266
+ = "[EXPIMG]"
1267
+ + items
1117
1268
  .map((item) => {
1118
1269
  let url = item.large || item.medium;
1119
1270
  return item.domain + url;
@@ -1131,10 +1282,10 @@ function getGrid(that, tableRef) {
1131
1282
  )
1132
1283
  ) {
1133
1284
  if (
1134
- resultStr !== null &&
1135
- resultStr !== undefined &&
1136
- resultStr !== "" &&
1137
- !(resultStr + "").startsWith("[EXPIMG]")
1285
+ resultStr !== null
1286
+ && resultStr !== undefined
1287
+ && resultStr !== ""
1288
+ && !(resultStr + "").startsWith("[EXPIMG]")
1138
1289
  ) {
1139
1290
  resultStr = "[EXPIMG]" + resultStr;
1140
1291
  }
@@ -1185,13 +1336,13 @@ function getGrid(that, tableRef) {
1185
1336
  let cUUid = encodeURIComponent(row.uuid);
1186
1337
  let ippaaapp = this.ippaaapp;
1187
1338
  let fileName = row.title;
1188
- let fileUrl =
1189
- configUtil.baseUrl +
1190
- this.CURRENT_PREFIX +
1191
- "/excel/download?uuid=" +
1192
- cUUid +
1193
- "&ippaaapp=" +
1194
- ippaaapp;
1339
+ let fileUrl
1340
+ = configUtil.baseUrl
1341
+ + this.CURRENT_PREFIX
1342
+ + "/excel/download?uuid="
1343
+ + cUUid
1344
+ + "&ippaaapp="
1345
+ + ippaaapp;
1195
1346
 
1196
1347
  function downLoadFile2() {
1197
1348
  let params = {
@@ -1201,8 +1352,8 @@ function getGrid(that, tableRef) {
1201
1352
 
1202
1353
  let form = document.createElement("form");
1203
1354
 
1204
- let randomNum =
1205
- new Date().valueOf() + Math.floor(Math.random() * 1000000);
1355
+ let randomNum
1356
+ = new Date().valueOf() + Math.floor(Math.random() * 1000000);
1206
1357
  let formId = "formId-" + randomNum;
1207
1358
  let formName = "formName-" + randomNum;
1208
1359
 
@@ -1287,8 +1438,8 @@ function getGrid(that, tableRef) {
1287
1438
  this.tableTarget = getGrid(this.option.vue, this.option.targetRef);
1288
1439
  let that = this;
1289
1440
  let options = this.option;
1290
- let title =
1291
- options.title || this.$t2("导出", "components.excelExport.title");
1441
+ let title
1442
+ = options.title || this.$t2("导出", "components.excelExport.title");
1292
1443
  if (!options.prefix && options.prefix !== "") {
1293
1444
  let originOption = this.tableTarget.params.originOption;
1294
1445
  let path = originOption.exportPath || originOption.path;