@nokecy/qc-ui 1.1.50 → 1.1.52

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.
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ /**
3
+ * 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
4
+ *
5
+ * 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
6
+ * 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
7
+ * 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
8
+ *
9
+ * 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
10
+ * blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
11
+ * (窗口切走时输入框依旧是 activeElement),同样不算离开。
12
+ *
13
+ * 用 display: contents,不产生盒子,不改变单元格里原有的布局。
14
+ *
15
+ * @author nokecy
16
+ */
17
+ export type CellLeaveHandler = (isStale: () => boolean) => void;
18
+ export declare const CellFocusScope: ({ onLeave, children }: {
19
+ onLeave: CellLeaveHandler;
20
+ children: React.ReactNode;
21
+ }) => React.JSX.Element;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.CellFocusScope = void 0;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
10
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
11
+ /**
12
+ * 判断「焦点离开了这个单元格」的作用域,给懒挂载格「用完卸回纯文本」用。
13
+ *
14
+ * 用 React 合成的 onFocus/onBlur 而不是原生监听:合成事件会沿 React 树冒泡,
15
+ * 穿过 portal。下拉面板、日期面板、选择对话框这类渲染到 body 上的弹层,焦点进去时
16
+ * 对本格来说仍是「格内」,不会被误判成离开;弹层的挂载点是哪、有没有 antd 的类名都不用管。
17
+ *
18
+ * 失焦不立刻判定,而是推到下一个任务再看:焦点从输入框换到格内别的元素(或弹层)时,
19
+ * blur 之后紧跟着 focus,后者会作废这次判定。判定时若 document.activeElement 仍在格内
20
+ * (窗口切走时输入框依旧是 activeElement),同样不算离开。
21
+ *
22
+ * 用 display: contents,不产生盒子,不改变单元格里原有的布局。
23
+ *
24
+ * @author nokecy
25
+ */
26
+
27
+ var CellFocusScope = exports.CellFocusScope = function CellFocusScope(_ref) {
28
+ var onLeave = _ref.onLeave,
29
+ children = _ref.children;
30
+ var rootRef = (0, _react.useRef)(null);
31
+ var timerRef = (0, _react.useRef)();
32
+ // 每次焦点回到格内、或卸载时递增;进行中的异步判定拿它判断自己是否已过期
33
+ var generationRef = (0, _react.useRef)(0);
34
+ var onLeaveRef = (0, _react.useRef)(onLeave);
35
+ onLeaveRef.current = onLeave;
36
+ var cancel = function cancel() {
37
+ generationRef.current += 1;
38
+ window.clearTimeout(timerRef.current);
39
+ };
40
+ (0, _react.useEffect)(function () {
41
+ return cancel;
42
+ }, []);
43
+ var handleBlur = function handleBlur() {
44
+ cancel();
45
+ var generation = generationRef.current;
46
+ timerRef.current = window.setTimeout(function () {
47
+ var _rootRef$current;
48
+ if ((_rootRef$current = rootRef.current) !== null && _rootRef$current !== void 0 && _rootRef$current.contains(document.activeElement)) return;
49
+ onLeaveRef.current(function () {
50
+ return generation !== generationRef.current;
51
+ });
52
+ }, 0);
53
+ };
54
+ return /*#__PURE__*/_react.default.createElement("div", {
55
+ ref: rootRef,
56
+ style: {
57
+ display: 'contents'
58
+ },
59
+ onFocus: cancel,
60
+ onBlur: handleBlur
61
+ }, children);
62
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ArrayTable 分组表头(多级表头)。
3
+ *
4
+ * 背景:schema 里的每个 ArrayTable.Column 被抽成「扁平」列定义(buildArrayTableColumns),
5
+ * 内部一批逻辑(懒挂载列识别、列方案还原、首列拖拽等)都按扁平列取值,不能改。
6
+ * 所以分组只在「交给 AG-Grid 的那一份 columnDefs」上做:
7
+ * 1. buildArrayTableColumns 生成列定义时,用 markHeaderGroup 把列声明的 headerGroup 记下来;
8
+ * 2. index.tsx 把扁平列定义交给 groupColumnDefs,得到含列组的 columnDefs 传给 AgGridReact。
9
+ *
10
+ * 分组名记在模块内的 WeakMap 里,不往 colDef 上塞自定义字段——AG-Grid 会把未知属性当作错误告警。
11
+ *
12
+ * 只支持一级分组;分组名相同但不相邻的列各自成组。
13
+ *
14
+ * @author nokecy
15
+ */
16
+ import type { ColDef, ColGroupDef } from 'ag-grid-community';
17
+ /**
18
+ * 记下列定义所属的表头分组。
19
+ * headerGroup 不是非空字符串(没声明、空串、纯空白)时不做任何事,该列按无分组处理。
20
+ * 返回传入的 colDef 本身,方便链式使用。
21
+ */
22
+ export declare const markHeaderGroup: <T extends object>(colDef: T, headerGroup: unknown) => T;
23
+ /**
24
+ * 把扁平列定义按「相邻且 headerGroup 相同」合并成 AG-Grid 列组。
25
+ *
26
+ * - 相邻同名的列 -> 一个 { headerName, groupId, children } 列组;
27
+ * - 同名但被别的列隔开 -> 各自成组,groupId 各不相同;
28
+ * - 没声明分组的列原样保留(同一个对象);
29
+ * - 一个分组也没有时直接返回传入的数组本身,没用到分组的表格行为零变化。
30
+ *
31
+ * groupId 由分组名派生(同名第 N 次出现追加 _N),同样的输入永远得到同样的 id:
32
+ * columnDefs 因别的原因重建时,AG-Grid 靠 groupId 认出还是那个列组,列组状态不丢。
33
+ *
34
+ * 有意不设 marryChildren:设了以后,任何打破组内相邻的列顺序(用户旧的扁平列方案、
35
+ * 列设置面板里跨组拖动的实时预览)都会被 AG-Grid 整体丢弃并报 error #39;不设则顺序照常生效,
36
+ * 组被隔开时表头显示成多段。
37
+ * openByDefault:列组默认展开(只对配了 columnGroupShow 的可折叠列组有意义)。
38
+ */
39
+ export declare const groupColumnDefs: (colDefs: ColDef[]) => (ColDef | ColGroupDef)[];
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.markHeaderGroup = exports.groupColumnDefs = void 0;
7
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
8
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
9
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
10
+ /**
11
+ * ArrayTable 分组表头(多级表头)。
12
+ *
13
+ * 背景:schema 里的每个 ArrayTable.Column 被抽成「扁平」列定义(buildArrayTableColumns),
14
+ * 内部一批逻辑(懒挂载列识别、列方案还原、首列拖拽等)都按扁平列取值,不能改。
15
+ * 所以分组只在「交给 AG-Grid 的那一份 columnDefs」上做:
16
+ * 1. buildArrayTableColumns 生成列定义时,用 markHeaderGroup 把列声明的 headerGroup 记下来;
17
+ * 2. index.tsx 把扁平列定义交给 groupColumnDefs,得到含列组的 columnDefs 传给 AgGridReact。
18
+ *
19
+ * 分组名记在模块内的 WeakMap 里,不往 colDef 上塞自定义字段——AG-Grid 会把未知属性当作错误告警。
20
+ *
21
+ * 只支持一级分组;分组名相同但不相邻的列各自成组。
22
+ *
23
+ * @author nokecy
24
+ */
25
+
26
+ /** 列定义 -> 它在 schema 里声明的 headerGroup */
27
+ var headerGroupOf = new WeakMap();
28
+
29
+ /** 列组 id 前缀,与列方案里生成的 col_N 区分开 */
30
+ var GROUP_ID_PREFIX = 'arrayTableGroup_';
31
+
32
+ /**
33
+ * 记下列定义所属的表头分组。
34
+ * headerGroup 不是非空字符串(没声明、空串、纯空白)时不做任何事,该列按无分组处理。
35
+ * 返回传入的 colDef 本身,方便链式使用。
36
+ */
37
+ var markHeaderGroup = exports.markHeaderGroup = function markHeaderGroup(colDef, headerGroup) {
38
+ if (typeof headerGroup === 'string' && headerGroup.trim() !== '') {
39
+ headerGroupOf.set(colDef, headerGroup);
40
+ }
41
+ return colDef;
42
+ };
43
+
44
+ /**
45
+ * 把扁平列定义按「相邻且 headerGroup 相同」合并成 AG-Grid 列组。
46
+ *
47
+ * - 相邻同名的列 -> 一个 { headerName, groupId, children } 列组;
48
+ * - 同名但被别的列隔开 -> 各自成组,groupId 各不相同;
49
+ * - 没声明分组的列原样保留(同一个对象);
50
+ * - 一个分组也没有时直接返回传入的数组本身,没用到分组的表格行为零变化。
51
+ *
52
+ * groupId 由分组名派生(同名第 N 次出现追加 _N),同样的输入永远得到同样的 id:
53
+ * columnDefs 因别的原因重建时,AG-Grid 靠 groupId 认出还是那个列组,列组状态不丢。
54
+ *
55
+ * 有意不设 marryChildren:设了以后,任何打破组内相邻的列顺序(用户旧的扁平列方案、
56
+ * 列设置面板里跨组拖动的实时预览)都会被 AG-Grid 整体丢弃并报 error #39;不设则顺序照常生效,
57
+ * 组被隔开时表头显示成多段。
58
+ * openByDefault:列组默认展开(只对配了 columnGroupShow 的可折叠列组有意义)。
59
+ */
60
+ var groupColumnDefs = exports.groupColumnDefs = function groupColumnDefs(colDefs) {
61
+ if (!colDefs.some(function (colDef) {
62
+ return headerGroupOf.has(colDef);
63
+ })) return colDefs;
64
+ var usedGroupIds = new Set();
65
+ var makeGroupId = function makeGroupId(name) {
66
+ var id = "".concat(GROUP_ID_PREFIX).concat(name);
67
+ for (var n = 2; usedGroupIds.has(id); n++) {
68
+ id = "".concat(GROUP_ID_PREFIX).concat(name, "_").concat(n);
69
+ }
70
+ usedGroupIds.add(id);
71
+ return id;
72
+ };
73
+ var result = [];
74
+ var current = null;
75
+ var _iterator = _createForOfIteratorHelper(colDefs),
76
+ _step;
77
+ try {
78
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
79
+ var colDef = _step.value;
80
+ var name = headerGroupOf.get(colDef);
81
+ if (name === undefined) {
82
+ current = null;
83
+ result.push(colDef);
84
+ } else if (current && current.name === name) {
85
+ current.group.children.push(colDef);
86
+ } else {
87
+ var group = {
88
+ headerName: name,
89
+ groupId: makeGroupId(name),
90
+ openByDefault: true,
91
+ children: [colDef]
92
+ };
93
+ current = {
94
+ name: name,
95
+ group: group
96
+ };
97
+ result.push(group);
98
+ }
99
+ }
100
+ } catch (err) {
101
+ _iterator.e(err);
102
+ } finally {
103
+ _iterator.f();
104
+ }
105
+ return result;
106
+ };
@@ -22,3 +22,13 @@ export declare const useKeyboardNavigation: (ref: RefObject<any>, dataSource: an
22
22
  export declare const useRowDrag: (field: ArrayField) => {
23
23
  handleRowDragMove: (event: any) => void;
24
24
  };
25
+ /**
26
+ * AG-Grid 的 suppressKeyboardEvent:单元格里的输入框按 Ctrl/Cmd+A 时,让浏览器自己全选文字。
27
+ *
28
+ * AG-Grid 把 Ctrl+A 当成「选中所有单元格」并 preventDefault,输入框(文本框、数字框都一样)
29
+ * 就选不中里面的文字。这里只放行这一个组合;方向键等其余按键仍归 handleCellKeyDown 做单元格导航。
30
+ * 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
31
+ */
32
+ export declare const suppressSelectAllInInput: ({ event }: {
33
+ event: KeyboardEvent;
34
+ }) => boolean;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.useRowDrag = exports.useKeyboardNavigation = exports.useCellFocus = void 0;
6
+ exports.useRowDrag = exports.useKeyboardNavigation = exports.useCellFocus = exports.suppressSelectAllInInput = void 0;
7
7
  var _react = require("react");
8
8
  // setSelectionRange 仅 textarea 与 text/search/url/tel/password 类型 input 支持;
9
9
  // checkbox/number/date 等调用会抛 InvalidStateError,故用白名单守卫。@author nokecy
@@ -209,4 +209,19 @@ var useRowDrag = exports.useRowDrag = function useRowDrag(field) {
209
209
  return {
210
210
  handleRowDragMove: handleRowDragMove
211
211
  };
212
+ };
213
+
214
+ /**
215
+ * AG-Grid 的 suppressKeyboardEvent:单元格里的输入框按 Ctrl/Cmd+A 时,让浏览器自己全选文字。
216
+ *
217
+ * AG-Grid 把 Ctrl+A 当成「选中所有单元格」并 preventDefault,输入框(文本框、数字框都一样)
218
+ * 就选不中里面的文字。这里只放行这一个组合;方向键等其余按键仍归 handleCellKeyDown 做单元格导航。
219
+ * 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
220
+ */
221
+ var suppressSelectAllInInput = exports.suppressSelectAllInInput = function suppressSelectAllInInput(_ref) {
222
+ var _target$matches;
223
+ var event = _ref.event;
224
+ if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'a') return false;
225
+ var target = event.target;
226
+ return !!(target !== null && target !== void 0 && (_target$matches = target.matches) !== null && _target$matches !== void 0 && _target$matches.call(target, 'input, textarea, [contenteditable="true"]'));
212
227
  };
