cloud-web-corejs 1.1.0-dev.17 → 1.1.0-dev.18

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cloud-web-corejs",
3
3
  "private": false,
4
- "version": "1.1.0-dev.17",
4
+ "version": "1.1.0-dev.18",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve",
7
7
  "dev:micro": "vue-cli-service serve --port 17527",
@@ -6,7 +6,7 @@
6
6
  v-if="showWrap"
7
7
  :close-on-click-modal="false"
8
8
  width="501px"
9
- custom-class="dialog-style tips-dialog error"
9
+ custom-class="dialog-style tips-dialog error "
10
10
  :append-to-body="true"
11
11
  :modal="false"
12
12
  @close="closeDialog"
@@ -39,7 +39,7 @@
39
39
  </el-dialog>
40
40
  </div>
41
41
  <div v-else-if="showWrap">
42
- <div class="pop-tipsBox error" :style="'display: block;z-index:' + zIndex">
42
+ <div class="pop-tipsBox error h5-pop-tips" :style="'display: block;z-index:' + zIndex">
43
43
  <div>
44
44
  <i class="icon"></i>
45
45
  <div class="cont">
@@ -91,7 +91,9 @@
91
91
  </div>
92
92
  <div v-show="!importLoading" class="import-box" style="margin-bottom: 30px;">
93
93
  <div class="tips">
94
- {{ $t2('导入中,请勿关闭当前窗口', 'components.excelImport.tip2') }}
94
+ {{ backendValidating
95
+ ? $t1('后台校验中,请勿关闭当前窗口')
96
+ : $t2('导入中,请勿关闭当前窗口', 'components.excelImport.tip2') }}
95
97
  <!-- <span class="fr">耗时:<em class="time-txt">0 时 0 分 0 秒 </em></span> -->
96
98
  </div>
97
99
  <el-progress :text-inside="true" :stroke-width="26" :percentage="percentageNum"
@@ -116,11 +118,14 @@
116
118
  /
117
119
  <span class="dataSize">{{ dataSize }}</span>
118
120
  </div>
119
- <el-button type="primary" plain class="button-sty" @click="dialogClose2" v-if="!param.importOption.hideCancelButton">
121
+ <el-button type="primary" plain class="button-sty" @click="dialogClose2"
122
+ :disabled="backendValidating"
123
+ v-if="!param.importOption.hideCancelButton">
120
124
  <i class="el-icon-close el-icon"></i>
121
125
  {{ $t2('取 消', 'system.button.cancel2') }}
122
126
  </el-button>
123
- <el-button type="primary" class="button-sty" @click="dialogPrimary2">
127
+ <el-button type="primary" class="button-sty" @click="dialogPrimary2"
128
+ :disabled="backendValidating">
124
129
  <i class="el-icon-check el-icon"></i>
125
130
  {{ $t2('确 定', 'system.button.confirm2') }}
126
131
  </el-button>
@@ -34,6 +34,13 @@ const IMPORT_META_FIELDS = [
34
34
  "_resultType",
35
35
  "_resultContent",
36
36
  ];
37
+ // 后台校验回包里的协议字段,合并补数时不能盖掉原行的序号
38
+ const BACKEND_VALIDATE_SKIP_FIELDS = [
39
+ "impSeq",
40
+ "impNumber",
41
+ "impReturnType",
42
+ "impReturnMsg",
43
+ ];
37
44
  // 一格里填多个附件的分隔符,与 attachmentValueUtil 的 URL_SEPARATOR_REG 同口径
38
45
  const IMPORT_ATTACH_SEPARATOR_REG = /[,\n\r]+/;
39
46
  // 可用的附件地址:带协议(或协议相对)的绝对 URL,或以 / 开头的存储路径
