cloud-web-corejs 1.1.0-dev.22 → 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 (30) 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/mobile/wf/content.vue +27 -3
  5. package/src/components/table/index.js +5 -2
  6. package/src/components/table/plugins/cell-area/index.js +10 -3
  7. package/src/components/wf/wfStartDialog.vue +1 -0
  8. package/src/components/wf/wfUtil.js +47 -29
  9. package/src/components/wf/wfVisibility.js +6 -0
  10. package/src/components/xform/form-designer/form-widget/container-widget/data-table-mixin.js +7 -0
  11. package/src/components/xform/form-designer/form-widget/container-widget/data-table-widget.vue +3 -1
  12. package/src/components/xform/form-designer/form-widget/field-widget/fieldMixin.js +25 -1
  13. package/src/components/xform/form-designer/setting-panel/form-setting.vue +14 -2
  14. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +318 -12
  15. package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/table-column-dialog.vue +23 -7
  16. package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +48 -0
  17. package/src/components/xform/form-render/container-item/data-table-item.vue +8 -6
  18. package/src/components/xform/form-render/container-item/data-table-mixin.js +584 -24
  19. package/src/components/xform/form-render/container-item/detail-h5-item.vue +26 -16
  20. package/src/components/xform/form-render/container-item/detail-item.vue +350 -446
  21. package/src/components/xform/form-render/fieldControlMixin.js +2 -0
  22. package/src/components/xform/form-render/indexMixin.js +25 -15
  23. package/src/components/xform/mixins/businessLock.js +59 -0
  24. package/src/components/xform/mixins/wfStart.js +103 -0
  25. package/src/components/xform/mixins/wfVisibility.js +33 -0
  26. package/src/components/xform/styles/h5.scss +6 -1
  27. package/src/components/xform/utils/util.js +2 -0
  28. package/src/views/user/home/index.vue +1 -1
  29. package/src/views/user/home/net/distributor.vue +1015 -0
  30. 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.22",
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
 
@@ -93,7 +93,12 @@
93
93
  <div class="tit"><span>{{
94
94
  (wfInfo.createDate ? wfInfo.createDate.substring(0, 10) : '') + wfInfo.name
95
95
  }}</span></div>
96
- <iframe style="width: 100%;flex:1" :src="wfImage"></iframe>
96
+ <iframe
97
+ ref="wfPreviewFrame"
98
+ style="width: 100%;flex:1"
99
+ :src="wfImage"
100
+ @load="fitWorkflowPreview"
101
+ ></iframe>
97
102
  </el-form>
98
103
  </div>
99
104
  <span slot="footer" class="dialog-footer">