@@ -10,6 +10,7 @@ var _agGridReact = require("ag-grid-react");
10
10
  var _react2 = require("@formily/react");
11
11
  var _uniqueId = _interopRequireDefault(require("lodash/uniqueId"));
12
12
  var _utils = require("./utils");
13
+ var _columnGroup = require("./columnGroup");
13
14
  var _hooks = require("./hooks");
14
15
  var _arrayBase = _interopRequireDefault(require("./arrayBase"));
15
16
  var _arrayAddBtn = _interopRequireDefault(require("./arrayAddBtn"));
@@ -61,7 +62,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
61
62
  isAdd = _useState2[0],
62
63
  setIsAdd = _useState2[1];
63
64
  var apiRef = (0, _react.useRef)();
64
- // 懒挂载激活态:存放已激活(点击/导航进过)的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读取。@author nokecy
65
+ // 懒挂载激活态:存放当前挂着真控件的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读写:
66
+ // 点击/导航进来时这里写入,必填且为空的格自己写入,焦点离开且校验通过后自己删除。@author nokecy
65
67
  var activeCellsRef = (0, _react.useRef)(new Set());
66
68
  // 无 _id/id 的外部数据按对象引用分配稳定 rowId(避免 getRowId 返回 undefined)。@author nokecy
67
69
  var rowIdMapRef = (0, _react.useRef)(new WeakMap());