@@ -75,6 +82,7 @@ tmixins = {
75
82
  urlAttachFields: {},
76
83
  importLoading: false,
77
84
  loadingTitle: null,
85
+ backendValidating: false,
78
86
 
79
87
  percent1: 0,
80
88
  percent2: 0,
@@ -347,6 +355,12 @@ tmixins = {
347
355
  return;
348
356
  },
349
357
  handleBeforeClose(hide) {
358
+ if (this.backendValidating) {
359
+ this.isEnd = true;
360
+ this.backendValidating = false;
361
+ hide();
362
+ return;
363
+ }
350
364
  this.option.onCancel &&
351
365
  this.option.onCancel(this.getCallbackData(), this.fileRaw);
352
366
  let importOption = this.option.importOption;
@@ -359,6 +373,7 @@ tmixins = {
359
373
  hide();
360
374
  },
361
375
  dialogClose2() {
376
+ if (this.backendValidating) return;
362
377
  this.showImportDialog2 = false;
363
378
  this.option.onBeforeCancel &&
364
379
  this.option.onBeforeCancel(this.getCallbackData(), this.fileRaw);
@@ -371,6 +386,7 @@ tmixins = {
371
386
  this.handleTableCallback();
372
387
  },
373
388
  handleClose2() {
389
+ this.backendValidating = false;
374
390
  this.clearImportTimer();
375
391
  this.showImportDialog2 = false;
376
392
  this.showContent = false;
@@ -381,6 +397,7 @@ tmixins = {
381
397
  this.dialogClose1();
382
398
  },
383
399
  dialogPrimary2() {
400
+ if (this.backendValidating) return;
384
401
  let importOption = this.option.importOption;
385
402
  this.showImportDialog2 = false;
386
403
  this.option.onBeforeConfirm &&
@@ -1001,18 +1018,23 @@ tmixins = {
1001
1018
  * 与 syncDataForImport 的分工:后端通道有"批量"与"唯一键分组"两种发包形态,
1002
1019
  * 一步推进的行数不固定,循环仍留在那边;这里只服务"一行一步"的前端通道。
1003
1020
  *
1004
- * @param {Object} opts { rows, handler, onFinish }
1021
+ * @param {Object} opts { rows, handler, onFinish, progress, deferFinish }
1022
+ * progress 复用已有记账(后台校验后的第二段不能清掉前台失败行)
1023
+ * deferFinish 第一段只收集通过行时不收尾,等后台回来再 finish
1005
1024
  */
1006
1025
  runImportRows(opts) {
1007
1026
  let that = this;
1008
1027
  let rows = opts.rows || [];
1009
1028
  let handler = opts.handler;
1010
1029
  let size = rows.length;
1011
- let progress = this.createImportProgress(size);
1030
+ let progress = opts.progress || this.createImportProgress(size);
1031
+ this.importProgress = progress;
1012
1032
  let index = 0;
1013
1033
  let finish = () => {
1014
- progress.setPercent(100);
1015
- progress.finish();
1034
+ if (!opts.deferFinish) {
1035
+ progress.setPercent(100);
1036
+ progress.finish();
1037
+ }
1016
1038
  opts.onFinish && opts.onFinish(that.getCallbackData());
1017
1039
  };
1018
1040
  let step = () => {
@@ -1022,18 +1044,23 @@ tmixins = {
1022
1044
  return;
1023
1045
  }
1024
1046
  let row = rows[index];
1025
- row.impSeq = index;
1026
- row.impNumber = index + 4;
1047
+ // 第二段(后台补数后入表)行上已有 Excel 序号,不能按子集下标重写
1048
+ if (row.impSeq == null) {
1049
+ row.impSeq = index;
1050
+ row.impNumber = index + 4;
1051
+ }
1027
1052
  let settled = false;
1028
1053
  let done = (result) => {
1029
1054
  // handler 重复调 done 不能把进度推两格
1030
1055
  if (settled) return;
1031
1056
  settled = true;
1032
1057
  // done() 不传参直接算成功;只有显式 success:false 才记失败
1058
+ // done({ defer: true }) 只推进、不记账——前台校验通过、等后台结果时用
1033
1059
  let failed = !!(result && result.success === false);
1060
+ let deferred = !!(result && result.defer);
1034
1061
  if (failed) {
1035
1062
  progress.markError(row, result.message);
1036
- } else {
1063
+ } else if (!deferred) {
1037
1064
  progress.markSuccess(row, result && result.message);
1038
1065
  }
1039
1066
  index = index + 1;
@@ -1341,6 +1368,7 @@ tmixins = {
1341
1368
  },
1342
1369
  /** 导入无法继续时的统一收口:停循环、停计时器、给一次明确提示、关掉进度框。 */
1343
1370
  handleImportAbort(message) {
1371
+ this.backendValidating = false;
1344
1372
  this.isEnd = true;
1345
1373
  this.clearImportTimer();
1346
1374
  this.$baseAlert(message);
@@ -1562,23 +1590,72 @@ tmixins = {
1562
1590
  },
1563
1591
 
1564
1592
  /**
1565
- * 前端通道的逐行执行:校验 → 逐行处理 → 通过的行一次性入表。
1566
- * startFrontImport 里拆出来,好让内嵌图片三段式插在它前面。
1593
+ * 前端通道的逐行执行。
1594
+ * 默认:校验 逐行处理 → 通过的行一次性入表。
1595
+ * 开了后台校验:先收集前台通过行(失败立刻记账),整批一次 formHttp,
1596
+ * 按 impSeq 合并补数后再走入表 / 逐行脚本(不再重复前台校验)。
1567
1597
  * @param {Object} resultData readExcel 的解析信封。
1568
1598
  */
1569
1599
  runFrontImportRows(resultData) {
1570
1600
  let that = this;
1571
- let importOption = this.option.importOption;
1572
- let rows = resultData?.data || [];
1601
+ let importOption = this.option.importOption || {};
1602
+ if (importOption.backendValidateEnabled) {
1603
+ if (!importOption.importScriptCode) {
1604
+ this.handleImportAbort(
1605
+ this.$t1("后台校验脚本编码(importScriptCode)未配置,无法校验")
1606
+ );
1607
+ return;
1608
+ }
1609
+ let passedRows = [];
1610
+ this.runImportRows({
1611
+ rows: resultData?.data || [],
1612
+ deferFinish: true,
1613
+ handler: (row, index, done) => {
1614
+ let errorInfo = that.validateImportRow(row, index, {
1615
+ data: resultData,
1616
+ });
1617
+ if (errorInfo) {
1618
+ done({ success: false, message: errorInfo });
1619
+ return;
1620
+ }
1621
+ passedRows.push(row);
1622
+ done({ defer: true });
1623
+ },
1624
+ onFinish: () => {
1625
+ that.requestBackendValidate(resultData, passedRows);
1626
+ },
1627
+ });
1628
+ return;
1629
+ }
1630
+ this.applyFrontImportRows(resultData, resultData?.data || [], {});
1631
+ },
1632
+
1633
+ /**
1634
+ * 入表 / 逐行脚本。skipValidate 时不再跑前台校验(后台那步已经滤过)。
1635
+ * @param {Object} resultData readExcel 的解析信封。
1636
+ * @param {Array} rows 待处理行。
1637
+ * @param {Object} opts { skipValidate, progress }
1638
+ */
1639
+ applyFrontImportRows(resultData, rows, opts) {
1640
+ let that = this;
1641
+ let importOption = this.option.importOption || {};
1573
1642
  let isScriptMode = importOption.importMode === "script";
1643
+ let skipValidate = !!(opts && opts.skipValidate);
1574
1644
  let passedRows = [];
1575
1645
  this.runImportRows({
1576
- rows: rows,
1646
+ rows: rows || [],
1647
+ progress: opts && opts.progress,
1577
1648
  handler: (row, index, done) => {
1578
- let errorInfo = that.validateImportRow(row, index, { data: resultData });
1579
- if (errorInfo) {
1580
- done({ success: false, message: errorInfo });
1581
- return;
1649
+ // 后台补数后只跑通过的子集,循环下标会从 0 重计;脚本要用 Excel 行序
1650
+ let rowIndex = row.impSeq != null ? row.impSeq : index;
1651
+ if (!skipValidate) {
1652
+ let errorInfo = that.validateImportRow(row, rowIndex, {
1653
+ data: resultData,
1654
+ });
1655
+ if (errorInfo) {
1656
+ done({ success: false, message: errorInfo });
1657
+ return;
1658
+ }
1582
1659
  }
1583
1660
  if (isScriptMode) {
1584
1661
  // 逐行脚本模式:脚本只写"这一行怎么处理",循环/让步/进度归壳管。
@@ -1592,7 +1669,7 @@ tmixins = {
1592
1669
  .handleCustomEvent(
1593
1670
  importOption.onImportRow,
1594
1671
  ["row", "index", "done"],
1595
- [row, index, done]
1672
+ [row, rowIndex, done]
1596
1673
  );
1597
1674
  return;
1598
1675
  }
@@ -1618,6 +1695,151 @@ tmixins = {
1618
1695
  });
1619
1696
  },
1620
1697
 
1698
+ /** 当前表单的 formCode / formVersion;formImport 通道没有 getReportTemplate,改从配置取。 */
1699
+ getImportFormMeta() {
1700
+ let importOption = this.option.importOption || {};
1701
+ let reportTemplate =
1702
+ typeof this.getReportTemplate === "function"
1703
+ ? this.getReportTemplate()
1704
+ : null;
1705
+ return {
1706
+ formCode: reportTemplate?.formCode || importOption.formCode,
1707
+ formVersion: reportTemplate?.formVersion,
1708
+ };
1709
+ },
1710
+
1711
+ /** onImportParams 整批一次,非对象则当 {}。 */
1712
+ getBackendValidateParams(resultData) {
1713
+ let importOption = this.option.importOption || {};
1714
+ if (!importOption.onImportParams) return {};
1715
+ let result = this.getFieldTarget().handleCustomEvent(
1716
+ importOption.onImportParams,
1717
+ ["resultData", "file"],
1718
+ [resultData, this.fileRaw]
1719
+ );
1720
+ if (!result || typeof result !== "object" || Array.isArray(result)) {
1721
+ return {};
1722
+ }
1723
+ return result;
1724
+ },
1725
+
1726
+ /** 送给后台的行:剥掉进度记账字段,保留 impSeq / impNumber 供回包对位。 */
1727
+ buildBackendValidatePayloadRows(rows) {
1728
+ return (rows || []).map((row) => {
1729
+ let item = this.stripImportMeta(row);
1730
+ item.impSeq = row.impSeq;
1731
+ item.impNumber = row.impNumber;
1732
+ return item;
1733
+ });
1734
+ },
1735
+
1736
+ mergeBackendValidateRow(row, item) {
1737
+ if (!item) return;
1738
+ Object.keys(item).forEach((key) => {
1739
+ if (BACKEND_VALIDATE_SKIP_FIELDS.indexOf(key) >= 0) return;
1740
+ row[key] = item[key];
1741
+ });
1742
+ },
1743
+
1744
+ /**
1745
+ * 按 impSeq 把后台逐行结果合并进原行。
1746
+ * 没返回的行、impReturnType=error 的行记失败,不入后续入表。
1747
+ */
1748
+ applyBackendValidateResult(passedRows, objx) {
1749
+ let progress = this.importProgress;
1750
+ let list = Array.isArray(objx) ? objx : [];
1751
+ let map = {};
1752
+ list.forEach((item) => {
1753
+ if (item && item.impSeq != null) map[item.impSeq] = item;
1754
+ });
1755
+ let enriched = [];
1756
+ (passedRows || []).forEach((row) => {
1757
+ let item = map[row.impSeq];
1758
+ if (!item) {
1759
+ progress.markError(row, this.$t1("后台未返回该行结果"));
1760
+ return;
1761
+ }
1762
+ if (item.impReturnType === "error") {
1763
+ progress.markError(
1764
+ row,
1765
+ item.impReturnMsg || this.$t1("数据校验未通过")
1766
+ );
1767
+ return;
1768
+ }
1769
+ this.mergeBackendValidateRow(row, item);
1770
+ enriched.push(row);
1771
+ });
1772
+ return enriched;
1773
+ },
1774
+
1775
+ /**
1776
+ * 前台通过的行整批一次送给后台校验 + 补数。
1777
+ * 不要求 importEntity、不套附件信封;data 就是行数组,params 来自 onImportParams。
1778
+ */
1779
+ requestBackendValidate(resultData, passedRows) {
1780
+ let that = this;
1781
+ let finishProgress = () => {
1782
+ that.backendValidating = false;
1783
+ if (that.importProgress) that.importProgress.finish();
1784
+ };
1785
+ if (!passedRows || !passedRows.length) {
1786
+ finishProgress();
1787
+ return;
1788
+ }
1789
+ let importOption = this.option.importOption || {};
1790
+ let scriptCode = importOption.importScriptCode;
1791
+ if (!scriptCode) {
1792
+ this.handleImportAbort(
1793
+ this.$t1("后台校验脚本编码(importScriptCode)未配置,无法校验")
1794
+ );
1795
+ return;
1796
+ }
1797
+ let formMeta = this.getImportFormMeta();
1798
+ let params;
1799
+ try {
1800
+ params = this.getBackendValidateParams(resultData);
1801
+ } catch (et) {
1802
+ console.error(et);
1803
+ this.handleImportAbort(this.$t1("请求额外参数脚本异常"));
1804
+ return;
1805
+ }
1806
+ this.backendValidating = true;
1807
+ this.formHttp({
1808
+ scriptCode: scriptCode,
1809
+ data: {
1810
+ formCode: formMeta.formCode,
1811
+ formVersion: formMeta.formVersion,
1812
+ data: this.buildBackendValidatePayloadRows(passedRows),
1813
+ params: params,
1814
+ },
1815
+ modal: false,
1816
+ failMsg: false,
1817
+ callback: (resultMsg) => {
1818
+ that.backendValidating = false;
1819
+ if (that.isEnd || !that.showContent) return;
1820
+ if (!resultMsg || resultMsg.type !== "success") {
1821
+ that.handleImportAbort(
1822
+ (resultMsg && resultMsg.content) || that.$t1("后台校验失败")
1823
+ );
1824
+ return;
1825
+ }
1826
+ let enriched = that.applyBackendValidateResult(
1827
+ passedRows,
1828
+ resultMsg.objx
1829
+ );
1830
+ that.applyFrontImportRows(resultData, enriched, {
1831
+ skipValidate: true,
1832
+ progress: that.importProgress,
1833
+ });
1834
+ },
1835
+ error: (et) => {
1836
+ that.backendValidating = false;
1837
+ console.error(et);
1838
+ that.handleImportAbort(that.$t1("后台校验失败"));
1839
+ },
1840
+ });
1841
+ },
1842
+
1621
1843
  //begin
1622
1844
  fileParse(file, type = "base64", callback) {
1623
1845
  let fileRead = new FileReader();
@@ -54,6 +54,47 @@
54
54
  <el-option label="逐行脚本处理" value="script"></el-option>
55
55
  </el-select>
56
56
  </el-form-item>
57
+ <el-form-item
58
+ label="启用后台校验"
59
+ class="el-form-item-second"
60
+ v-if="optionModel.showProgressDialog"
61
+ >
62
+ <el-switch v-model="optionModel.backendValidateEnabled"></el-switch>
63
+ <el-tooltip effect="dark" placement="top">
64
+ <div slot="content">
65
+ 打开后,前台校验通过的行整批一次送给后台脚本:校验并补字段。<br/>
66
+ 通过的行再按「每行处理方式」入表或走逐行脚本。<br/>
67
+ 失败行进结果表,不入表格。
68
+ </div>
69
+ <i class="el-icon-question label-tip-icon"></i>
70
+ </el-tooltip>
71
+ </el-form-item>
72
+ <el-form-item
73
+ label="执行后台脚本编码"
74
+ class="form-item-label-top el-form-item-second"
75
+ v-if="optionModel.showProgressDialog && optionModel.backendValidateEnabled"
76
+ >
77
+ <el-input v-model="optionModel.importScriptCode"></el-input>
78
+ </el-form-item>
79
+ <el-form-item
80
+ label="请求额外参数脚本"
81
+ label-width="150px"
82
+ class="el-form-item-second"
83
+ v-if="optionModel.showProgressDialog && optionModel.backendValidateEnabled"
84
+ >
85
+ <a href="javascript:void(0);" class="a-link link-oneLind"
86
+ @click="editEventHandler('onImportParams', onImportParamsParams)">
87
+ <span>{{ optionModel.onImportParams }}</span>
88
+ <i class="el-icon-edit"></i>
89
+ </a>
90
+ <el-tooltip effect="dark" placement="top">
91
+ <div slot="content">
92
+ 发请求前整批跑一次,返回对象作为 params。<br/>
93
+ 不要在脚本里自己发请求。
94
+ </div>
95
+ <i class="el-icon-question label-tip-icon"></i>
96
+ </el-tooltip>
97
+ </el-form-item>
57
98
  <el-form-item
58
99
  label="启用图片处理"
59
100
  class="el-form-item-second"
@@ -86,6 +127,8 @@
86
127
  <div slot="content">
87
128
  每行调一次,必须调 done:done() 表示成功;<br/>
88
129
  done({success:false, message:"未匹配到编码"}) 表示失败。<br/>
130
+ 若启用了后台校验,row 已含后台补数字段。<br/>
131
+ 更新:this.getWidgetRef(表格名) 比对后改现有行;插入可 addTableData([row])。<br/>
89
132
  循环、让步与进度由组件负责,脚本里不要自己写整趟 for。
90
133
  </div>
91
134
  <i class="el-icon-question label-tip-icon"></i>
@@ -158,7 +201,8 @@ export default {
158
201
  onConfirmImportParams: ["dataId", "formCode", 'resultData', 'file', 'done', "close"],
159
202
  onSuccessImportParams: ["dataId", "formCode", 'resultData', 'file'],
160
203
  onImportRowParams: ["dataId", "formCode", "row", "index", "done"],
161
- onValidateRowParams: ["dataId", "formCode", "row", "index"]
204
+ onValidateRowParams: ["dataId", "formCode", "row", "index"],
205
+ onImportParamsParams: ["dataId", "formCode", "resultData", "file"]
162
206
  };
163
207
  },
164
208
  methods: {
@@ -3507,6 +3507,10 @@ export const advancedFields = [
3507
3507
  showProgressDialog: false,
3508
3508
  // 进度框形态下每行怎么处理:"table" 塞入关联表格;"script" 交给 onImportRow 逐行脚本
3509
3509
  importMode: "table",
3510
+ // 前台校验通过后,整批一次送给后台校验并补字段;默认关=不发请求
3511
+ backendValidateEnabled: false,
3512
+ importScriptCode: "",
3513
+ onImportParams: "",
3510
3514
  // 内嵌图片三段式:把 Excel 里贴的图取回来填进 attachments_ 列。
3511
3515
  // 依赖进度框,仅在 showProgressDialog 打开时生效
3512
3516
  enabledImportPreHandle: false,