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.
Files changed (59) hide show
  1. package/lib/button.js +1 -1
  2. package/lib/icon.js +1 -1
  3. package/lib/index.js +1 -1
  4. package/lib/locale/format.js +1 -1
  5. package/lib/locale/index.js +1 -72
  6. package/lib/mixins/locale.js +1 -1
  7. package/lib/theme-chalk/button.css +1 -1
  8. package/lib/theme-chalk/icon.css +1 -1
  9. package/lib/theme-chalk/iconfont.1785242763644.ttf +0 -0
  10. package/lib/theme-chalk/iconfont.1785242763644.woff +0 -0
  11. package/lib/theme-chalk/iconfont.1785242763644.woff2 +0 -0
  12. package/lib/theme-chalk/index.css +1 -1
  13. package/lib/theme-chalk/u-table.css +1 -1
  14. package/lib/theme-chalk/ux-grid.css +1 -1
  15. package/lib/u-table-column.js +1 -1
  16. package/lib/u-table.js +1 -1
  17. package/lib/umy-ui.common.js +1 -1
  18. package/lib/utils/util.js +1 -1
  19. package/lib/ux-grid.js +1 -1
  20. package/lib/ux-table-column.js +1 -1
  21. package/package.json +4 -3
  22. package/packages/.DS_Store +0 -0
  23. package/packages/input/.DS_Store +0 -0
  24. package/packages/pagination/.DS_Store +0 -0
  25. package/packages/scrollbar/.DS_Store +0 -0
  26. package/packages/select/.DS_Store +0 -0
  27. package/packages/table/.DS_Store +0 -0
  28. package/packages/tag/.DS_Store +0 -0
  29. package/packages/tools/.DS_Store +0 -0
  30. package/packages/tools/locale/index.js +2 -48
  31. package/packages/tools/utils/.DS_Store +0 -0
  32. package/packages/tooltip/.DS_Store +0 -0
  33. package/packages/u-table/.DS_Store +0 -0
  34. package/packages/ux-grid/.DS_Store +0 -0
  35. package/packages/ux-grid/mixins/index.js +136 -29
  36. package/packages/ux-grid/src/dialog-draft.js +75 -0
  37. package/packages/ux-grid/src/element-renderer.js +784 -0
  38. package/packages/ux-grid/src/grid-height.js +65 -0
  39. package/packages/ux-grid/src/grid-kernel.js +14 -0
  40. package/packages/ux-grid/src/setup.js +42 -0
  41. package/packages/ux-grid/src/ux-grid.vue +225 -44
  42. package/packages/ux-grid/test/browser-acceptance.cjs +192 -0
  43. package/packages/ux-grid/test/browser-baseline-loader.cjs +10 -0
  44. package/packages/ux-grid/test/browser-entry.js +242 -0
  45. package/packages/ux-grid/test/browser-renderer-contracts.cjs +246 -0
  46. package/packages/ux-grid/test/browser-renderer-entry.js +129 -0
  47. package/packages/ux-grid/test/browser-resize-contracts.cjs +150 -0
  48. package/packages/ux-grid/test/compatibility.cjs +176 -0
  49. package/packages/ux-grid/test/contracts.cjs +281 -0
  50. package/packages/ux-grid/test/delivery.cjs +103 -0
  51. package/packages/ux-grid/test/integration.cjs +132 -0
  52. package/packages/ux-grid/test/real-kernel-contracts.cjs +335 -0
  53. package/packages/ux-grid/test/real-kernel-events.cjs +233 -0
  54. package/packages/ux-grid/test/renderer-boundaries.cjs +165 -0
  55. package/packages/ux-grid/test/ui-components.cjs +103 -0
  56. package/tools/locale/format.js +1 -1
  57. package/tools/locale/index.js +1 -7
  58. package/tools/mixins/locale.js +1 -1
  59. package/tools/utils/util.js +1 -1
