cloud-web-corejs 1.1.0-dev.21 → 1.1.0-dev.23

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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/components/bizTab/BizTabListPage.vue +66 -33
  3. package/src/components/bizTab/README.md +3 -1
  4. package/src/components/excelExport/conditionExportConfig.js +112 -0
  5. package/src/components/excelExport/exportType.js +8 -0
  6. package/src/components/excelExport/index.vue +7 -1
  7. package/src/components/excelExport/mixins.js +410 -259
  8. package/src/components/mobile/wf/content.vue +27 -3
  9. package/src/components/table/index.js +5 -2
  10. package/src/components/table/plugins/cell-area/index.js +10 -3
  11. package/src/components/wf/wfStartDialog.vue +1 -0
  12. package/src/components/wf/wfUtil.js +47 -29
  13. package/src/components/wf/wfVisibility.js +6 -0
  14. package/src/components/xform/form-designer/form-widget/container-widget/data-table-mixin.js +7 -0
  15. package/src/components/xform/form-designer/form-widget/container-widget/data-table-widget.vue +3 -1
  16. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +25 -1
  17. package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +2 -1
  18. package/src/components/xform/form-designer/form-widget/field-widget/table-export-button-widget.vue +2 -1
  19. package/src/components/xform/form-designer/setting-panel/form-setting.vue +14 -2
  20. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +318 -12
  21. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/table-column-dialog.vue +23 -7
  22. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/empty-number-input.vue +51 -0
  23. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +27 -2
  24. package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/table-export-button-editor.vue +33 -3
  25. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +54 -0
  26. package/src/components/xform/form-render/container-item/data-table-item.vue +8 -6
  27. package/src/components/xform/form-render/container-item/data-table-mixin.js +587 -25
  28. package/src/components/xform/form-render/container-item/detail-h5-item.vue +26 -16
  29. package/src/components/xform/form-render/container-item/detail-item.vue +350 -446
  30. package/src/components/xform/form-render/fieldControlMixin.js +2 -0
  31. package/src/components/xform/form-render/indexMixin.js +25 -15
  32. package/src/components/xform/mixins/businessLock.js +59 -0
  33. package/src/components/xform/mixins/wfStart.js +103 -0
  34. package/src/components/xform/mixins/wfVisibility.js +33 -0
  35. package/src/components/xform/styles/h5.scss +6 -1
  36. package/src/components/xform/utils/util.js +2 -0
  37. package/src/views/user/home/index.vue +1 -1
  38. package/src/views/user/home/net/distributor.vue +1015 -0
  39. package/src/views/user/home/net/index.vue +61 -0
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.21",
4
+ "version": "1.1.0-dev.23",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve",
7
7
  "dev:micro": "vue-cli-service serve --port 17527",
@@ -163,33 +163,34 @@ export default {
163
163
  return key;
164
164
  },
165
165
  /**
166
- * 打开「详情/编辑」页签。同一单据已开则复用并激活,不重复开。
166
+ * 打开「详情/编辑」页签。同一单据已开则复用并重新加载,不检查未保存修改。
167
167
  * option.navRows / option.navIndex 传了就能用「上一单/下一单」翻单,
168
168
  * 一般直接把列表的 fullData 和当前行下标丢进来。
169
169
  */
170
170
  openDetail(row, param, option) {
171
171
  let dataId = row ? row.id : null;
172
172
  if (dataId == null || dataId === "") return this.openAdd(param, option);
173
- if (!this.hasTab(dataId) && !this.checkTabLimit()) return null;
174
- let key = String(dataId);
175
- // 这单已经开着、而且里面有没保存的修改:只激活,不重新取数。
176
- // openEditTab 的复用分支会 showContent 置 false 再置 true(等于重挂载),
177
- // 从列表再点一次就把用户改了半天的内容无声冲掉了。
178
- if (this.hasTab(key) && this.isDetailDirty(this.getTab(key))) {
179
- this.activeName = key;
180
- return key;
181
- }
182
- this.$refs.xTabs.openEditTab(row, param || null, {
183
- tabParam: {
184
- tabName: key,
185
- tabLabelField: this.titleField,
186
- isAdd: false,
187
- dataId: dataId,
188
- navRows: (option && option.navRows) || null,
189
- navIndex: (option && option.navIndex != null) ? option.navIndex : -1,
190
- ...((option && option.tabParam) || {}),
191
- },
192
- });
173
+ let tab = this.getTabByDataId(dataId);
174
+ if (!tab && !this.checkTabLimit()) return null;
175
+ let key = tab ? tab.tabName : this.getAvailableTabName(option?.tabParam?.tabName || dataId);
176
+ let tabParam = {
177
+ tabLabelField: this.titleField,
178
+ navRows: (option && option.navRows) || null,
179
+ navIndex: (option && option.navIndex != null) ? option.navIndex : -1,
180
+ ...((option && option.tabParam) || {}),
181
+ tabName: key,
182
+ isAdd: false,
183
+ dataId,
184
+ };
185
+ if (tab) {
186
+ // 已按当前单据定位,直接更新原页签,避免 x-tabs 按 row.id 严格比较时
187
+ // 把数字/字符串 ID 当成两张单据;tabName 始终保留,详情登记关系不变。
188
+ Object.assign(tab, { data: row, param: param || null }, tabParam);
189
+ this.activeName = key;
190
+ this.refreshTab(tab);
191
+ } else {
192
+ this.$refs.xTabs.openEditTab(row, param || null, { tabParam });
193
+ }
193
194
  return key;
194
195
  },