@@ -82,14 +84,16 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
82
84
  // 通过 JSON.stringify 比较内容,只有内容真正变化时才返回新引用
83
85
  var sourcesKey = (0, _react.useMemo)(function () {
84
86
  return JSON.stringify(rawSources.map(function (s) {
85
- var _s$columnProps$header, _s$columnProps, _s$columnProps2;
87
+ var _s$columnProps$header, _s$columnProps, _s$columnProps2, _s$columnProps3;
86
88
  return {
87
89
  name: s.name,
88
90
  display: s.display,
89
91
  // 列标题可由 reactions 运行时改写,纳入 key 才能让 stableColumnDefs 跟着重建。
90
92
  // 宽度/隐藏/固定由 applyColumnState(方案)管,故意不纳入,避免行编辑时列状态被重置。
91
93
  // 可编辑性:带 x-reactions 的列在 buildArrayTableColumns 里已强制走 AgField 全树(尊重运行时 pattern),无需进 key。@author nokecy
92
- headerName: (_s$columnProps$header = (_s$columnProps = s.columnProps) === null || _s$columnProps === void 0 ? void 0 : _s$columnProps.headerName) !== null && _s$columnProps$header !== void 0 ? _s$columnProps$header : (_s$columnProps2 = s.columnProps) === null || _s$columnProps2 === void 0 ? void 0 : _s$columnProps2.title
94
+ headerName: (_s$columnProps$header = (_s$columnProps = s.columnProps) === null || _s$columnProps === void 0 ? void 0 : _s$columnProps.headerName) !== null && _s$columnProps$header !== void 0 ? _s$columnProps$header : (_s$columnProps2 = s.columnProps) === null || _s$columnProps2 === void 0 ? void 0 : _s$columnProps2.title,
95
+ // 分组名变了列组结构就变,同样要重建。@author nokecy
96
+ headerGroup: (_s$columnProps3 = s.columnProps) === null || _s$columnProps3 === void 0 ? void 0 : _s$columnProps3.headerGroup
93
97
  };
94
98
  }));
95
99
  }, [rawSources]);