@@ -1 +1 @@
1
- module.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist/",n(n.s=35)}({33:function(e,t){e.exports=require("vxe-table/lib/column")},35:function(e,t,n){"use strict";n.r(t);var r=n(33),o=n.n(r),u=Object.assign({},o.a,{name:"UxTableColumn"});u.install=function(e){e.component(u.name,u)},t.default=u}});
1
+ module.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist/",n(n.s=39)}({35:function(e,t){e.exports=require("vxe-table/lib/column")},39:function(e,t,n){"use strict";n.r(t);var r=n(35),o=n.n(r),u=Object.assign({},o.a,{name:"UxTableColumn"});u.install=function(e){e.component(u.name,u)},t.default=u}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umy-ui-table",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "一个基于 vue 的 PC 端表格UI库,解决万级数据渲染卡顿问题,过万数据点击全选卡顿,等等问题",
5
5
  "author": {
6
6
  "name": "foriacus"
@@ -9,7 +9,7 @@
9
9
  "lint": "eslint --fix --ext .js,.vue examples",
10
10
  "clean": "rimraf lib && rimraf packages/*/lib",
11
11
  "lib:file": "node build/build-entry.js",
12
- "lib:utils": "cross-env BABEL_ENV=utils babel tools --out-dir lib",
12
+ "lib:utils": "cross-env BABEL_ENV=utils babel tools --out-dir lib && webpack --config build/webpack.locale.js",
13
13
  "lib:umd": "node build/build-locale.js",
14
14
  "lib:theme": "gulp build",
15
15
  "lib": "npm run clean && npm run lib:file && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run lib:utils && npm run lib:umd && npm run lib:theme"
@@ -34,6 +34,7 @@
34
34
  "url": "git+https://github.com/popo-fishes/umy-ui.git"
35
35
  },
36
36
  "dependencies": {
37
+ "babel-helper-vue-jsx-merge-props": "^2.0.0",
37
38
  "async-validator": "~1.8.1",
38
39
  "deepmerge": "^1.2.0",
39
40
  "normalize-wheel": "^1.0.1",
@@ -41,6 +42,7 @@
41
42
  "throttle-debounce": "^1.0.1",
42
43
  "vuedraggable": "^2.23.2",
43
44
  "vxe-table": "^3.22.9",
45
+ "vxe-pc-ui": "3.16.23",
44
46
  "@vxe-ui/core": "^3.4.19",
45
47
  "xe-utils": ">= 3"
46
48
  },
@@ -54,7 +56,6 @@
54
56
  "animejs": "^3.1.0",
55
57
  "babel-cli": "^6.26.0",
56
58
  "babel-core": "^6.26.3",
57
- "babel-helper-vue-jsx-merge-props": "^2.0.0",
58
59
  "babel-loader": "^7.1.5",
59
60
  "babel-plugin-add-module-exports": "^0.2.1",
60
61
  "babel-plugin-istanbul": "^4.1.1",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,48 +1,2 @@
1
- import defaultLang from './lang/zh-CN';
2
- import Vue from 'vue';
3
- import deepmerge from 'deepmerge';
4
- import Format from './format';
5
-
6
- const format = Format(Vue);
7
- let lang = defaultLang;
8
- let merged = false;
9
- let i18nHandler = function() {
10
- const vuei18n = Object.getPrototypeOf(this || Vue).$t;
11
- if (typeof vuei18n === 'function' && !!Vue.locale) {
12
- if (!merged) {
13
- merged = true;
14
- Vue.locale(
15
- Vue.config.lang,
16
- deepmerge(lang, Vue.locale(Vue.config.lang) || {}, { clone: true })
17
- );
18
- }
19
- return vuei18n.apply(this, arguments);
20
- }
21
- };
22
-
23
- export const t = function(path, options) {
24
- let value = i18nHandler.apply(this, arguments);
25
- if (value !== null && value !== undefined) return value;
26
- const array = path.split('.');
27
- let current = lang;
28
- for (let i = 0, j = array.length; i < j; i++) {
29
- const property = array[i];
30
- value = current[property];
31
- if (i === j - 1) {
32
- return format(value, options)
33
- }
34
- if (!value) return '';
35
- current = value;
36
- }
37
- return '';
38
- };
39
-
40
- export const use = function(l) {
41
- lang = l || lang;
42
- };
43
-
44
- export const i18n = function(fn) {
45
- i18nHandler = fn || i18nHandler;
46
- };
47
-
48
- export default { use, t, i18n };
1
+ // Table, pagination and the public locale entry share one language store.
2
+ export { default, use, t, i18n } from "../../../tools/locale";
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,41 @@
1
1
  import { isArrayFn } from "../../utils/common";
