umy-ui-table 1.2.3 → 1.2.5
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/lib/button.js +1 -1
- package/lib/icon.js +1 -1
- package/lib/index.js +1 -1
- package/lib/locale/format.js +1 -1
- package/lib/locale/index.js +1 -72
- package/lib/mixins/locale.js +1 -1
- package/lib/theme-chalk/button.css +1 -1
- package/lib/theme-chalk/icon.css +1 -1
- package/lib/theme-chalk/iconfont.1785242763644.ttf +0 -0
- package/lib/theme-chalk/iconfont.1785242763644.woff +0 -0
- package/lib/theme-chalk/iconfont.1785242763644.woff2 +0 -0
- package/lib/theme-chalk/index.css +1 -1
- package/lib/theme-chalk/u-table.css +1 -1
- package/lib/theme-chalk/ux-grid.css +1 -1
- package/lib/u-table-column.js +1 -1
- package/lib/u-table.js +1 -1
- package/lib/umy-ui.common.js +1 -1
- package/lib/utils/util.js +1 -1
- package/lib/ux-grid.js +1 -1
- package/lib/ux-table-column.js +1 -1
- package/package.json +4 -3
- package/packages/.DS_Store +0 -0
- package/packages/input/.DS_Store +0 -0
- package/packages/pagination/.DS_Store +0 -0
- package/packages/scrollbar/.DS_Store +0 -0
- package/packages/select/.DS_Store +0 -0
- package/packages/table/.DS_Store +0 -0
- package/packages/tag/.DS_Store +0 -0
- package/packages/tools/.DS_Store +0 -0
- package/packages/tools/locale/index.js +2 -48
- package/packages/tools/utils/.DS_Store +0 -0
- package/packages/tooltip/.DS_Store +0 -0
- package/packages/u-table/.DS_Store +0 -0
- package/packages/ux-grid/.DS_Store +0 -0
- package/packages/ux-grid/mixins/index.js +136 -29
- package/packages/ux-grid/src/dialog-draft.js +75 -0
- package/packages/ux-grid/src/element-renderer.js +784 -0
- package/packages/ux-grid/src/grid-height.js +65 -0
- package/packages/ux-grid/src/grid-kernel.js +14 -0
- package/packages/ux-grid/src/setup.js +42 -0
- package/packages/ux-grid/src/ux-grid.vue +225 -44
- package/packages/ux-grid/test/browser-acceptance.cjs +192 -0
- package/packages/ux-grid/test/browser-baseline-loader.cjs +10 -0
- package/packages/ux-grid/test/browser-entry.js +242 -0
- package/packages/ux-grid/test/browser-renderer-contracts.cjs +246 -0
- package/packages/ux-grid/test/browser-renderer-entry.js +129 -0
- package/packages/ux-grid/test/browser-resize-contracts.cjs +150 -0
- package/packages/ux-grid/test/compatibility.cjs +176 -0
- package/packages/ux-grid/test/contracts.cjs +281 -0
- package/packages/ux-grid/test/delivery.cjs +103 -0
- package/packages/ux-grid/test/integration.cjs +132 -0
- package/packages/ux-grid/test/real-kernel-contracts.cjs +335 -0
- package/packages/ux-grid/test/real-kernel-events.cjs +233 -0
- package/packages/ux-grid/test/renderer-boundaries.cjs +165 -0
- package/packages/ux-grid/test/ui-components.cjs +103 -0
- package/tools/locale/format.js +1 -1
- package/tools/locale/index.js +1 -7
- package/tools/mixins/locale.js +1 -1
- package/tools/utils/util.js +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import ResizeObserver from "resize-observer-polyfill";
|
|
2
|
+
|
|
3
|
+
export function hasGridHeight(value) {
|
|
4
|
+
return value !== undefined && value !== null && value !== "";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Measure the configured limit in the root's actual grid/flex position. Size
|
|
8
|
+
// containment excludes table content from intrinsic sizing during measurement.
|
|
9
|
+
export function resolveGridHeight(value, root, isMax = false) {
|
|
10
|
+
if (!hasGridHeight(value)) return null;
|
|
11
|
+
if (typeof value === "number") return Number.isFinite(value) ? Math.max(0, value) : null;
|
|
12
|
+
const css = String(value).trim();
|
|
13
|
+
if (/^\d+(?:\.\d+)?(?:px)?$/i.test(css)) return Number.parseFloat(css);
|
|
14
|
+
if (!root || !root.parentElement || !root.getClientRects().length) return null;
|
|
15
|
+
const view = root.ownerDocument.defaultView;
|
|
16
|
+
const width = view.getComputedStyle(root).width;
|
|
17
|
+
const saved = root.style.cssText;
|
|
18
|
+
try {
|
|
19
|
+
const style = root.style;
|
|
20
|
+
style.removeProperty("height");
|
|
21
|
+
style.setProperty("height", css === "auto" ? "100%" : css, "important");
|
|
22
|
+
if (!style.height) return null;
|
|
23
|
+
style.setProperty("width", width, "important");
|
|
24
|
+
style.setProperty("min-height", "0", "important");
|
|
25
|
+
style.setProperty("max-height", isMax ? (css === "auto" ? "100%" : css) : "none", "important");
|
|
26
|
+
// A configured ceiling is not the space assigned by flex. Match numeric
|
|
27
|
+
// maxHeight semantics; restore the user's real flex rules in finally.
|
|
28
|
+
if (isMax) style.setProperty("flex", "none", "important");
|
|
29
|
+
style.setProperty("contain", "size", "important");
|
|
30
|
+
style.setProperty("overflow", "hidden", "important");
|
|
31
|
+
const size = Number.parseFloat(view.getComputedStyle(root).height);
|
|
32
|
+
if (size === 0 && (css === "auto" || css.indexOf("%") !== -1)) return null;
|
|
33
|
+
return Number.isFinite(size) && size >= 0 ? size : null;
|
|
34
|
+
} finally {
|
|
35
|
+
root.style.cssText = saved;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getGridHeightInset(root) {
|
|
40
|
+
if (!root) return 0;
|
|
41
|
+
const style = root.ownerDocument.defaultView.getComputedStyle(root);
|
|
42
|
+
return style.boxSizing === "border-box"
|
|
43
|
+
? ["paddingTop", "paddingBottom", "borderTopWidth", "borderBottomWidth"]
|
|
44
|
+
.reduce((sum, key) => sum + (Number.parseFloat(style[key]) || 0), 0)
|
|
45
|
+
: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function observeGridHeight(vm) {
|
|
49
|
+
const root = vm.$el;
|
|
50
|
+
if (!root || !root.parentElement) return () => {};
|
|
51
|
+
const observer = new ResizeObserver(() => vm.scheduleHeight());
|
|
52
|
+
observer.observe(root.parentElement);
|
|
53
|
+
observer.observe(root);
|
|
54
|
+
// Pagination can appear/disappear after mount; updated() reconnects when its
|
|
55
|
+
// element identity changes. Root observation also covers hidden -> visible.
|
|
56
|
+
const pager = vm.$refs.myPagination;
|
|
57
|
+
if (pager) observer.observe(pager);
|
|
58
|
+
const view = root.ownerDocument.defaultView;
|
|
59
|
+
const onResize = () => vm.scheduleHeight();
|
|
60
|
+
if (view) view.addEventListener("resize", onResize);
|
|
61
|
+
return () => {
|
|
62
|
+
observer.disconnect();
|
|
63
|
+
if (view) view.removeEventListener("resize", onResize);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import VxeGrid from "vxe-table/lib/grid";
|
|
2
|
+
|
|
3
|
+
// The upstream columns watcher queues loadColumn in nextTick. A wrapper can
|
|
4
|
+
// be destroyed before that callback runs, after its table refs are released.
|
|
5
|
+
// Keep this cancellation local to UxGrid; active calls retain kernel semantics.
|
|
6
|
+
export default {
|
|
7
|
+
extends: VxeGrid,
|
|
8
|
+
methods: {
|
|
9
|
+
loadColumn(...args) {
|
|
10
|
+
if (this._isDestroyed || this._isBeingDestroyed) return Promise.resolve();
|
|
11
|
+
return VxeGrid.methods.loadColumn.apply(this, args);
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { VxeUI } from "vxe-table/lib/ui";
|
|
2
|
+
import zhCN from "vxe-table/lib/locale/lang/zh-CN";
|
|
3
|
+
import XEUtils from "xe-utils";
|
|
4
|
+
|
|
5
|
+
// Core setConfig deep-merges arrays by index. Restore pre-existing arrays
|
|
6
|
+
// exactly after merging defaults (including empty arrays and nested arrays).
|
|
7
|
+
function restoreConfigArrays(target, source) {
|
|
8
|
+
Object.keys(source).forEach(key => {
|
|
9
|
+
const value = source[key];
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
target[key] = XEUtils.clone(value, true);
|
|
12
|
+
} else if (XEUtils.isPlainObject(value) && target[key]) {
|
|
13
|
+
restoreConfigArrays(target[key], value);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Register only missing UI dependencies. Importing PC UI initializes shared
|
|
19
|
+
// defaults, so preserve existing application config and icon customizations.
|
|
20
|
+
if (!VxeUI.getComponent("VxeTooltip") || !VxeUI.getComponent("VxeLoading")) {
|
|
21
|
+
const config = XEUtils.clone(VxeUI.getConfig(), true);
|
|
22
|
+
const icons = Object.assign({}, VxeUI.getIcon());
|
|
23
|
+
try {
|
|
24
|
+
if (!VxeUI.getComponent("VxeTooltip")) {
|
|
25
|
+
VxeUI.component(require("vxe-pc-ui/lib/tooltip/src/tooltip").default);
|
|
26
|
+
}
|
|
27
|
+
if (!VxeUI.getComponent("VxeLoading")) {
|
|
28
|
+
VxeUI.component(require("vxe-pc-ui/lib/loading/src/loading").default);
|
|
29
|
+
}
|
|
30
|
+
} finally {
|
|
31
|
+
VxeUI.setConfig(config);
|
|
32
|
+
restoreConfigArrays(VxeUI.getConfig(), config);
|
|
33
|
+
VxeUI.setIcon(icons);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Initialize fresh consumers without replacing an application's chosen language
|
|
38
|
+
// or translator. An already registered Chinese dictionary is retained as well.
|
|
39
|
+
if (!VxeUI.getLanguage() && !VxeUI.getConfig().i18n) {
|
|
40
|
+
if (!VxeUI.hasLanguage("zh-CN")) VxeUI.setI18n("zh-CN", zhCN);
|
|
41
|
+
VxeUI.setLanguage("zh-CN");
|
|
42
|
+
}
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
:border="beautifyTable ? false : border"
|
|
22
22
|
:scroll-y="scrollYConfig"
|
|
23
23
|
:scroll-x="scrollXConfig"
|
|
24
|
-
|
|
24
|
+
:virtual-y-config="computedVirtualYConfig"
|
|
25
|
+
:virtual-x-config="computedVirtualXConfig"
|
|
25
26
|
:stripe="stripe"
|
|
26
27
|
:row-key="rowKey"
|
|
27
28
|
:column-key="columnKey"
|
|
@@ -33,18 +34,20 @@
|
|
|
33
34
|
:expand-config="expandConfig"
|
|
34
35
|
:keyboard-config="keyboardConfig"
|
|
35
36
|
:mouse-config="mouseConfig"
|
|
37
|
+
:menu-config="{ enabled: true, header: { options: [] }, body: { options: [] }, footer: { options: [] } }"
|
|
36
38
|
:checkbox-config="checkboxConfig"
|
|
37
39
|
:tree-config="treeConfig"
|
|
38
40
|
:sort-config="sortConfig"
|
|
39
41
|
:show-header="showHeader"
|
|
40
42
|
:show-overflow="showOverflow"
|
|
43
|
+
:tooltip-config="{}"
|
|
41
44
|
:footer-method="footerMethod"
|
|
42
45
|
:show-header-overflow="showHeaderOverflow"
|
|
43
46
|
:show-footer-overflow="showFooterOverflow"
|
|
44
47
|
:highlight-current-row="highlightCurrentRow"
|
|
45
|
-
:highlight-hover-row="
|
|
48
|
+
:highlight-hover-row="highlightHoverRow"
|
|
46
49
|
:row-class-name="rowClassName"
|
|
47
|
-
:cell-class-name="
|
|
50
|
+
:cell-class-name="renderCellClassName"
|
|
48
51
|
:header-row-class-name="headerRowClassName"
|
|
49
52
|
:header-cell-class-name="headerCellClassName"
|
|
50
53
|
:header-row-style="headerRowStyle"
|
|
@@ -69,14 +72,17 @@
|
|
|
69
72
|
@edit-closed="editClosed"
|
|
70
73
|
@edit-actived="editActived"
|
|
71
74
|
@edit-disabled="editDisabled"
|
|
75
|
+
@cell-selected="editCurrentlyActive"
|
|
72
76
|
@toggle-tree-expand="toggleTreeExpandEv"
|
|
73
77
|
@toggle-row-expand="expChang"
|
|
74
78
|
@keydown="keydown"
|
|
75
|
-
@edit-currently-active="editCurrentlyActive"
|
|
76
79
|
@scroll="tableBodyScroll"
|
|
77
80
|
@init-rendered="initRendered"
|
|
78
81
|
@data-rendered="dataRendered"
|
|
79
82
|
>
|
|
83
|
+
<template v-for="(_, name) in forwardedScopedSlots()" v-slot:[name]="params">
|
|
84
|
+
<slot :name="name" v-bind="params" />
|
|
85
|
+
</template>
|
|
80
86
|
<!--无数据布局-->
|
|
81
87
|
<template slot="empty">
|
|
82
88
|
<slot name="empty">{{ emptyText }}</slot>
|
|
@@ -146,13 +152,15 @@ import draggable from "vuedraggable";
|
|
|
146
152
|
import ElPagination from "../../pagination";
|
|
147
153
|
import ElCheckbox from "../../checkbox";
|
|
148
154
|
import ElButton from "../../button";
|
|
155
|
+
import "./element-renderer";
|
|
149
156
|
// 直接用 vxe-table@3.22.9 的 VxeGrid 作内核(替代原 umy-table 内置的 elx-grid)
|
|
150
157
|
// 注意:VxeGrid 3.x 不渲染默认插槽(renderTable 仅透传 scopedSlots,不渲染 children),
|
|
151
158
|
// 所以槽位式 <ux-table-column> 不会被其内部表格收集;这里在组件内解析这些槽位列 vnode,
|
|
152
159
|
// 转成 columns 配置后经 :columns 交给 VxeGrid,从而兼容原有的 <ux-table-column> 写法
|
|
153
|
-
import VxeGrid from "
|
|
160
|
+
import VxeGrid from "./grid-kernel";
|
|
154
161
|
import uxGridMixin from "../mixins";
|
|
155
|
-
import {
|
|
162
|
+
import { isArrayFn } from "../../utils/common";
|
|
163
|
+
import { hasGridHeight, resolveGridHeight, getGridHeightInset, observeGridHeight } from "./grid-height";
|
|
156
164
|
|
|
157
165
|
export default {
|
|
158
166
|
name: "UxGrid",
|
|
@@ -169,6 +177,7 @@ export default {
|
|
|
169
177
|
newHeight: null, // 表格高度
|
|
170
178
|
newMaxHeight: null, // 表格最大高度
|
|
171
179
|
slotColumns: [], // 由槽位式 <ux-table-column> 解析出的列配置(无 columns prop 时使用)
|
|
180
|
+
slotFormatterRevision: 0,
|
|
172
181
|
};
|
|
173
182
|
},
|
|
174
183
|
computed: {
|
|
@@ -187,7 +196,9 @@ export default {
|
|
|
187
196
|
) {
|
|
188
197
|
return Object.assign({}, this.scrollY, { enabled: false });
|
|
189
198
|
}
|
|
190
|
-
return this.scrollY
|
|
199
|
+
return Object.assign({}, this.scrollY, {
|
|
200
|
+
enabled: this.scrollY && this.scrollY.enabled !== undefined ? this.scrollY.enabled : true,
|
|
201
|
+
});
|
|
191
202
|
},
|
|
192
203
|
scrollXConfig() {
|
|
193
204
|
const propsData = this.$options.propsData || {};
|
|
@@ -197,22 +208,30 @@ export default {
|
|
|
197
208
|
) {
|
|
198
209
|
return Object.assign({}, this.scrollX, { enabled: false });
|
|
199
210
|
}
|
|
200
|
-
return this.scrollX
|
|
211
|
+
return Object.assign({}, this.scrollX, {
|
|
212
|
+
enabled: this.scrollX && this.scrollX.enabled !== undefined ? this.scrollX.enabled : true,
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
// vxe3 新接口:优先使用 virtualYConfig/virtualXConfig,不存在时回退到兼容 scrollY/scrollX
|
|
216
|
+
computedVirtualYConfig() {
|
|
217
|
+
if (this.virtualYConfig) return this.virtualYConfig;
|
|
218
|
+
return this.scrollYConfig;
|
|
219
|
+
},
|
|
220
|
+
computedVirtualXConfig() {
|
|
221
|
+
if (this.virtualXConfig) return this.virtualXConfig;
|
|
222
|
+
return this.scrollXConfig;
|
|
201
223
|
},
|
|
202
224
|
plTableHeight() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const maxHeight = parseHeight(this.maxHeight);
|
|
209
|
-
if (typeof maxHeight === "number") {
|
|
210
|
-
return {
|
|
211
|
-
"max-height": this.maxHeight + "px",
|
|
212
|
-
};
|
|
213
|
-
}
|
|
225
|
+
const property = hasGridHeight(this.height) ? "height" : "max-height";
|
|
226
|
+
let value = property === "height" ? this.height : this.maxHeight;
|
|
227
|
+
if (!hasGridHeight(value)) return {};
|
|
228
|
+
if (typeof value === "number") {
|
|
229
|
+
return Number.isFinite(value) ? { [property]: Math.max(0, value) + "px" } : {};
|
|
214
230
|
}
|
|
215
|
-
|
|
231
|
+
value = String(value).trim();
|
|
232
|
+
// Keep the original CSS basis for flex/grid. Only the kernel receives
|
|
233
|
+
// the measured pixels; writing them here would redistribute flex space.
|
|
234
|
+
return { [property]: value === "auto" ? "100%" : /^\d+(?:\.\d+)?$/.test(value) ? value + "px" : value };
|
|
216
235
|
},
|
|
217
236
|
},
|
|
218
237
|
watch: {
|
|
@@ -234,14 +253,23 @@ export default {
|
|
|
234
253
|
immediate: true,
|
|
235
254
|
handler() {
|
|
236
255
|
this.setHeight();
|
|
256
|
+
this.scheduleHeight();
|
|
237
257
|
},
|
|
238
258
|
},
|
|
239
259
|
maxHeight: {
|
|
240
260
|
immediate: true,
|
|
241
261
|
handler() {
|
|
242
262
|
this.setHeight();
|
|
263
|
+
this.scheduleHeight();
|
|
243
264
|
},
|
|
244
265
|
},
|
|
266
|
+
paginationShow: "scheduleHeight",
|
|
267
|
+
layout: "scheduleHeight",
|
|
268
|
+
pagerCount: "scheduleHeight",
|
|
269
|
+
pageSizes: "scheduleHeight",
|
|
270
|
+
total: "scheduleHeight",
|
|
271
|
+
newPageSize: "scheduleHeight",
|
|
272
|
+
newcurrentPage: "scheduleHeight",
|
|
245
273
|
},
|
|
246
274
|
created() {
|
|
247
275
|
this.newPageSize = this.pageSize;
|
|
@@ -252,32 +280,114 @@ export default {
|
|
|
252
280
|
updated() {
|
|
253
281
|
// 处理 <ux-table-column v-if> 动态增减列的场景
|
|
254
282
|
this.syncSlotColumns();
|
|
283
|
+
this.connectHeightObserver();
|
|
255
284
|
},
|
|
256
285
|
mounted() {
|
|
257
286
|
this.$ready = true;
|
|
287
|
+
this.connectHeightObserver();
|
|
288
|
+
this.setHeight();
|
|
258
289
|
// VxeGrid 原生支持 columns prop(mounted 自动 loadColumn + watch 响应变化),无需手动 loadColumn
|
|
259
290
|
},
|
|
291
|
+
activated() {
|
|
292
|
+
this._heightInactive = false;
|
|
293
|
+
this._heightLayoutSignature = null;
|
|
294
|
+
this.connectHeightObserver();
|
|
295
|
+
this.scheduleHeight();
|
|
296
|
+
},
|
|
297
|
+
deactivated() {
|
|
298
|
+
this._heightInactive = true;
|
|
299
|
+
this.disconnectHeightObserver();
|
|
300
|
+
},
|
|
301
|
+
beforeDestroy() {
|
|
302
|
+
this.disconnectHeightObserver();
|
|
303
|
+
},
|
|
260
304
|
methods: {
|
|
305
|
+
renderCellClassName(params) {
|
|
306
|
+
// Every visible cell tracks this revision, including formatter cache hits.
|
|
307
|
+
this.slotFormatterRevision;
|
|
308
|
+
return typeof this.cellClassName === "function" ? this.cellClassName(params) : this.cellClassName;
|
|
309
|
+
},
|
|
310
|
+
forwardedScopedSlots() {
|
|
311
|
+
const slots = {};
|
|
312
|
+
Object.keys(this.$scopedSlots).forEach((name) => {
|
|
313
|
+
if (name !== "default" && name !== "empty") slots[name] = this.$scopedSlots[name];
|
|
314
|
+
});
|
|
315
|
+
return slots;
|
|
316
|
+
},
|
|
261
317
|
// 解析默认插槽里的 <ux-table-column> vnode,转成 VxeGrid 的 columns 配置
|
|
262
318
|
syncSlotColumns() {
|
|
263
319
|
// columns prop 优先;仅在未传 columns 时解析槽位列
|
|
264
320
|
if (this.columns && this.columns.length) return;
|
|
265
321
|
try {
|
|
266
322
|
const slotVnodes = (this.$slots.default || []).filter(
|
|
267
|
-
(vnode) => vnode
|
|
323
|
+
(vnode) => this.isColumnVNode(vnode)
|
|
268
324
|
);
|
|
269
|
-
//
|
|
325
|
+
// 比较父组件提供的 vnode;自身更新不会改变这些引用,避免渲染循环。
|
|
270
326
|
const sig = this.columnsSig(slotVnodes);
|
|
271
327
|
if (sig === this._slotColumnsSig) return;
|
|
272
328
|
this._slotColumnsSig = sig;
|
|
273
|
-
|
|
329
|
+
const columns = slotVnodes.map((vnode) => this.vnodeToColumn(vnode));
|
|
330
|
+
// Only render callbacks may change without loading columns again. Stable
|
|
331
|
+
// proxies retain VXE ColumnInfo, sort, resized width and edit identity.
|
|
332
|
+
const structure = this.columnsSig(slotVnodes, true);
|
|
333
|
+
if (structure === this._slotColumnsStructure && this._slotRenderSources) {
|
|
334
|
+
let index = 0;
|
|
335
|
+
let formatterChanged = false;
|
|
336
|
+
const update = (items) => items.forEach((column) => {
|
|
337
|
+
const source = this._slotRenderSources[index++];
|
|
338
|
+
if (source.column.formatter !== column.formatter) formatterChanged = true;
|
|
339
|
+
source.column = column;
|
|
340
|
+
source.revision.value++;
|
|
341
|
+
if (column.children) update(column.children);
|
|
342
|
+
});
|
|
343
|
+
update(columns);
|
|
344
|
+
const grid = this.$refs && this.$refs.singleTable;
|
|
345
|
+
if (formatterChanged && grid && grid.clearFormatterCache) {
|
|
346
|
+
grid.clearFormatterCache();
|
|
347
|
+
// Refresh visible cell rendering, without sorting/filtering all data.
|
|
348
|
+
this.slotFormatterRevision++;
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
this._slotColumnsStructure = structure;
|
|
352
|
+
this._slotRenderSources = [];
|
|
353
|
+
const wrap = (items) => items.map((column) => {
|
|
354
|
+
const revision = Vue.observable ? Vue.observable({ value: 0 }) : { value: 0 };
|
|
355
|
+
const source = { column, revision };
|
|
356
|
+
this._slotRenderSources.push(source);
|
|
357
|
+
const result = Object.assign({}, column);
|
|
358
|
+
if (typeof column.formatter === "function") {
|
|
359
|
+
result.formatter = function (...args) {
|
|
360
|
+
source.revision.value;
|
|
361
|
+
return source.column.formatter.apply(this, args);
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (column.slots) {
|
|
365
|
+
result.slots = {};
|
|
366
|
+
Object.keys(column.slots).forEach((name) => {
|
|
367
|
+
const value = column.slots[name];
|
|
368
|
+
result.slots[name] = typeof value === "function" ? (...args) => {
|
|
369
|
+
source.revision.value;
|
|
370
|
+
return source.column.slots[name](...args);
|
|
371
|
+
} : value;
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
if (column.children) result.children = wrap(column.children);
|
|
375
|
+
return result;
|
|
376
|
+
});
|
|
377
|
+
this.slotColumns = wrap(columns);
|
|
378
|
+
}
|
|
274
379
|
} catch (e) {
|
|
275
|
-
|
|
380
|
+
console.error("[UxGrid] 无法解析槽位列", e);
|
|
276
381
|
}
|
|
277
382
|
},
|
|
383
|
+
isColumnVNode(vnode) {
|
|
384
|
+
const options = vnode && vnode.componentOptions;
|
|
385
|
+
const name = options && options.Ctor && options.Ctor.options.name;
|
|
386
|
+
return ["UxTableColumn", "VxeColumn", "VxeColgroup"].indexOf(name) > -1;
|
|
387
|
+
},
|
|
278
388
|
// 把单个 <ux-table-column> vnode 转成列配置(支持嵌套分组列)
|
|
279
389
|
vnodeToColumn(vnode) {
|
|
280
|
-
if (!vnode
|
|
390
|
+
if (!this.isColumnVNode(vnode)) return null;
|
|
281
391
|
const { propsData = {}, children } = vnode.componentOptions;
|
|
282
392
|
const scopedSlots = (vnode.data && vnode.data.scopedSlots) || {};
|
|
283
393
|
const column = Object.assign({}, propsData);
|
|
@@ -290,25 +400,48 @@ export default {
|
|
|
290
400
|
}
|
|
291
401
|
if (children && children.length) {
|
|
292
402
|
const nested = [];
|
|
403
|
+
const normalSlots = {};
|
|
293
404
|
children.forEach((child) => {
|
|
294
405
|
const sub = this.vnodeToColumn(child);
|
|
295
406
|
if (sub) nested.push(sub);
|
|
407
|
+
else if (!child.isComment && !(child.text && !child.text.trim())) {
|
|
408
|
+
const name = (child.data && child.data.slot) || "default";
|
|
409
|
+
const nodes = normalSlots[name] || (normalSlots[name] = []);
|
|
410
|
+
if (child.tag === "template") nodes.push(...(child.children || []));
|
|
411
|
+
else nodes.push(child);
|
|
412
|
+
}
|
|
296
413
|
});
|
|
297
414
|
if (nested.length) column.children = nested;
|
|
415
|
+
Object.keys(normalSlots).forEach((name) => {
|
|
416
|
+
if (!column.slots) column.slots = {};
|
|
417
|
+
if (!column.slots[name]) column.slots[name] = () => normalSlots[name];
|
|
418
|
+
});
|
|
298
419
|
}
|
|
299
420
|
return column;
|
|
300
421
|
},
|
|
301
|
-
//
|
|
302
|
-
columnsSig(vnodes) {
|
|
422
|
+
// 保留函数和普通 vnode 身份,使动态 slot/formatter 和 v-for 闭包及时更新。
|
|
423
|
+
columnsSig(vnodes, structureOnly) {
|
|
424
|
+
if (!this._columnReferenceIds) {
|
|
425
|
+
this._columnReferenceIds = new WeakMap();
|
|
426
|
+
this._columnReferenceSeq = 0;
|
|
427
|
+
}
|
|
428
|
+
const referenceId = (value) => {
|
|
429
|
+
if (!this._columnReferenceIds.has(value)) {
|
|
430
|
+
this._columnReferenceIds.set(value, ++this._columnReferenceSeq);
|
|
431
|
+
}
|
|
432
|
+
return this._columnReferenceIds.get(value);
|
|
433
|
+
};
|
|
303
434
|
const fnReplacer = (key, val) =>
|
|
304
|
-
typeof val === "function" ?
|
|
435
|
+
typeof val === "function" ? `[fn:${referenceId(val)}]` : val;
|
|
305
436
|
const describe = (vnode) => {
|
|
306
|
-
if (!vnode
|
|
437
|
+
if (!vnode) return "";
|
|
438
|
+
if (!this.isColumnVNode(vnode)) return structureOnly ? `slot:${(vnode.data && vnode.data.slot) || "default"}` : `node:${referenceId(vnode)}`;
|
|
307
439
|
const { propsData = {}, children } = vnode.componentOptions;
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
).
|
|
311
|
-
|
|
440
|
+
const slots = (vnode.data && vnode.data.scopedSlots) || {};
|
|
441
|
+
const signatureProps = Object.assign({}, propsData);
|
|
442
|
+
if (structureOnly && typeof signatureProps.formatter === "function") signatureProps.formatter = "[formatter]";
|
|
443
|
+
const signatureSlots = structureOnly ? Object.keys(slots).filter((key) => key.charAt(0) !== "$").sort() : slots;
|
|
444
|
+
let sig = JSON.stringify([vnode.key, signatureProps, signatureSlots], fnReplacer);
|
|
312
445
|
if (children && children.length) {
|
|
313
446
|
const childSigs = [];
|
|
314
447
|
children.forEach((child) => {
|
|
@@ -340,21 +473,69 @@ export default {
|
|
|
340
473
|
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
|
|
341
474
|
return arr;
|
|
342
475
|
},
|
|
476
|
+
// Measure after the pagination DOM has applied its new layout. Coalesce
|
|
477
|
+
// related prop changes and never keep retrying a not-yet-mounted instance.
|
|
478
|
+
scheduleHeight() {
|
|
479
|
+
if (!this.$ready || this._heightInactive || this._heightUpdatePending || this._isDestroyed || this._isBeingDestroyed) return;
|
|
480
|
+
this._heightUpdatePending = true;
|
|
481
|
+
this.$nextTick(() => {
|
|
482
|
+
this._heightUpdatePending = false;
|
|
483
|
+
this.setHeight();
|
|
484
|
+
});
|
|
485
|
+
},
|
|
486
|
+
disconnectHeightObserver() {
|
|
487
|
+
if (this._stopHeightObserver) this._stopHeightObserver();
|
|
488
|
+
this._stopHeightObserver = null;
|
|
489
|
+
this._heightObservedParent = null;
|
|
490
|
+
this._heightObservedPager = null;
|
|
491
|
+
},
|
|
492
|
+
connectHeightObserver() {
|
|
493
|
+
if (!this.$ready || this._heightInactive || this._isDestroyed || this._isBeingDestroyed) return;
|
|
494
|
+
if (!hasGridHeight(this.height) && !hasGridHeight(this.maxHeight)) {
|
|
495
|
+
this.disconnectHeightObserver();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const parent = this.$el && this.$el.parentElement;
|
|
499
|
+
const pager = this.$refs.myPagination;
|
|
500
|
+
if (this._stopHeightObserver && this._heightObservedParent === parent && this._heightObservedPager === pager) return;
|
|
501
|
+
this.disconnectHeightObserver();
|
|
502
|
+
this._heightObservedParent = parent;
|
|
503
|
+
this._heightObservedPager = pager;
|
|
504
|
+
this._stopHeightObserver = observeGridHeight(this);
|
|
505
|
+
this.scheduleHeight();
|
|
506
|
+
},
|
|
343
507
|
// 设置表格高度
|
|
344
508
|
setHeight() {
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
509
|
+
if (this._heightInactive || this._isDestroyed || this._isBeingDestroyed) return;
|
|
510
|
+
const myPagination = this.paginationShow && this.$refs && this.$refs.myPagination;
|
|
511
|
+
const paginationHeight = myPagination ? myPagination.offsetHeight : 0;
|
|
512
|
+
const total = resolveGridHeight(hasGridHeight(this.height) ? this.height : this.maxHeight, this.$el, !hasGridHeight(this.height));
|
|
513
|
+
const tableHeight = total === null ? null : Math.max(0, total - paginationHeight - getGridHeightInset(this.$el));
|
|
514
|
+
if (hasGridHeight(this.height)) {
|
|
515
|
+
this.newHeight = tableHeight;
|
|
352
516
|
this.newMaxHeight = null;
|
|
353
|
-
} else if (this.maxHeight) {
|
|
354
|
-
this.newMaxHeight =
|
|
355
|
-
|
|
356
|
-
|
|
517
|
+
} else if (hasGridHeight(this.maxHeight)) {
|
|
518
|
+
this.newMaxHeight = tableHeight;
|
|
519
|
+
this.newHeight = null;
|
|
520
|
+
} else {
|
|
357
521
|
this.newHeight = null;
|
|
522
|
+
this.newMaxHeight = null;
|
|
523
|
+
this._heightLayoutSignature = null;
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const root = this.$el;
|
|
527
|
+
const signature = root ? root.clientWidth + ":" + !!root.getClientRects().length : null;
|
|
528
|
+
if (signature !== this._heightLayoutSignature) {
|
|
529
|
+
this._heightLayoutSignature = signature;
|
|
530
|
+
// Reopening a hidden/kept-alive container may retain the same height
|
|
531
|
+
// props. Refresh widths through the public API, respecting opt-out.
|
|
532
|
+
if (root && root.getClientRects().length && this.widthResize) {
|
|
533
|
+
this.$nextTick(() => {
|
|
534
|
+
if (this._heightInactive || this._isDestroyed || this._isBeingDestroyed) return;
|
|
535
|
+
const table = this.$refs.singleTable;
|
|
536
|
+
if (table && table.recalculate) table.recalculate();
|
|
537
|
+
});
|
|
538
|
+
}
|
|
358
539
|
}
|
|
359
540
|
},
|
|
360
541
|
},
|