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
@@ -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 (
@@ -44,6 +44,7 @@ import i18n from "../../../utils/i18n";
44
44
  import fieldMixin from "./fieldMixin";
45
45
  import StaticContentWrapper from "./static-content-wrapper.vue";
46
46
  import { EXPORT_TYPE } from "../../../../excelExport/exportType";
47
+ import { pickConditionExportOverrides } from "@base/components/excelExport/conditionExportConfig";
47
48
 
48
49
  export default {
49
50
  name: "select-export-item-button-widget",
@@ -101,8 +102,8 @@ export default {
101
102
  let opt = {
102
103
  title: this.field.options.exportFileName || null,
103
104
  targetRef: this.field.options.tableRef || null,
104
- pageSize: this.field.options.exportPageSize || null,
105
105
  showImageAtTable: this.field.options.showImageAtTable || null,
106
+ ...pickConditionExportOverrides(this.field.options),
106
107
  // 明细导出的列 / 脚本编码 / 取数参数三项各自独立:按钮上维护了哪项就以哪项为准,
107
108
  // 留空的那项沿用数据表格属性上那套。参数脚本原样下发,由数据表格求值(this 是
108
109
  // 表格组件,与配在表格属性上时同义)
@@ -32,6 +32,7 @@ import emitter from "../../../../../components/xform/utils/emitter";
32
32
  import i18n from "../../../../../components/xform/utils/i18n";
33
33
  import fieldMixin from "../../../../../components/xform/form-designer/form-widget/field-widget/fieldMixin";
34
34
  import StaticContentWrapper from "../../../../../components/xform/form-designer/form-widget/field-widget/static-content-wrapper.vue";
35
+ import { pickConditionExportOverrides } from "@base/components/excelExport/conditionExportConfig";
35
36
 
36
37
  export default {
37
38
  name: "table-export-button-widget",
@@ -81,8 +82,8 @@ export default {
81
82
  let opt = {
82
83
  title: this.field.options.exportFileName || null,
83
84
  targetRef: this.field.options.tableRef || null,
84
- pageSize: this.field.options.exportPageSize || null,
85
85
  showImageAtTable: this.field.options.showImageAtTable || null,
86
+ ...pickConditionExportOverrides(this.field.options),
86
87
  };
87
88
  let tableExportParam = this.handleCustomEvent(this.field.options.tableExportParam);
88
89
  let options = { ...opt, ...tableExportParam, type: type };
@@ -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
  >流程主题