@@ -620,8 +625,27 @@ export default {
620
625
  mounted() {
621
626
  this.init();
622
627
  },
623
- methods: {
624
- getWfStatusLabel() {
628
+ methods: {
629
+ fitWorkflowPreview() {
630
+ /* warm-flow-ui 是独立 iframe,窄屏下默认画布宽于视口,左右节点会被
631
+ 它自己的 overflow:hidden 裁掉。复用其「自适应」能力,在数据逐步
632
+ 渲染的几个时点重新 fit;跨域部署时访问 document 会抛错,静默跳过。 */
633
+ [0, 300, 800, 1500].forEach((delay) => {
634
+ window.setTimeout(() => {
635
+ try {
636
+ const frame = this.$refs.wfPreviewFrame;
637
+ const buttons = frame?.contentDocument?.querySelectorAll("button") || [];
638
+ const fitButton = Array.from(buttons).find(
639
+ (button) => button.textContent.trim() === "自适应"
640
+ );
641
+ fitButton?.click();
642
+ } catch (e) {
643
+ // 跨域 iframe 无法读取内部 DOM,保留 warm-flow-ui 原有交互。
644
+ }
645
+ }, delay);
646
+ });
647
+ },
648
+ getWfStatusLabel() {
625
649
  return this.wfStatusMap[this.wfInfo.stat]?.label;
626
650
  },
627
651
  getWfStatusType() {
@@ -97,7 +97,7 @@ async function initVxeTable(option) {
97
97
  let searchColumns = null;
98
98
  if (tableName) {
99
99
  setTableState($grid, "tableName", tableName);
100
- if (option.path) {
100
+ if (option.path && option.readQueryAllPage !== false) {
101
101
  await getTableData(that, option.tableRef, "isQueryAllPage", (res1) => {
102
102
  let result = res1.objx ? res1.objx.data : null;
103
103
  if (result !== null && result !== undefined) {
@@ -1252,7 +1252,10 @@ function checkQueryTotal(that, tableRef, value) {
1252
1252
  let pagerConfig = $grid.pagerConfig;
1253
1253
 
1254
1254
  $grid.pagerConfig.layouts = pagerConfig.layouts || [];
1255
- addTableData(that, tableRef, value);
1255
+ let originOption = $grid.params && $grid.params.originOption;
1256
+ if (!originOption || originOption.readQueryAllPage !== false) {
1257
+ addTableData(that, tableRef, value);
1258
+ }
1256
1259
  $grid.commitProxy("query");
1257
1260
  }
1258
1261
 
@@ -464,8 +464,8 @@ function clearAreaValues($table, area) {
464
464
  }
465
465
 
466
466
  /**
467
- * 粘贴落盘。必须整段同步执行:业务的 pasteMethod 里会读 window.event.clipboardData
468
- * 取粘贴进来的图片文件,一旦跨出原生事件派发(setTimeout/await/$nextTick)就读不到了。
467
+ * 粘贴落盘。必须整段同步执行:clipboardData 会随 clipData 传给业务 pasteMethod,
468
+ * 一旦跨出原生事件派发(setTimeout/await/$nextTick),部分浏览器就读不到图片文件了。
469
469
  */
470
470
  function applyPaste($table, pasteCells, clipData) {
471
471
  let store = getStore($table);
@@ -499,8 +499,11 @@ function applyPaste($table, pasteCells, clipData) {
499
499
  let newRows = insertRows;
500
500
  if (clipOpts.createRowsMethod) {
501
501
  let created = clipOpts.createRowsMethod({
502
+ $table: $table,
502
503
  insertRows: insertRows,
503
504
  pasteCells: pasteCells,
505
+ startRowIndex: startRowIndex,
506
+ targetRow: rows[startRowIndex] || null,
504
507
  });
505
508
  newRows = created || insertRows;
506
509
  }
@@ -887,7 +890,11 @@ const cellAreaMixin = {
887
890
  pasteCells = [[""]];
888
891
  }
889
892
  evnt.preventDefault();
890
- applyPaste($table, pasteCells, { text: text, html: html });
893
+ applyPaste($table, pasteCells, {
894
+ text: text,
895
+ html: html,
896
+ clipboardData: clipboardData,
897
+ });
891
898
  if (store.isCut) {
892
899
  store.isCut = false;
893
900
  store.copyArea = null;
@@ -3,6 +3,7 @@
3
3
  <el-dialog
4
4
  :title="$t2('流程启动', 'components.wf.wfStart')"
5
5
  :visible.sync="showDialog"
6
+ :width="option && option.layoutType === 'H5' ? 'calc(100% - 32px)' : undefined"
6
7
  v-if="showDialog"
7
8
  :modal-append-to-body="false"
8
9
  @close="close"
@@ -1,6 +1,7 @@
1
1
  import Vue from 'vue';
2
2
  import settingConfig from '@/settings.js'
3
- import { scrollIntoScrollParent } from '@base/utils/scrollUtil';
3
+ import { scrollIntoScrollParent } from '@base/utils/scrollUtil';
4
+ import { isWfInfoHidden } from '@base/components/wf/wfVisibility';
4
5
 
5
6
  let f;
6
7
  let configUtil = {
@@ -48,26 +49,36 @@ f = async function (option) {
48
49
  content.vue 里的 $http 回调/定时器还会回来调宿主方法,
49
50
  它内部那些 append-to-body 的 popover/dialog 的 DOM 也没人收。
50
51
  页签模式下详情会被反复重挂载,一次一份,所以统一登记、挂到宿主的销毁钩子上回收。 */
51
- let instances = [];
52
- let disposed = false;
53
- let registerDispose = () => {
54
- if (!instances.length || !that.$once) return;
55
- that.$once("hook:beforeDestroy", () => {
56
- disposed = true;
57
- instances.forEach((vm) => {
58
- try {
59
- vm.$destroy(); //会级联销毁子组件,append-to-body 的那些 el 由它们自己的 destroyed 摘掉
60
- //$destroy 不管 DOM,自己收一下(宿主子树里的那份本来也会被父级移除)
61
- if (vm.$el && vm.$el.parentNode) vm.$el.parentNode.removeChild(vm.$el);
62
- } catch (e) {
63
- //拆的时候出问题不要拖累宿主销毁
64
- }
65
- });
66
- instances.length = 0;
67
- });
68
- };
69
-
70
- let done = () => {
52
+ let instances = [];
53
+ let disposed = false;
54
+ let disposeRegistered = false;
55
+ let renderRevision = 0;
56
+ let clearInstances = () => {
57
+ renderRevision++;
58
+ instances.forEach((vm) => {
59
+ try {
60
+ vm.$destroy();
61
+ if (vm.$el && vm.$el.parentNode) vm.$el.parentNode.removeChild(vm.$el);
62
+ } catch (e) {
63
+ //显隐切换或宿主销毁时,单个流程实例回收失败不能影响其余实例
64
+ }
65
+ });
66
+ instances.length = 0;
67
+ };
68
+ let registerDispose = () => {
69
+ if (disposeRegistered || !that.$once) return;
70
+ disposeRegistered = true;
71
+ that.$once("hook:beforeDestroy", () => {
72
+ disposed = true;
73
+ clearInstances();
74
+ if (that._refreshWfInfoVisibility === refreshVisibility) {
75
+ delete that._refreshWfInfoVisibility;
76
+ }
77
+ });
78
+ };
79
+
80
+ let done = () => {
81
+ let currentRevision = ++renderRevision;
71
82
  let $wfBtnRegion = that.$refs[option.wfBtnRegion || "wfBtnRegion"];
72
83
  let $wfViewRegion = that.$refs[(option.wfViewRegion || "wfViewRegion")];
73
84
 
@@ -83,10 +94,9 @@ f = async function (option) {
83
94
  let showModifyBtn = option.showModifyBtn != null ? option.showModifyBtn : true;
84
95
  let contentInstance = null; //流程面板实例,标题栏下拉要点它的方法
85
96
  //宿主声明「不挂流程信息面板」:面板整棵 v-if 掉,标题栏那份审批入口也不能挂
86
- let _hideWf = that._hideWf || that.$attrs._hideWf;
87
- let isHideWf = _hideWf === true || _hideWf === 'true';
97
+ let isHideWf = isWfInfoHidden(that, option);
88
98
 
89
- if (wfInfo) {
99
+ if (wfInfo && !isHideWf) {
90
100
  let wfModel = configUtil.settingConfig.wfModel || 1;
91
101
  let url = wfModel == 2 ? '/content2.vue' : '/content.vue';
92
102
  let a = configUtil.Vue.extend(configUtil.getWfObj(url));
@@ -106,7 +116,7 @@ f = async function (option) {
106
116
  });
107
117
  instances.push(instance);
108
118
  contentInstance = instance;
109
- if (!_hideWf && slideWrap) {
119
+ if (slideWrap) {
110
120
  let navVm = new configUtil.Vue({
111
121
  render: function (h) {
112
122
  let node = h("div", {}, [
@@ -137,8 +147,8 @@ f = async function (option) {
137
147
  if (settingConfig.version == 'oa') {
138
148
  slideWrap = slideWrap.children[0];
139
149
  }
140
- setTimeout(() => {
141
- if (disposed) return; //这一步延后了 500ms,期间宿主可能已经销毁
150
+ setTimeout(() => {
151
+ if (disposed || currentRevision !== renderRevision) return;
142
152
  slideWrap.append(navDom);
143
153
  if (that.$attrs.toWfArea) {
144
154
  //待办进来,直接跳到流程模块
@@ -225,8 +235,16 @@ f = async function (option) {
225
235
  contentInstance.headerActionsMounted = true;
226
236
  }
227
237
 
228
- registerDispose();
229
- }
238
+ registerDispose();
239
+ }
240
+
241
+ let refreshVisibility = () => {
242
+ if (disposed) return;
243
+ clearInstances();
244
+ done();
245
+ };
246
+ // xform 的 setWfInfoHidden 可在流程初始化前后调用;初始化后即时重建流程展示区。
247
+ that._refreshWfInfoVisibility = refreshVisibility;
230
248
 
231
249
  if (true || wfInfo) {
232
250
  if (option.callback) {
@@ -0,0 +1,6 @@
1
+ // 面板显隐独立于流程初始化和启动权限;兼容既有宿主属性。
2
+ export function isWfInfoHidden(host, option = {}) {
3
+ const attrs = (host && host.$attrs) || {};
4
+ return [host && host._hideWf, attrs._hideWf, option.hideWfInfo]
5
+ .some(value => value === true || value === "true");
6
+ }
@@ -63,6 +63,13 @@ modules = {
63
63
  customClass() {
64
64
  return this.widget.options.customClass || "";
65
65
  },
66
+ dataTableStyle() {
67
+ let width = this.widget.options.tableWidth;
68
+ if (width === null || width === undefined || width === "") return {};
69
+ width = String(width).trim();
70
+ if (/^\d+(?:\.\d+)?$/.test(width)) width += "px";
71
+ return { width };
72
+ },
66
73
  columns() {
67
74
  let tableColumns = this.widget.options.tableColumns;
68
75
  let newColumns = [];
@@ -12,6 +12,7 @@
12
12
  <container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList"
13
13
  :index-of-parent-list="indexOfParentList">
14
14
  <div :key="widget.id" class="table-container" :class="[selected ? 'selected' : '', customClass]"
15
+ :style="dataTableStyle"
15
16
  @click.stop="selectWidget(widget)">
16
17
  <vxe-grid v-bind="gridOptions" :data="widget.options.tableData" :auto-resize="true">
17
18
  <template #form>
@@ -68,7 +69,7 @@
68
69
  plain>刷新
69
70
  </vxe-button>
70
71
 
71
- <vxe-button status="warning" icon="el-icon-search" class="button-sty" @click="searchEvent">搜索
72
+ <vxe-button v-if="!widget.options.isNotShowQueryButton" status="warning" icon="el-icon-search" class="button-sty" @click="searchEvent">搜索
72
73
  </vxe-button>
73
74
  </div>
74
75
  </div>
@@ -212,6 +213,7 @@ export default {
212
213
  },
213
214
  created() {
214
215
  if(this.widget.options.isNotQueryAllPage===undefined)this.widget.options.isNotQueryAllPage = false;
216
+ if(this.widget.options.isNotReadQueryAllPage===undefined)this.widget.options.isNotReadQueryAllPage = false;
215
217
  }
216
218
  };
217
219
  </script>
@@ -155,7 +155,21 @@ modules = {
155
155
  },
156
156
  },
157
157
  isReadMode: function () {
158
- return !!this.getReadMode() || this.fieldReadonlyFlag;
158
+ return !!this.getReadMode()
159
+ || this.fieldReadonlyFlag
160
+ || this.tableCellReadonly;
161
+ },
162
+ tableCellReadonly: function () {
163
+ if (!this.tableParam?.row || !this.tableParam?.$table) {
164
+ return false;
165
+ }
166
+ if (!this.tableParam.$table.isActiveByRow?.(this.tableParam.row)) {
167
+ return false;
168
+ }
169
+ let tableTarget = this.getParentTarget();
170
+ return typeof tableTarget?.isTableCellEditable === "function"
171
+ ? !tableTarget.isTableCellEditable(this.tableParam, true)
172
+ : false;
159
173
  },
160
174
  optionLabel() {
161
175
  return this.getOptionLabel();
@@ -1277,6 +1291,7 @@ modules = {
1277
1291
  this.emitFieldDataChange(e, this.oldFieldValue),
1278
1292
  (this.oldFieldValue = baseRefUtil.deepClone(e)),
1279
1293
  this.dispatch("VFormRender", "fieldValidation", [this.getPropName()]);
1294
+ this.requestTableAutoSave("change");
1280
1295
  },
1281
1296
  handleFocusCustomEvent: function (e) {
1282
1297
  if (
@@ -1292,6 +1307,15 @@ modules = {
1292
1307
  let t = new Function("event", this.field.options.onBlur);
1293
1308
  t.call(this, e);
1294
1309
  }
1310
+ this.requestTableAutoSave("blur");
1311
+ },
1312
+ /** @param {String} trigger 自动保存触发来源。 */
1313
+ requestTableAutoSave(trigger) {
1314
+ if (this.designState || !this.tableParam?.row) return;
1315
+ let tableTarget = this.getParentTarget();
1316
+ if (typeof tableTarget?.scheduleEditRowAutoSave === "function") {
1317
+ tableTarget.scheduleEditRowAutoSave(this.tableParam, trigger);
1318
+ }
1295
1319
  },
1296
1320
  handleInputCustomEvent: function (e) {
1297
1321
  if (
@@ -202,12 +202,24 @@
202
202
  @change="changeWfEnabled"
203
203
  ></el-switch>
204
204
  </el-form-item>
205
- <el-form-item class="form-item-label-top" :label="i18nt('默认流程单据类型名称')">
205
+ <el-form-item class="form-item-label-top" :label="i18nt('默认流程单据类型名称')">
206
206
  <el-input
207
207
  type="text"
208
208
  v-model="designer.vueInstance.reportTemplate.objTypeName"
209
209
  ></el-input>
210
- </el-form-item>
210
+ </el-form-item>
211
+ <el-form-item v-if="formConfig.wfEnabled" label="隐藏流程信息的角色">
212
+ <el-input
213
+ :value="formConfig.wfInfoHiddenRoleCodes"
214
+ @input="$set(formConfig, 'wfInfoHiddenRoleCodes', $event)"
215
+ placeholder="角色编码,多个用英文逗号分隔"
216
+ clearable
217
+ />
218
+ <div style="line-height: 1.6; color: #909399; font-size: 12px">
219
+ 当前公司角色命中任意编码时,隐藏流程信息及审批入口,保留启动流程按钮;脚本可调用
220
+ this.setWfInfoHidden(true/false)。
221
+ </div>
222
+ </el-form-item>
211
223
  <el-form-item>
212
224
  <span slot="label"
213
225
  >流程主题