2
+ import { createDialogDraft, mergeDialogDraft } from "../src/dialog-draft";
3
+ // Vue 2 instance listeners ($on/$once) are not included in $listeners.
4
+ function hasGridListener(vm, name) {
5
+ const events = vm._events && vm._events[name];
6
+ return !!((vm.$listeners && vm.$listeners[name]) || (events && events.length));
7
+ }
8
+ function bridgeStoppedClick(vm, params, header) {
9
+ if (vm._isDestroyed || vm._isBeingDestroyed) return;
10
+ const event = params && params.$event;
11
+ if (!event || event.type !== "click" || !event.target || !event.target.closest) return;
12
+ // Only VXE's built-in controls stop propagation. Row/cell-triggered actions
13
+ // must reach the native cell/header handler so current-change stays ordered.
14
+ if (!event.target.closest(".vxe-cell--checkbox,.vxe-cell--radio,.vxe-table--expand-btn,.vxe-cell--tree-btn,.vxe-sort--asc-btn,.vxe-sort--desc-btn")) return;
15
+ if (!hasGridListener(vm, header ? "header-click" : "cell-click") &&
16
+ (header || !hasGridListener(vm, "row-click"))) return;
17
+ const cell = event.target.closest(header ? "th" : "td");
18
+ const table = vm.$refs.singleTable;
19
+ const columnNode = cell && table && table.getColumnNode ? table.getColumnNode(cell) : null;
20
+ const column = params.column || (columnNode && columnNode.item);
21
+ if (!cell || !column) return;
22
+ const payload = Object.assign({}, params, { column, cell });
23
+ if (header) vm.headerClick(payload);
24
+ else vm.cellClick(payload);
25
+ }
26
+ function repeatedClick(vm, key, event) {
27
+ if (!event) return false;
28
+ const seen = vm[key] || (vm[key] = new WeakSet());
29
+ if (seen.has(event)) return true;
30
+ seen.add(event);
31
+ return false;
32
+ }
2
33
  export default {
34
+ beforeDestroy() {
35
+ this.stopDialogSync();
36
+ clearTimeout(this._times);
37
+ this.clearNode();
38
+ },
3
39
  props: {
4
40
  beautifyTable: { default: false, type: Boolean }, // 美化表格,修改了样式
5
41
  showDialogIcon: { default: true, type: Boolean }, // 是否显示操作字段的上下移动按钮(侧滑框)
@@ -24,6 +60,8 @@ export default {
24
60
  return { enabled: true, gt: 24, oSize: 0 };
25
61
  },
26
62
  }, // 横向虚拟滚动配置(列数超过 gt 时启用)
63
+ virtualYConfig: Object, // vxe3 新接口:纵向虚拟滚动(优先于 scrollY)
64
+ virtualXConfig: Object, // vxe3 新接口:横向虚拟滚动(优先于 scrollX)
27
65
  widthResize: Boolean,
28
66
  mergeCells: Array,
29
67
  validConfig: Object, // 校验配置项
@@ -51,6 +89,7 @@ export default {
51
89
  columnKey: Boolean, // 是否需要为每一列的 VNode 设置 key 属性(非特殊情况下不需要使用, 常用于拖拽列)
52
90
  rowId: String, // 自定义行数据唯一主键的字段名(行数据必须要有唯一主键,默认自动生成)
53
91
  highlightCurrentRow: { default: true, type: Boolean }, // 是否要高亮当前行
92
+ highlightHoverRow: { default: true, type: Boolean }, // 鼠标移到行是否要高亮显示
54
93
  showAmend: { default: false, type: Boolean }, // 是否显示修改字段名按钮(侧滑框)
55
94
  amendBtnIcon: { default: "", type: String }, // 修改字段按钮的图标(侧滑框)
56
95
  fieldTitle: { default: "选择显示字段", type: String }, // 弹框的标题(侧滑框)
@@ -92,9 +131,10 @@ export default {
92
131
  },
93
132
  // 打开自定义字段框
94
133
  plDialogOpens() {
95
- this._times = null;
96
- // 打开时同步一次最新 dialogData(替代 deep watcher,避免每次变更都深拷贝)
97
- this.newDialogData = JSON.parse(JSON.stringify(this.dialogData));
134
+ if (this._dialogUnwatch) return;
135
+ this._dialogDraft = createDialogDraft(this.dialogData);
136
+ this.newDialogData = this._dialogDraft.draft.slice();
137
+ this._dialogUnwatch = this.$watch("dialogData", () => this.syncDialogDraft(), { deep: true });
98
138
  this.$refs.plDialog.style.width = 300 + "px";
99
139
  this._times = setTimeout(() => {
100
140
  this.plDialogFals = true;
@@ -110,31 +150,43 @@ export default {
110
150
  },
111
151
  // 关闭自定义字段框(取消选择)
112
152
  closeModal() {
153
+ this.stopDialogSync();
113
154
  this.plDialogFals = false;
114
155
  clearTimeout(this._times);
115
156
  this.$refs.plDialog.style.width = 0 + "px";
116
- this.aBox.style.display = "none";
117
157
  this.clearNode();
118
158
  this.newDialogData = JSON.parse(JSON.stringify(this.dialogData));
119
159
  },
120
160
  // 确认按钮
121
161
  confirmField() {
162
+ this.syncDialogDraft();
163
+ this.stopDialogSync();
122
164
  this.$emit("show-field", this.newDialogData);
123
165
  this.plDialogFals = false;
124
166
  clearTimeout(this._times);
125
167
  this.$refs.plDialog.style.width = 0 + "px";
126
- this.aBox.style.display = "none";
127
168
  this.clearNode();
128
169
  },
129
170
  // 重置按钮
130
171
  reset() {
172
+ this.syncDialogDraft();
173
+ this.stopDialogSync();
131
174
  this.$emit("reset", this.newDialogData);
132
175
  this.plDialogFals = false;
133
176
  clearTimeout(this._times);
134
177
  this.$refs.plDialog.style.width = 0 + "px";
135
- this.aBox.style.display = "none";
136
178
  this.clearNode();
137
179
  },
180
+ syncDialogDraft() {
181
+ if (!this._dialogDraft) return;
182
+ this._dialogDraft = mergeDialogDraft(this._dialogDraft, this.dialogData, this.newDialogData);
183
+ this.newDialogData = this._dialogDraft.value;
184
+ },
185
+ stopDialogSync() {
186
+ if (this._dialogUnwatch) this._dialogUnwatch();
187
+ this._dialogUnwatch = null;
188
+ this._dialogDraft = null;
189
+ },
138
190
  // 修改字段名按钮事件
139
191
  amendField(item, index) {
140
192
  this.$emit("amend-field", item, index);
@@ -144,10 +196,6 @@ export default {
144
196
  // 删除节点
145
197
  let parent = this.aBox ? this.aBox.parentNode : "";
146
198
  parent && parent.removeChild(this.aBox);
147
- let doms = document.getElementsByClassName("modal-backdrop");
148
- if (doms.length > 0) {
149
- document.body.removeChild(doms[0]);
150
- }
151
199
  this.aBox = null;
152
200
  },
153
201
 
@@ -199,7 +247,7 @@ export default {
199
247
  }
200
248
  });
201
249
  // 当选择项发生变化时会触发该事件(仅在有监听者时取值,避免无监听时全表扫描)
202
- if (this.$listeners["selection-change"]) {
250
+ if (hasGridListener(this, "selection-change")) {
203
251
  const data = this.$refs.singleTable.getCheckboxRecords();
204
252
  this.$emit("selection-change", data);
205
253
  }
@@ -210,7 +258,7 @@ export default {
210
258
  if (this.$refs.singleTable) {
211
259
  this.$refs.singleTable.setAllCheckboxRow(checked);
212
260
  // 当选择项发生变化时会触发该事件(仅在有监听者时取值)
213
- if (this.$listeners["selection-change"]) {
261
+ if (hasGridListener(this, "selection-change")) {
214
262
  const data = this.$refs.singleTable.getCheckboxRecords();
215
263
  this.$emit("selection-change", data);
216
264
  }
@@ -223,7 +271,7 @@ export default {
223
271
  if (this.$refs.singleTable) {
224
272
  this.$refs.singleTable.clearCheckboxRow();
225
273
  // 当选择项发生变化时会触发该事件(仅在有监听者时取值)
226
- if (this.$listeners["selection-change"]) {
274
+ if (hasGridListener(this, "selection-change")) {
227
275
  const data = this.$refs.singleTable.getCheckboxRecords();
228
276
  this.$emit("selection-change", data);
229
277
  }
@@ -249,7 +297,8 @@ export default {
249
297
  },
250
298
  // 对 Table 进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时,可能需要调用此方法
251
299
  doLayout() {
252
- this.$refs.singleTable.recalculate();
300
+ const revision = this._selectedRevision || 0;
301
+ this.refreshSelectedAfter(this.$refs.singleTable.recalculate(), revision);
253
302
  },
254
303
  // 让表格滚动条回到顶部,和左侧
255
304
  pagingScrollTopLeft(top = 0, left = 0) {
@@ -274,7 +323,8 @@ export default {
274
323
  return this.$refs.singleTable.loadColumn(data);
275
324
  },
276
325
  refreshColumn() {
277
- this.$refs.singleTable.refreshColumn();
326
+ const revision = this._selectedRevision || 0;
327
+ this.refreshSelectedAfter(this.$refs.singleTable.refreshColumn(), revision);
278
328
  },
279
329
  // 激活编辑状态
280
330
  setActiveRow(row) {
@@ -450,31 +500,34 @@ export default {
450
500
  selectAll(params) {
451
501
  // vxe 事件参数里的 records 是惰性 getter,仅在有监听者时才取值,避免每次全选都全表扫描
452
502
  if (
453
- this.$listeners["select-all"] ||
454
- this.$listeners["selection-change"]
503
+ hasGridListener(this, "select-all") ||
504
+ hasGridListener(this, "selection-change")
455
505
  ) {
456
506
  const data =
457
507
  params && typeof params.records === "function"
458
508
  ? params.records()
459
- : this.$refs.singleTable.getCheckboxRecords();
509
+ : params && Array.isArray(params.records)
510
+ ? params.records : this.$refs.singleTable.getCheckboxRecords();
460
511
  this.$emit("select-all", data);
461
512
  // 当选择项发生变化时会触发该事件
462
513
  this.$emit("selection-change", data);
463
514
  }
515
+ bridgeStoppedClick(this, params, true);
464
516
  },
465
517
  // 当用户手动勾选数据行的 Checkbox 时触发的事件
466
518
  plSelect(params) {
467
519
  const { row, records } = params || {};
468
520
  // vxe 事件参数里的 records 是惰性 getter,仅在有监听者时才取值,避免每次勾选都全表扫描
469
- if (this.$listeners["select"] || this.$listeners["selection-change"]) {
521
+ if (hasGridListener(this, "select") || hasGridListener(this, "selection-change")) {
470
522
  const data =
471
523
  typeof records === "function"
472
524
  ? records()
473
- : this.$refs.singleTable.getCheckboxRecords();
525
+ : Array.isArray(records) ? records : this.$refs.singleTable.getCheckboxRecords();
474
526
  this.$emit("select", data, row);
475
527
  // 当选择项发生变化时会触发该事件
476
528
  this.$emit("selection-change", data);
477
529
  }
530
+ bridgeStoppedClick(this, params, false);
478
531
  },
479
532
  // 只对 type=radio 有效,当手动勾选并且值发生改变时触发的事件
480
533
  radioChange({
@@ -485,13 +538,16 @@ export default {
485
538
  columnIndex,
486
539
  $columnIndex,
487
540
  cell,
541
+ $event,
488
542
  }) {
489
543
  this.$emit("radio-change", row, column);
544
+ bridgeStoppedClick(this, { row, column, cell, $event }, false);
490
545
  },
491
546
  // 当表格的排序条件发生变化的时候会触发该事件
492
- sortChange({ column, property, order }) {
493
- let objs = { column, prop: property, order };
547
+ sortChange({ column, property, field, order, $event }) {
548
+ let objs = { column, prop: property === undefined ? field : property, order };
494
549
  this.$emit("sort-change", objs);
550
+ bridgeStoppedClick(this, { column, $event }, true);
495
551
  },
496
552
  // 当表格的当前行发生变化的时候会触发该事件
497
553
  currentChange({ row }) {
@@ -518,15 +574,32 @@ export default {
518
574
  $columnIndex,
519
575
  $event,
520
576
  });
577
+ bridgeStoppedClick(this, { row, column, $event }, false);
521
578
  },
522
579
  // 当表格的筛选条件发生变化的时候会触发该事件
523
- filterChange({ column, property, values, datas, filters, $event }) {
580
+ filterChange({ column, property, field, values, datas, filters, $event }) {
581
+ const supplied = typeof filters === "function" ? filters() : filters;
582
+ const table = this.$refs.singleTable;
583
+ const columns = table && table.getTableColumn ? table.getTableColumn().visibleColumn : null;
584
+ // The legacy wrapper reports every visible filterable column, including
585
+ // those with no selection. Keep the new kernel's checked-only API intact.
586
+ const compatibleFilters = columns ? columns.filter(item => item.filters && item.filters.length).map(item => {
587
+ const checked = item.filters.filter(option => option.checked);
588
+ const existing = (supplied || []).find(entry => entry.column === item);
589
+ return Object.assign({}, existing, {
590
+ column: item,
591
+ field: item.field || item.property,
592
+ property: item.property || item.field,
593
+ values: checked.map(option => option.value),
594
+ datas: checked.map(option => option.data),
595
+ });
596
+ }) : supplied;
524
597
  this.$emit("filter-change", {
525
598
  column,
526
- property,
599
+ property: property === undefined ? field : property,
527
600
  values,
528
601
  datas,
529
- filters,
602
+ filters: compatibleFilters,
530
603
  $event,
531
604
  });
532
605
  },
@@ -541,6 +614,10 @@ export default {
541
614
  cell,
542
615
  $event,
543
616
  }) {
617
+ // VXE dispatches this from mouseover, which also bubbles when moving
618
+ // between descendants. The legacy wrapper exposes mouseenter semantics.
619
+ const relatedTarget = $event && $event.relatedTarget;
620
+ if (cell && relatedTarget && relatedTarget.nodeType && cell.contains(relatedTarget)) return;
544
621
  this.$emit("cell-mouse-enter", row, column, cell, $event);
545
622
  },
546
623
  // 当单元格 hover 退出时会触发该事件
@@ -567,6 +644,7 @@ export default {
567
644
  cell,
568
645
  $event,
569
646
  }) {
647
+ if (repeatedClick(this, "_cellClickEvents", $event)) return;
570
648
  this.$emit("cell-click", row, column, cell, $event);
571
649
  // row-click 当某一行被点击时会触发该事件 row, event, column
572
650
  // 只有当点击的单元格不是复选框才触发行事件!
@@ -603,6 +681,7 @@ export default {
603
681
  columnIndex,
604
682
  $event,
605
683
  }) {
684
+ if (repeatedClick(this, "_headerClickEvents", $event)) return;
606
685
  this.$emit("header-click", column, $event);
607
686
  },
608
687
  // 当某一列的表头被鼠标右键点击时触发该事件 column, $event
@@ -683,25 +762,53 @@ export default {
683
762
  $columnIndex,
684
763
  });
685
764
  },
765
+ // elx-grid 扩展事件:鼠标或键盘切换当前选中单元格时触发
766
+ editCurrentlyActive({ row, column, cell }) {
767
+ this._selectedRevision = (this._selectedRevision || 0) + 1;
768
+ const table = this.$refs.singleTable;
769
+ const targetCell =
770
+ cell ||
771
+ (table && table.getCellElement
772
+ ? table.getCellElement(row, column)
773
+ : null);
774
+ this.$emit("edit-currently-active", {
775
+ row,
776
+ column,
777
+ cell: targetCell,
778
+ });
779
+ },
780
+ // Only public wrapper refreshes participate; no global VXE hooks or
781
+ // private editStore access. Preserve the legacy undefined method return.
782
+ refreshSelectedAfter(completion, revision = this._selectedRevision || 0) {
783
+ Promise.resolve(completion).then(() => this.$nextTick()).then(() => {
784
+ if (this._isDestroyed || this._isBeingDestroyed ||
785
+ revision !== (this._selectedRevision || 0) ||
786
+ !hasGridListener(this, "edit-currently-active")) return;
787
+ const table = this.$refs.singleTable;
788
+ const selected = table && table.getSelectedCell ? table.getSelectedCell() : null;
789
+ if (!selected) return;
790
+ const cell = table.getCellElement(selected.row, selected.column);
791
+ if (cell) this.editCurrentlyActive(Object.assign({}, selected, { cell }));
792
+ }).catch(error => console.error("[UxGrid] Selected cell refresh failed", error));
793
+ },
686
794
  // 当树节点展开或收起时会触发该事件
687
795
  toggleTreeExpandEv(row) {
688
796
  this.$emit("toggle-tree-expand", row);
797
+ bridgeStoppedClick(this, row, false);
689
798
  },
690
799
  // 键盘按下事件
691
800
  keydown({ $event }) {
692
801
  this.$emit("keydown", { $event });
693
802
  },
694
- // 当前被激活的单元格
695
- editCurrentlyActive({ row, column, cell }) {
696
- this.$emit("edit-currently-active", { row, column, cell });
697
- },
698
803
  // vxe 首次加载完数据并渲染完成后触发(此时列信息已构建完,DOM 稳定,可安全绑定第三方拖拽等)
699
804
  initRendered(params) {
700
805
  this.$emit("init-rendered", params);
701
806
  },
702
807
  // vxe 后续重新加载/刷新数据并渲染完成后触发
703
808
  dataRendered(params) {
809
+ const revision = this._selectedRevision || 0;
704
810
  this.$emit("data-rendered", params);
811
+ this.refreshSelectedAfter(undefined, revision);
705
812
  },
706
813
  },
707
814
  };
@@ -0,0 +1,75 @@
1
+ import XEUtils from "xe-utils";
2
+
3
+ // Keep the source snapshot separate from the editable draft. Only an open
4
+ // dialog owns this state; closed grids do not traverse dialogData.
5
+ export function createDialogDraft(source) {
6
+ return { source: source.slice(), baseline: XEUtils.clone(source, true), draft: XEUtils.clone(source, true) };
7
+ }
8
+
9
+ export function mergeDialogDraft(previous, source, draft) {
10
+ const keyOf = item => {
11
+ for (const field of ["id", "field", "prop", "key"]) {
12
+ if (item[field] !== undefined && item[field] !== null) return field + ":" + item[field];
13
+ }
14
+ return null;
15
+ };
16
+ const matchIndices = (items, originals) => {
17
+ const indices = items.map(() => -1);
18
+ const used = new Set();
19
+ const assign = (index, oldIndex) => {
20
+ if (oldIndex >= 0 && !used.has(oldIndex)) {
21
+ indices[index] = oldIndex;
22
+ used.add(oldIndex);
23
+ }
24
+ };
25
+ // Reserve every surviving reference before considering positional fallback:
26
+ // a new item inserted at index 0 must not take the old index of a later A.
27
+ items.forEach((item, index) => {
28
+ assign(index, originals.findIndex((entry, oldIndex) => entry === item && !used.has(oldIndex)));
29
+ });
30
+ items.forEach((item, index) => {
31
+ const key = keyOf(item);
32
+ if (indices[index] < 0 && key !== null) {
33
+ assign(index, previous.baseline.findIndex((entry, oldIndex) => keyOf(entry) === key && !used.has(oldIndex)));
34
+ }
35
+ });
36
+ items.forEach((item, index) => {
37
+ if (indices[index] < 0 && keyOf(item) === null &&
38
+ index < previous.baseline.length && keyOf(previous.baseline[index]) === null) {
39
+ assign(index, index);
40
+ }
41
+ });
42
+ return indices;
43
+ };
44
+ const draftIndices = matchIndices(draft, previous.draft);
45
+ const sourceIndices = matchIndices(source, previous.source);
46
+ const incoming = source.map((item, index) => {
47
+ const oldIndex = sourceIndices[index];
48
+ const draftIndex = draftIndices.indexOf(oldIndex);
49
+ const value = XEUtils.clone(item, true);
50
+ if (oldIndex >= 0 && draftIndex >= 0) {
51
+ const base = previous.baseline[oldIndex];
52
+ const edited = draft[draftIndex];
53
+ // A local field edit wins a simultaneous parent change to that field.
54
+ new Set(Object.keys(base).concat(Object.keys(edited))).forEach(field => {
55
+ if (!XEUtils.isEqual(base[field], edited[field])) {
56
+ if (Object.prototype.hasOwnProperty.call(edited, field)) value[field] = XEUtils.clone(edited[field], true);
57
+ else delete value[field];
58
+ }
59
+ });
60
+ }
61
+ return { oldIndex, value };
62
+ });
63
+ const reordered = draftIndices.some((index, position) => index !== position);
64
+ let nextDraft = incoming.map(entry => entry.value);
65
+ if (reordered) {
66
+ const ordered = draftIndices.filter(index => index >= 0)
67
+ .map(index => incoming.find(entry => entry.oldIndex === index)).filter(Boolean);
68
+ const retained = new Set(ordered);
69
+ nextDraft = ordered.concat(incoming.filter(entry => !retained.has(entry))).map(entry => entry.value);
70
+ }
71
+ // Store draft references in source order so subsequent drag operations can
72
+ // still be matched even when the item does not declare a stable key.
73
+ return { source: source.slice(), baseline: XEUtils.clone(source, true),
74
+ draft: incoming.map(entry => entry.value), value: nextDraft };
75
+ }