@@ -150,6 +154,12 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
150
154
  return cols;
151
155
  }, [sources, props.onRowDrag]); // 移除 field 依赖,避免数据变化时重新计算
152
156
 
157
+ // 交给 AgGridReact 的列定义:按 x-component-props.headerGroup 把相邻同组的列合并成列组(多级表头)。
158
+ // stableColumnDefs 保持扁平,懒列识别、列方案还原等内部逻辑仍用它;只有喂给 AG-Grid 的这一份带分组。@author nokecy
159
+ var gridColumnDefs = (0, _react.useMemo)(function () {
160
+ return (0, _columnGroup.groupColumnDefs)(stableColumnDefs);
161
+ }, [stableColumnDefs]);
162
+
153
163
  // 懒挂载:把激活态 Set 通过 AG-Grid context 暴露给 LazyAgField(引用稳定,原地 mutate)。@author nokecy
154
164
  var gridContext = (0, _react.useMemo)(function () {
155
165
  return {
@@ -169,15 +179,15 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
169
179
 
170
180
  /**
171
181
  * 激活某个懒挂载单元格:加入激活集合并强制重渲该格 -> LazyAgField 改挂 AgField 全树。
172
- * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/新增行/已激活)。@author nokecy
182
+ * 焦点离开后 LazyAgField 自己把它从集合里移除并卸回文本(见 utils.tsx)。
183
+ * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/已挂载着,
184
+ * 如必填且为空的格本就挂着)。新增行的格与存量行一视同仁:非必填的也是先文本、点了才挂。@author nokecy
173
185
  */
174
186
  var activateCell = (0, _react.useCallback)(function (api, node, colId) {
175
- var _node$data;
176
187
  if (!api || !node || !colId) return false;
177
188
  if (!lazyColIds.has(colId)) return false; // 非懒列(已是 AgField/纯文本)
178
- if ((_node$data = node.data) !== null && _node$data !== void 0 && _node$data._isNew) return false; // 新增行本就 eager 挂着
179
189
  var key = "".concat(node.id, ":").concat(colId);
180
- if (activeCellsRef.current.has(key)) return false; // 已激活
190
+ if (activeCellsRef.current.has(key)) return false; // 已挂载
181
191
  activeCellsRef.current.add(key);
182
192
  api.refreshCells({
183
193
  rowNodes: [node],
@@ -226,8 +236,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
226
236
  if (hasGroups) {
227
237
  // 传入 stableColumnDefs 作为原始定义,保留 cellRenderer(AgField/LazyAgField)及其
228
238
  // cellRendererParams——否则分组方案重建出的是裸列,单元格退化为纯文本、编辑失效。@author nokecy
229
- var gridColumnDefs = (0, _columnGroupUtils.flatConfigToGridState)(currentScheme.columnConfig, stableColumnDefs);
230
- gridApiState.setGridOption('columnDefs', gridColumnDefs);
239
+ var _gridColumnDefs = (0, _columnGroupUtils.flatConfigToGridState)(currentScheme.columnConfig, stableColumnDefs);
240
+ gridApiState.setGridOption('columnDefs', _gridColumnDefs);
231
241
  }
232
242
 
233
243
  // 2. 应用列状态(列顺序、宽度、隐藏、固定等)
@@ -382,6 +392,8 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
382
392
  enableCellTextSelection: true,
383
393
  defaultColDef: {
384
394
  resizable: true,
395
+ // 输入框里的 Ctrl/Cmd+A 交给浏览器全选文字,不让 AG-Grid 当成「选中所有单元格」拦走
396
+ suppressKeyboardEvent: _hooks.suppressSelectAllInInput,
385
397
  headerComponentParams: {
386
398
  template: '<div class="ag-cell-label-container" role="presentation">' +
387
399
  // ' <span data-ref="eMenu" class="ag-header-icon ag-header-cell-menu-button" aria-hidden="true"></span>' +
@@ -420,7 +432,7 @@ var ArrayTableInner = (0, _react2.observer)(function (props) {
420
432
  ,
421
433
  rowHeight: 38,
422
434
  pagination: false,
423
- columnDefs: stableColumnDefs,
435
+ columnDefs: gridColumnDefs,
424
436
  rowData: dataSource
425
437
  })), sources.map(function (column, key) {
426
438
  //专门用来承接对Column的状态管理
@@ -3,9 +3,28 @@ import { FieldDisplayTypes, GeneralField } from "@formily/core";
3
3
  import { Schema } from "@formily/json-schema";
4
4
  import { AgGridReactProps } from "ag-grid-react";
5
5
  import React from "react";
6
+ /**
7
+ * ArrayTable.Column 的 x-component-props 里 qc-ui 自己认的扩展属性;
8
+ * 其余属性(width、headerName、pinned 等)按 AG-Grid 列定义处理。
9
+ *
10
+ * @author nokecy
11
+ */
12
+ interface ArrayTableColumnProps {
13
+ /**
14
+ * 表头分组名(多级表头,仅一级)。
15
+ * 相邻且 headerGroup 相同的列合并成一个列组,分组名作为组头;
16
+ * 同名但不相邻的列各自成组;不写则该列不分组。
17
+ */
18
+ headerGroup?: string;
19
+ /**
20
+ * 默认隐藏:为 true 时该列首次出现时是隐藏的,用户可在列设置里打开,列方案(applyColumnState)优先。
21
+ * 对应 AG-Grid 的 initialHide。注意 hide 在 ArrayTable 里不生效(状态归列方案管),要默认隐藏请用它。
22
+ */
23
+ defaultHide?: boolean;
24
+ }
6
25
  interface ObservableColumnSource {
7
26
  field: GeneralField;
8
- columnProps: any;
27
+ columnProps: ArrayTableColumnProps & Record<string, any>;
9
28
  schema: Schema;
10
29
  display: FieldDisplayTypes;
11
30
  name: string;
@@ -13,4 +32,4 @@ interface ObservableColumnSource {
13
32
  type ComposedArrayTable = React.FC<React.PropsWithChildren<AgGridReactProps>> & ArrayBaseMixins & {
14
33
  Column?: any;
15
34
  };
16
- export { ComposedArrayTable, ObservableColumnSource };
35
+ export { ComposedArrayTable, ObservableColumnSource, ArrayTableColumnProps };
@@ -1,18 +1,3 @@
1
- /**
2
- * 只读列「纯文本快路径」格式化器注册表
3
- *
4
- * 背景:ArrayTable 每个可见单元格默认挂一棵完整 Formily 字段树(AgField),
5
- * 行虚拟化滚动时整行销毁重建,列多必然掉帧。只读列没有编辑/校验/写回,
6
- * 完全可以不挂 Formily 树、改用 AG-Grid 的 valueFormatter 出纯文本。
7
- *
8
- * 本注册表负责「内层组件类型 -> 显示文本」的映射。命中返回格式化器,
9
- * 未命中返回 null —— 调用方据此安全回退 AgField(正确优先于速度)。
10
- *
11
- * 取舍:这是 Formily readPretty 之外的一份精简显示逻辑,可能与定制 readPretty
12
- * 发散。靠「未命中即回退 + 消费方可 registerReadOnlyFormatter 扩展」控制风险。
13
- *
14
- * @author nokecy
15
- */
16
1
  export type ReadOnlyFormatterCtx = {
17
2
  /** 列的 x-component-props(headerName/width/options/format 等) */
18
3
  columnProps: any;
@@ -4,8 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.registerReadOnlyFormatter = exports.getReadOnlyFormatter = void 0;
7
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
8
- /**
7
+ var _dayjs = _interopRequireDefault(require("dayjs"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } /**
9
10
  * 只读列「纯文本快路径」格式化器注册表
10
11
  *
11
12
  * 背景:ArrayTable 每个可见单元格默认挂一棵完整 Formily 字段树(AgField),
@@ -20,7 +21,6 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
20
21
  *
21
22
  * @author nokecy
22
23
  */
23
-
24
24
  /** 从字段 schema / 列 props 里尽量取到 Select 的可选项(value->label 映射用) */
25
25
  var resolveOptions = function resolveOptions(ctx) {
26
26
  var _ref, _ref2, _ref3, _fieldSchema$enum, _fieldSchema$xCompon, _fieldSchema$xCompon2;
@@ -62,9 +62,11 @@ var dateFormatter = function dateFormatter(value, ctx) {
62
62
  var _ref6, _ctx$columnProps$form, _ctx$columnProps, _ctx$fieldSchema;
63
63
  if (value == null || value === '') return '';
64
64
  var fmt = (_ref6 = (_ctx$columnProps$form = (_ctx$columnProps = ctx.columnProps) === null || _ctx$columnProps === void 0 ? void 0 : _ctx$columnProps.format) !== null && _ctx$columnProps$form !== void 0 ? _ctx$columnProps$form : (_ctx$fieldSchema = ctx.fieldSchema) === null || _ctx$fieldSchema === void 0 || (_ctx$fieldSchema = _ctx$fieldSchema['x-component-props']) === null || _ctx$fieldSchema === void 0 ? void 0 : _ctx$fieldSchema.format) !== null && _ref6 !== void 0 ? _ref6 : 'YYYY-MM-DD';
65
- // 值多为 dayjs 对象(有 .format)或已格式化的字符串;不强引 dayjs,保持轻量
66
- if (value && typeof value.format === 'function') return value.format(fmt);
67
- return String(value);
65
+ if (typeof value.format === 'function') return value.format(fmt);
66
+ // 后端回来的多是 ISO 串(2024-10-19T00:00:00)或时间戳:与 Formily readPretty 一致,按 dayjs(value).format(fmt) 显示;
67
+ // 解析不了的文本(如「待定」)原样显示,不吞掉。dayjs 随 antd 传递依赖引入,本库其他模块已直接使用。
68
+ var parsed = (0, _dayjs.default)(value);
69
+ return parsed.isValid() ? parsed.format(fmt) : String(value);
68
70
  };
69
71
  var booleanFormatter = function booleanFormatter(value, ctx) {
70
72
  var _ctx$fieldSchema$xCo, _ctx$fieldSchema2, _props$checkedChildre, _props$unCheckedChild;
@@ -10,14 +10,21 @@ declare const useArrayTableActions: () => ObservableColumnSource[];
10
10
  * 懒挂载单元格渲染器(可编辑列)。
11
11
  *
12
12
  * 已有行的可编辑格被 Editable 装饰器默认转成 readPretty 只读文本态(点击才进编辑),
13
- * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器:
14
- * - 新增行(_isNew,保持编辑态需即时校验) 或 已被激活(点击/导航进过)的格 -> 挂 AgField 全树;
15
- * - 其余(存量行、没碰过) -> 只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。
13
+ * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器让每个格在两种形态间切换:
14
+ * - 懒挂载文本:只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。默认形态。
15
+ * - 挂载态:挂 AgField 全树,可编辑、可校验。
16
16
  *
17
- * 激活态存放在 AG-Grid context.activeCells(Set),由 index.tsx 在点击/键盘导航时写入并 refreshCells。
18
- * 「挂了不卸」:一旦激活保持挂载(直到行被虚拟化销毁),不做失焦退回,避开一堆边界。
17
+ * 何时挂载:
18
+ * 1. 被激活(点击/键盘导航进来):激活态存放在 AG-Grid context.activeCells(Set),
19
+ * 由 index.tsx 写入并 refreshCells;
20
+ * 2. 必填且为空:空的必填格必须是真控件,否则没有字段节点,提交时没人校验它。
21
+ * 不区分新增行、存量行、initialValues 带进来的行——只看「必填且空」。
22
+ * 例外:Editable 装饰器的非新增行,Editable 自己会把它转成只读态,挂了也不校验,留在文本态。
23
+ * 何时卸回文本:焦点离开本格,且该字段校验通过(必填的已填上、没有格式错误)。
24
+ * 必填仍为空或校验有错则留着控件,让用户看见错误——与 Editable 失焦时的判断一致。
25
+ * 卸掉的只是渲染树,Formily 字段节点与值都还在,再次点击会接着用。
19
26
  *
20
- * 校验安全:未碰过的存量格本就是 readPretty、本就不参与校验,不挂树校验语义零变化。@author nokecy
27
+ * 校验安全:空的必填格不会卸成文本(见上);其余格卸载前已校验通过。@author nokecy
21
28
  */
22
29
  declare const LazyAgField: (params: any) => React.JSX.Element;
23
30
  /**