195
196
 
@@ -215,7 +216,7 @@ export default {
215
216
  tab.navIndex = 0;
216
217
  }
217
218
  // 页签 key(tabName) 仍是当初的合成 id:换掉会连带 el-tab-pane 重建、详情被迫重挂载,
218
- // 正好破坏「保存后不刷详情」的设计。重复打开的去重靠 tab.data.id,它已是真单号。
219
+ // 正好破坏「保存后不刷详情」的设计。重复打开按当前 dataId 去重。
219
220
  }
220
221
  this.syncTabTitle(tab, info ? info.title : null);
221
222
  },
@@ -288,14 +289,28 @@ export default {
288
289
  * 翻单会丢掉未保存的修改,所以先过一遍脏数据确认(与关页签同一判据)。
289
290
  * @returns {Promise<Boolean>} 是否真的翻了
290
291
  */
291
- navigateTab(tab, step) {
292
- let idx = this.canNavigate(tab, step);
293
- if (idx < 0) return Promise.resolve(false);
294
- return this.confirmLeaveDetail(tab).then((pass) => {
292
+ navigateTab(tab, step) {
293
+ let idx = this.canNavigate(tab, step);
294
+ if (idx < 0) return Promise.resolve(false);
295
+ let existing = this.getTabByDataId(tab.navRows[idx].id);
296
+ if (existing && existing !== tab) {
297
+ // 与重复打开一致:直接重新加载目标详情,不检查其未保存修改。
298
+ this.activeName = existing.tabName;
299
+ this.refreshTab(existing);
300
+ return Promise.resolve(true);
301
+ }
302
+ return this.confirmLeaveDetail(tab).then((pass) => {
295
303
  if (!pass) return false;
296
304
  // 确认期间可能已经被关掉或翻走了,重新校一次
297
- let target = this.canNavigate(tab, step);
298
- if (target < 0 || !this.isLiveTab(tab)) return false;
305
+ let target = this.canNavigate(tab, step);
306
+ if (target < 0 || !this.isLiveTab(tab)) return false;
307
+ // 确认期间目标单据可能已从其他入口打开。
308
+ let existing = this.getTabByDataId(tab.navRows[target].id);
309
+ if (existing && existing !== tab) {
310
+ this.activeName = existing.tabName;
311
+ this.refreshTab(existing);
312
+ return true;
313
+ }
299
314
  tab.navIndex = target;
300
315
  tab.data = tab.navRows[target];
301
316
  tab.dataId = tab.data.id; // 详情不 watch dataId,靠下面的重挂载按新单号取数
@@ -338,14 +353,32 @@ export default {
338
353
  getTabs() {
339
354
  return this.$refs.xTabs ? this.$refs.xTabs.tabs : [];
340
355
  },
341
- getTab(tabName) {
342
- return this.getTabs().find((tab) => tab.tabName === tabName) || null;
343
- },
356
+ getTab(tabName) {
357
+ return this.getTabs().find((tab) => tab.tabName === tabName) || null;
358
+ },
359
+ getTabByDataId(dataId) {
360
+ if (dataId == null || dataId === "") return null;
361
+ return this.getTabs().find((tab) => {
362
+ if (tab.isAdd) return false;
363
+ const currentId = tab.dataId ?? tab.data?.id;
364
+ return currentId != null && String(currentId) === String(dataId);
365
+ }) || null;
366
+ },
367
+ getAvailableTabName(preferredName) {
368
+ const baseName = String(preferredName);
369
+ let name = baseName;
370
+ let suffix = 1;
371
+ // 翻单后旧页签仍占用原名称;列表页签名称也不可复用。
372
+ while (name === LIST_TAB_NAME || this.getTab(name)) {
373
+ name = baseName + "_" + suffix++;
374
+ }
375
+ return name;
376
+ },
344
377
  isLiveTab(tab) {
345
378
  return this.getTabs().indexOf(tab) >= 0;
346
379
  },
347
- hasTab(dataId) {
348
- return this.getTabs().some((tab) => tab.tabName === String(dataId));
380
+ hasTab(dataId) {
381
+ return !!this.getTabByDataId(dataId);
349
382
  },
350
383
  getTabLabel(tab) {
351
384
  let label = tab.data ? tab.data[this.titleField] : null;
@@ -94,7 +94,9 @@ this.$refs.bizTab.openAdd(null, { navRows: rows });
94
94
 
95
95
  内置关闭确认的判定顺序:详情实现了 `isBillDirty()` → 问它;否则只拦「还没保存过的新增页签」。
96
96
 
97
- **未保存的修改在三条路径上都会拦**(都以 `isBillDirty()` 为判据):关页签要确认;**翻单**要确认(放弃修改才翻);**从列表重复打开同一单**时若详情是脏的,只激活页签、不重新取数(否则 `openEditTab` 的复用分支会重挂载,把改了半天的内容无声冲掉)。详情没实现 `isBillDirty()` 时后两条不拦。
97
+ **重复打开单据时直接重新加载**:按当前 `dataId` 复用页签,不检查 `isBillDirty()`、不弹确认,未保存的修改会被重新加载的数据覆盖。翻单目标已在其他页签打开时,同样激活并重新加载目标页签,源页签内容保留。关闭页签、在原页签内翻到未打开的单据时,仍按 `isBillDirty()` 判断是否需要放弃修改确认;详情没实现该方法时不拦。
98
+
99
+ 单据查找按当前 `dataId` 匹配(数字与字符串 ID 等价);`tabName` 是稳定的页签标识,翻单和新增保存时保持不变。`getTab(tabName)` 用于按页签名称查找,`getTabByDataId(dataId)` / `hasTab(dataId)` 用于按当前单据查找。`openDetail()` 返回实际页签名称,可能与单据 ID 不同,应使用返回值激活页签。重新打开翻单前的单据时,会为新页签分配未占用的名称。
98
100
 
99
101
  #### behavior
100
102
 
@@ -0,0 +1,112 @@
1
+ import { getLogicParamValue } from "@base/api/user";
2
+
3
+ export const CONDITION_EXPORT_PARAM_CODE = "conditionExportParams";
4
+ export const CONDITION_EXPORT_SEARCH_COUNT_CODE
5
+ = "conditionExportSearchCount";
6
+
7
+ export const DEFAULT_CONDITION_EXPORT_CONFIG = Object.freeze({
8
+ maxQueryTotal: 300000,
9
+ pageSize: 1000,
10
+ limitFileSize: 100000,
11
+ searchCount: true,
12
+ });
13
+
14
+ function parsePositiveInteger(value, fallback) {
15
+ let text = String(value == null ? "" : value).trim();
16
+ if (!/^\d+$/.test(text)) return fallback;
17
+ let result = Number(text);
18
+ if (!Number.isSafeInteger(result) || result <= 0) return fallback;
19
+ return result;
20
+ }
21
+
22
+ /**
23
+ * 解析「最大查询总数;每页查询数量;单文件最大数据量」。每一项独立回落默认值,
24
+ * 避免一项填错导致另外两项已生效的配置也被丢弃。
25
+ */
26
+ export function parseConditionExportParams(rawValue) {
27
+ let values = typeof rawValue === "string" ? rawValue.split(";") : [];
28
+ return {
29
+ maxQueryTotal: parsePositiveInteger(
30
+ values[0],
31
+ DEFAULT_CONDITION_EXPORT_CONFIG.maxQueryTotal
32
+ ),
33
+ pageSize: parsePositiveInteger(
34
+ values[1],
35
+ DEFAULT_CONDITION_EXPORT_CONFIG.pageSize
36
+ ),
37
+ limitFileSize: parsePositiveInteger(
38
+ values[2],
39
+ DEFAULT_CONDITION_EXPORT_CONFIG.limitFileSize
40
+ ),
41
+ };
42
+ }
43
+
44
+ export function parseConditionExportSearchCount(rawValue, fallback = true) {
45
+ if (typeof rawValue === "boolean") return rawValue;
46
+ if (typeof rawValue !== "string") return fallback;
47
+ let value = rawValue.trim().toLowerCase();
48
+ if (value === "true") return true;
49
+ if (value === "false") return false;
50
+ return fallback;
51
+ }
52
+
53
+ function readLogicParam(paramCode) {
54
+ return getLogicParamValue({
55
+ data: { paramCode },
56
+ modal: false,
57
+ failMsg: false,
58
+ errorMsg: false,
59
+ })
60
+ .then((res) => (res && res.type === "success" ? res.objx : ""))
61
+ .catch(() => "");
62
+ }
63
+
64
+ /** 读取两条全局逻辑参数;接口异常时静默使用内置默认值。 */
65
+ export function loadConditionExportConfig() {
66
+ return Promise.all([
67
+ readLogicParam(CONDITION_EXPORT_PARAM_CODE),
68
+ readLogicParam(CONDITION_EXPORT_SEARCH_COUNT_CODE),
69
+ ]).then(([paramsRaw, searchCountRaw]) => ({
70
+ ...parseConditionExportParams(paramsRaw),
71
+ searchCount: parseConditionExportSearchCount(searchCountRaw, true),
72
+ }));
73
+ }
74
+
75
+ /**
76
+ * 从导出按钮 options 取出条件导出的三项覆盖值。空值转成 null,交给
77
+ * resolveConditionExportConfig 回落到全局逻辑参数。
78
+ */
79
+ export function pickConditionExportOverrides(fieldOptions = {}) {
80
+ return {
81
+ pageSize: fieldOptions.exportPageSize || null,
82
+ maxQueryTotal: fieldOptions.maxQueryTotal || null,
83
+ limitFileSize: fieldOptions.limitFileSize || null,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * 单次导出参数优先于全局逻辑参数。按钮参数与脚本参数在进入导出器前已经合并,且脚本
89
+ * 位于展开表达式右侧,所以这里读取到的 option 天然满足「脚本 > 按钮 > 全局」。
90
+ */
91
+ export function resolveConditionExportConfig(globalConfig, option = {}) {
92
+ let base = globalConfig || DEFAULT_CONDITION_EXPORT_CONFIG;
93
+ let searchCountOverride
94
+ = option.conditionExportSearchCount !== undefined
95
+ ? option.conditionExportSearchCount
96
+ : option.searchCount;
97
+ return {
98
+ maxQueryTotal: parsePositiveInteger(
99
+ option.maxQueryTotal,
100
+ base.maxQueryTotal
101
+ ),
102
+ pageSize: parsePositiveInteger(option.pageSize, base.pageSize),
103
+ limitFileSize: parsePositiveInteger(
104
+ option.limitFileSize,
105
+ base.limitFileSize
106
+ ),
107
+ searchCount: parseConditionExportSearchCount(
108
+ searchCountOverride,
109
+ base.searchCount
110
+ ),
111
+ };
112
+ }
@@ -39,6 +39,14 @@ export function isRowExport(type) {
39
39
  return type === EXPORT_TYPE.SELECTED || type === EXPORT_TYPE.CURRENT;
40
40
  }
41
41
 
42
+ /**
43
+ * 是否条件导出。主表条件导出的历史 type 是 null/undefined;明细条件导出有独立类型。
44
+ * 明细选择导出虽然也走后端分页,但不受条件导出全局上限与查总数开关影响。
45
+ */
46
+ export function isConditionExport(type) {
47
+ return type == null || type === EXPORT_TYPE.ITEM_CONDITION;
48
+ }
49
+
42
50
  /**
43
51
  * 是否必须先勾选行:空勾选要拦截,且以勾选行 id 作为查询条件。
44
52
  * @param {String|null} type 导出模式。
@@ -41,6 +41,12 @@
41
41
  <div class="export-box">
42
42
  <div class="tips">
43
43
  {{ $t2("导出中,请勿关闭当前窗口", "components.excelExport.tip") }}
44
+ <template v-if="conditionExportMode">
45
+ ,{{
46
+ $t2("导出最大数据量", "components.excelExport.maxDataSize")
47
+ }}:{{ conditionExportConfig.maxQueryTotal
48
+ }}{{ $t2("条", "components.excelExport.rowUnit") }}
49
+ </template>
44
50
  </div>
45
51
  <el-progress
46
52
  :text-inside="true"
@@ -62,7 +68,7 @@
62
68
  <div class="fl import-count">
63
69
  <span class="f-red doneNum">{{ doneSize }}</span>
64
70
  /
65
- <span class="dataSize">{{ tTotalSize }}</span>
71
+ <span class="dataSize">{{ exportTotalText }}</span>
66
72
  </div>
67
73
  <el-button type="primary" plain class="button-sty" @click="dialogClose2">
68
74
  <i class="el-icon-close el-icon"></i>