cloud-web-corejs 1.0.54-dev.806 → 1.0.54-dev.808
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 +1 -1
- package/src/components/VabUpload/image-viewer.vue +1 -1
- package/src/components/VabUpload/mixins.js +48 -15
- package/src/components/VabUpload/previewGroup.js +128 -0
- package/src/components/VabUpload/view.vue +1 -1
- package/src/components/excelExport/exportFieldDialog.vue +23 -12
- package/src/components/excelExport/exportItemConfigUtil.js +30 -0
- package/src/components/excelExport/mixins.js +3 -3
- package/src/components/table/plugins/cell-area/index.js +1041 -0
- package/src/components/table/plugins/cell-area/index.scss +77 -0
- package/src/components/table/plugins/cell-area/util.js +224 -0
- package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +2 -0
- package/src/components/xform/form-designer/indexMixin.js +7 -3
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/exportItemColumns-dialog.vue +7 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +36 -0
- package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +2 -0
- package/src/components/xform/form-render/container-item/data-table-mixin.js +98 -81
- package/src/components/xform/form-render/indexMixin.js +7 -3
- package/src/components/xform/utils/textMaskUtil.js +6 -3
- package/src/components/xform/utils/util.js +23 -0
- package/src/index.js +6 -1
package/package.json
CHANGED
|
@@ -3,6 +3,11 @@ let tmixins, tViewMixins, pmixins;
|
|
|
3
3
|
import axios from "axios";
|
|
4
4
|
import SparkMD5 from "spark-md5";
|
|
5
5
|
import settingConfig from "@/settings.js";
|
|
6
|
+
import {
|
|
7
|
+
registerPreviewInstance,
|
|
8
|
+
unregisterPreviewInstance,
|
|
9
|
+
collectPreviewList,
|
|
10
|
+
} from "./previewGroup.js";
|
|
6
11
|
|
|
7
12
|
let imFun = {
|
|
8
13
|
axios,
|
|
@@ -1372,6 +1377,8 @@ tViewMixins = {
|
|
|
1372
1377
|
attachments: [],
|
|
1373
1378
|
pictureList: [],
|
|
1374
1379
|
pictureDtoList: [],
|
|
1380
|
+
// 传给预览弹框的图片列表:可能跨字段汇总了整个表单的图片,见 previewGroup.js
|
|
1381
|
+
previewList: [],
|
|
1375
1382
|
showPropertiesDialog: false,
|
|
1376
1383
|
editAttachment: null,
|
|
1377
1384
|
showViewer: false,
|
|
@@ -1442,6 +1449,12 @@ tViewMixins = {
|
|
|
1442
1449
|
this.initFileServerInfo();
|
|
1443
1450
|
this.initFile();
|
|
1444
1451
|
},
|
|
1452
|
+
mounted() {
|
|
1453
|
+
registerPreviewInstance(this);
|
|
1454
|
+
},
|
|
1455
|
+
beforeDestroy() {
|
|
1456
|
+
unregisterPreviewInstance(this);
|
|
1457
|
+
},
|
|
1445
1458
|
methods: {
|
|
1446
1459
|
pasteEvent(e) {
|
|
1447
1460
|
e.stopPropagation();
|
|
@@ -1657,23 +1670,41 @@ tViewMixins = {
|
|
|
1657
1670
|
.concat(this.pictureDtoList.slice(0, pictureIndex));
|
|
1658
1671
|
return res;
|
|
1659
1672
|
},
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
let
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
+
isPictureAttachment(attachment) {
|
|
1674
|
+
if (!attachment) return false;
|
|
1675
|
+
let typeStr = Object.prototype.toString.call(attachment);
|
|
1676
|
+
if (typeStr == "[object Object]") {
|
|
1677
|
+
return this.$commonFileUtil.isPictureFile(attachment.url);
|
|
1678
|
+
}
|
|
1679
|
+
return this.$commonFileUtil.isPictureFile(attachment);
|
|
1680
|
+
},
|
|
1681
|
+
/**
|
|
1682
|
+
* 本字段当前的图片列表。现算而不用 pictureDtoList:后者靠 attachments 的引用监听维护,
|
|
1683
|
+
* 删除(splice)、父组件直接改 file 数组都不会触发,拿到的可能是旧列表。
|
|
1684
|
+
* previewGroup.js 汇总整表图片时也会调用它。
|
|
1685
|
+
*/
|
|
1686
|
+
getPreviewPictureList() {
|
|
1687
|
+
let list = [];
|
|
1688
|
+
(this.attachments || []).forEach((item) => {
|
|
1689
|
+
if (this.isPictureAttachment(item)) {
|
|
1690
|
+
list.push(item);
|
|
1673
1691
|
}
|
|
1674
|
-
return index == i;
|
|
1675
1692
|
});
|
|
1676
|
-
|
|
1693
|
+
return list;
|
|
1694
|
+
},
|
|
1695
|
+
openImagePreView(index) {
|
|
1696
|
+
let pictures = this.getPreviewPictureList();
|
|
1697
|
+
// 点中的图片在本字段图片里的下标 = 它前面有几张图片
|
|
1698
|
+
let pictureIndex = 0;
|
|
1699
|
+
for (let i = 0; i < index; i++) {
|
|
1700
|
+
if (this.isPictureAttachment(this.attachments[i])) {
|
|
1701
|
+
pictureIndex++;
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
// 上一张 / 下一张要能翻遍整个表单,列表拼的是同组所有字段的图片
|
|
1705
|
+
let { list, offset } = collectPreviewList(this, pictures);
|
|
1706
|
+
this.previewList = list;
|
|
1707
|
+
this.chooseIndex = offset + pictureIndex;
|
|
1677
1708
|
this.showViewer = true;
|
|
1678
1709
|
},
|
|
1679
1710
|
openPropertiesDialog(attachment) {
|
|
@@ -1682,6 +1713,8 @@ tViewMixins = {
|
|
|
1682
1713
|
},
|
|
1683
1714
|
closeViewer() {
|
|
1684
1715
|
this.showViewer = false;
|
|
1716
|
+
// 列表里可能持有别的字段的附件对象,关掉就释放
|
|
1717
|
+
this.previewList = [];
|
|
1685
1718
|
},
|
|
1686
1719
|
hasPreview(attachment) {
|
|
1687
1720
|
let fileServerPreviewEnabled = this.getFileServerPreviewEnabled();
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 附件图片预览分组
|
|
3
|
+
*
|
|
4
|
+
* 单个附件字段(baseUpload)只持有自己的图片,预览弹框的「上一张 / 下一张」原先也只能在
|
|
5
|
+
* 本字段内翻页。这里维护一份已挂载 baseUpload 实例的登记表:打开预览时把同一表单范围内
|
|
6
|
+
* 的所有图片按 DOM 顺序拼成一个列表交给 el-image-viewer,翻页于是能跨字段覆盖整个表单。
|
|
7
|
+
*
|
|
8
|
+
* 分组范围 = 从自身向上找到的第一个「边界元素」(见 SCOPE_SELECTOR)。弹框 / 抽屉 / 表单
|
|
9
|
+
* 都算边界,保证背景页面的图片不会串进弹框的预览里,明细表编辑行里的附件也只跟同一行翻页。
|
|
10
|
+
*
|
|
11
|
+
* 组件上可用 preview-group 覆盖:
|
|
12
|
+
* preview-group="xxx" 给同一字符串的实例强制同组(可跨边界)
|
|
13
|
+
* :preview-group="false" 退回只翻本字段(旧行为)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// 已挂载的 baseUpload 实例。组件 mounted 时登记、beforeDestroy 时注销
|
|
17
|
+
const instances = new Set();
|
|
18
|
+
|
|
19
|
+
// 分组边界:弹框、抽屉、表单
|
|
20
|
+
const SCOPE_SELECTOR = ".el-dialog,.el-drawer,.el-form,form";
|
|
21
|
+
|
|
22
|
+
export function registerPreviewInstance(vm) {
|
|
23
|
+
instances.add(vm);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function unregisterPreviewInstance(vm) {
|
|
27
|
+
instances.delete(vm);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 模板里写 preview-group 与 previewGroup 落到 $attrs 的 key 不同,两个都认
|
|
31
|
+
function readGroupAttr(vm) {
|
|
32
|
+
let attrs = (vm && vm.$attrs) || {};
|
|
33
|
+
return attrs.previewGroup !== undefined
|
|
34
|
+
? attrs.previewGroup
|
|
35
|
+
: attrs["preview-group"];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isGroupEnabled(vm) {
|
|
39
|
+
let value = readGroupAttr(vm);
|
|
40
|
+
return value !== false && value !== "false";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getScope(vm) {
|
|
44
|
+
let group = readGroupAttr(vm);
|
|
45
|
+
// 显式指定的分组名优先,此时不再看 DOM 位置
|
|
46
|
+
if (typeof group === "string") {
|
|
47
|
+
let name = group.trim();
|
|
48
|
+
if (name && name !== "true" && name !== "false") return name;
|
|
49
|
+
}
|
|
50
|
+
let el = vm.$el;
|
|
51
|
+
while (el && el.nodeType === 1) {
|
|
52
|
+
if (el.matches && el.matches(SCOPE_SELECTOR)) {
|
|
53
|
+
return el;
|
|
54
|
+
}
|
|
55
|
+
el = el.parentElement;
|
|
56
|
+
}
|
|
57
|
+
return (vm.$el && vm.$el.ownerDocument.body) || null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 隐藏的实例不参与分组:看不见的图片(未激活的页签、折叠面板)混进来翻页只会莫名其妙。
|
|
62
|
+
* 不用 getClientRects / offsetParent 判定——jsdom 没有排版,那两个恒为空。
|
|
63
|
+
*/
|
|
64
|
+
function isHidden(el) {
|
|
65
|
+
if (!el || el.nodeType !== 1 || el.isConnected === false) return true;
|
|
66
|
+
let view = el.ownerDocument && el.ownerDocument.defaultView;
|
|
67
|
+
if (!view || !view.getComputedStyle) return false;
|
|
68
|
+
let node = el;
|
|
69
|
+
while (node && node.nodeType === 1) {
|
|
70
|
+
let style = view.getComputedStyle(node);
|
|
71
|
+
if (style && (style.display === "none" || style.visibility === "hidden")) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
node = node.parentElement;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function compareDomOrder(a, b) {
|
|
80
|
+
if (a === b) return 0;
|
|
81
|
+
let relation = a.compareDocumentPosition(b);
|
|
82
|
+
// 2 = DOCUMENT_POSITION_PRECEDING, 4 = DOCUMENT_POSITION_FOLLOWING
|
|
83
|
+
if (relation & 4) return -1;
|
|
84
|
+
if (relation & 2) return 1;
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getPictures(vm) {
|
|
89
|
+
if (typeof vm.getPreviewPictureList !== "function") return [];
|
|
90
|
+
return vm.getPreviewPictureList() || [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 汇总 vm 所在分组的图片列表
|
|
95
|
+
* @param {Object} vm 触发预览的 baseUpload 实例
|
|
96
|
+
* @param {Array} ownList vm 自己的图片列表(调用方已算好,避免重复计算)
|
|
97
|
+
* @returns {{list: Array, offset: Number}} 拼接后的列表与 vm 自己的图片在其中的起始下标
|
|
98
|
+
*/
|
|
99
|
+
export function collectPreviewList(vm, ownList) {
|
|
100
|
+
let own = ownList || [];
|
|
101
|
+
if (!isGroupEnabled(vm)) {
|
|
102
|
+
return { list: own, offset: 0 };
|
|
103
|
+
}
|
|
104
|
+
let scope = getScope(vm);
|
|
105
|
+
let members = [];
|
|
106
|
+
instances.forEach((item) => {
|
|
107
|
+
if (item === vm || !item.$el) return;
|
|
108
|
+
if (!isGroupEnabled(item)) return;
|
|
109
|
+
if (getScope(item) !== scope) return;
|
|
110
|
+
if (isHidden(item.$el)) return;
|
|
111
|
+
members.push(item);
|
|
112
|
+
});
|
|
113
|
+
members.push(vm);
|
|
114
|
+
members.sort((a, b) => compareDomOrder(a.$el, b.$el));
|
|
115
|
+
|
|
116
|
+
let list = [];
|
|
117
|
+
let offset = 0;
|
|
118
|
+
members.forEach((item) => {
|
|
119
|
+
let pictures = item === vm ? own : getPictures(item);
|
|
120
|
+
if (item === vm) {
|
|
121
|
+
offset = list.length;
|
|
122
|
+
}
|
|
123
|
+
if (pictures.length) {
|
|
124
|
+
list = list.concat(pictures);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return { list, offset };
|
|
128
|
+
}
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
<script>
|
|
45
45
|
import xeUtils from "xe-utils";
|
|
46
46
|
import { isItemExport } from "./exportType";
|
|
47
|
+
import { getExportItemColumns } from "./exportItemConfigUtil";
|
|
47
48
|
export default {
|
|
48
49
|
name: "exportFieldDialog",
|
|
49
50
|
props: ["param"],
|
|
@@ -203,8 +204,7 @@ export default {
|
|
|
203
204
|
});
|
|
204
205
|
} else {
|
|
205
206
|
//导出子表数据
|
|
206
|
-
let
|
|
207
|
-
let oriExportItemColumns = originOption.exportItemConfig?.columns || [];
|
|
207
|
+
let oriExportItemColumns = this.getExportItemColumns();
|
|
208
208
|
|
|
209
209
|
// 与主表分支一致的三态:记为导出→勾选,记为不导出→不勾,两者都无(新增列)→ 默认勾选。
|
|
210
210
|
// 这里曾以 allFields.includes(field) 为前置门槛,但 allFields 只在 custom-config
|
|
@@ -263,16 +263,21 @@ export default {
|
|
|
263
263
|
this.$vxeTableUtil.setTableState($grid, "exportColumns", exportColumns);
|
|
264
264
|
} else {
|
|
265
265
|
//导出子表数据
|
|
266
|
-
let
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
let columns = this.getExportItemColumns();
|
|
267
|
+
if (columns.length) {
|
|
268
|
+
// 勾选记忆是整张表格共用的一条状态,而明细列可以按导出按钮各配一套,直接整体覆盖
|
|
269
|
+
// 会把其它按钮的记忆连带清掉,故按字段增量合并:本次导出涉及的字段更新,其余保留。
|
|
270
|
+
let exportItemColumns = (
|
|
271
|
+
this.$vxeTableUtil.getTableStateValue($grid, "exportItemColumns") || []
|
|
272
|
+
).map((item) => ({ ...item }));
|
|
273
|
+
this.loopColumnHandle(columns, (column) => {
|
|
274
|
+
if (!column.field || !column.title) return;
|
|
275
|
+
let item = exportItemColumns.find((t) => t.field === column.field);
|
|
276
|
+
if (!item) {
|
|
277
|
+
item = { field: column.field };
|
|
274
278
|
exportItemColumns.push(item);
|
|
275
279
|
}
|
|
280
|
+
item.export = checkLeafIds.includes(column.id);
|
|
276
281
|
});
|
|
277
282
|
this.$vxeTableUtil.setTableState(
|
|
278
283
|
$grid,
|
|
@@ -298,13 +303,12 @@ export default {
|
|
|
298
303
|
},
|
|
299
304
|
dialogConfirm() {
|
|
300
305
|
let target = this.getGrid();
|
|
301
|
-
let originOption = target.params.originOption;
|
|
302
306
|
let leafColumns = this.$refs.tree.getCheckedNodes(true);
|
|
303
307
|
let checkIds = this.$refs.tree.getCheckedNodes(false, true).map((item) => item.id);
|
|
304
308
|
let exportItem = isItemExport(this.param.type);
|
|
305
309
|
let collectColumn = !exportItem
|
|
306
310
|
? target.getTableColumn().collectColumn
|
|
307
|
-
:
|
|
311
|
+
: this.getExportItemColumns();
|
|
308
312
|
// 明细导出列未维护时这里是 undefined,下面 depthDo/loopDo 直接遍历会抛错
|
|
309
313
|
collectColumn = collectColumn || [];
|
|
310
314
|
// 按勾选后的实际层级算表头总行数(item.level 在导出明细列上不存在,不可依赖)
|
|
@@ -356,6 +360,13 @@ export default {
|
|
|
356
360
|
});
|
|
357
361
|
return cols;
|
|
358
362
|
}, */
|
|
363
|
+
/**
|
|
364
|
+
* @returns {Array} 本次导出实际生效的明细导出列:导出按钮上单独维护时以按钮为准,
|
|
365
|
+
* 否则取数据表格属性上配的那套。
|
|
366
|
+
*/
|
|
367
|
+
getExportItemColumns() {
|
|
368
|
+
return getExportItemColumns(this.param, this.getGrid());
|
|
369
|
+
},
|
|
359
370
|
getGrid() {
|
|
360
371
|
let that = this.param.vue;
|
|
361
372
|
let tableRef = this.param.targetRef;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 明细导出配置的取值口径。
|
|
3
|
+
*
|
|
4
|
+
* 明细导出列有两个来源:数据表格属性上的 `exportItemColumns`(表格级,历史唯一来源),
|
|
5
|
+
* 以及导出按钮自己维护的那份(按钮级,为的是同一张表格挂多个明细导出按钮、各导各自的列)。
|
|
6
|
+
* 按钮级的配置由 xform 的 exportData 组装好后随导出参数一路下发,表格级的挂在
|
|
7
|
+
* `$grid.params.originOption` 上,两者形状完全一致,**按钮优先**。
|
|
8
|
+
*
|
|
9
|
+
* 判定散在字段选择弹框、导出主流程、分页取数三处,口径必须一致,故收敛到这里。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {Object} exportOption 本次导出的参数($excelExport 的入参)。
|
|
14
|
+
* @param {Object} $grid vxe-grid 实例。
|
|
15
|
+
* @returns {Object|null} 本次导出实际生效的明细导出配置。
|
|
16
|
+
*/
|
|
17
|
+
export function getExportItemConfig(exportOption, $grid) {
|
|
18
|
+
let buttonConfig = exportOption?.exportItemConfig;
|
|
19
|
+
let tableConfig = $grid?.params?.originOption?.exportItemConfig;
|
|
20
|
+
return buttonConfig || tableConfig || null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {Object} exportOption 本次导出的参数($excelExport 的入参)。
|
|
25
|
+
* @param {Object} $grid vxe-grid 实例。
|
|
26
|
+
* @returns {Array} 本次导出实际生效的明细导出列;未维护时为空数组。
|
|
27
|
+
*/
|
|
28
|
+
export function getExportItemColumns(exportOption, $grid) {
|
|
29
|
+
return getExportItemConfig(exportOption, $grid)?.columns || [];
|
|
30
|
+
}
|
|
@@ -5,6 +5,7 @@ import indexUtil from "../../utils/index.js";
|
|
|
5
5
|
import exportFieldDialog from "./exportFieldDialog.vue";
|
|
6
6
|
import { getCellValue } from "@base/components/table/util/index";
|
|
7
7
|
import { isItemExport, isRowExport, requiresCheckedRows } from "./exportType";
|
|
8
|
+
import { getExportItemColumns } from "./exportItemConfigUtil";
|
|
8
9
|
|
|
9
10
|
let configUtil = {
|
|
10
11
|
baseUrl: process.env.VUE_APP_BASE_API,
|
|
@@ -110,10 +111,9 @@ function getGrid(that, tableRef) {
|
|
|
110
111
|
// 明细导出的列来自 exportItemConfig,未维护时字段树为空、表头 JSON 也无从生成,
|
|
111
112
|
// 必须在此拦掉:这里是所有导出的唯一入口,放行下去会在字段选择弹框里抛异常。
|
|
112
113
|
if (isItemExport(this.option.type)) {
|
|
113
|
-
|
|
114
|
-
if (!originOption.exportItemConfig?.columns?.length) {
|
|
114
|
+
if (!getExportItemColumns(this.option, $grid).length) {
|
|
115
115
|
this.$baseAlert(
|
|
116
|
-
this.$t1("
|
|
116
|
+
this.$t1("未维护明细导出列,请先在导出按钮或数据表格属性中配置")
|
|
117
117
|
);
|
|
118
118
|
this.param.destroyComponent();
|
|
119
119
|
return;
|