@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.
@@ -203,4 +203,19 @@ export var useRowDrag = function useRowDrag(field) {
203
203
  return {
204
204
  handleRowDragMove: handleRowDragMove
205
205
  };
206
+ };
207
+
208
+ /**
209
+ * AG-Grid 的 suppressKeyboardEvent:单元格里的输入框按 Ctrl/Cmd+A 时,让浏览器自己全选文字。
210
+ *
211
+ * AG-Grid 把 Ctrl+A 当成「选中所有单元格」并 preventDefault,输入框(文本框、数字框都一样)
212
+ * 就选不中里面的文字。这里只放行这一个组合;方向键等其余按键仍归 handleCellKeyDown 做单元格导航。
213
+ * 复制/粘贴/剪切/撤销 AG-Grid 本来就没拦,不必处理。@author nokecy
214
+ */
215
+ export var suppressSelectAllInInput = function suppressSelectAllInInput(_ref) {
216
+ var _target$matches;
217
+ var event = _ref.event;
218
+ if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'a') return false;
219
+ var target = event.target;
220
+ return !!(target !== null && target !== void 0 && (_target$matches = target.matches) !== null && _target$matches !== void 0 && _target$matches.call(target, 'input, textarea, [contenteditable="true"]'));
206
221
  };
@@ -18,7 +18,8 @@ import { useFieldSchema } from "@formily/react";
18
18
  import { connect, mapProps, mapReadPretty } from "@formily/react";
19
19
  import uniqueId from "lodash/uniqueId";
20
20
  import { useAddition, buildArrayTableColumns, useArrayTableSources, useArrayTableActions, isColumnComponent } from "./utils";
21
- import { useCellFocus, useKeyboardNavigation, useRowDrag } from "./hooks";
21
+ import { groupColumnDefs } from "./columnGroup";
22
+ import { useCellFocus, useKeyboardNavigation, useRowDrag, suppressSelectAllInInput } from "./hooks";
22
23
  import ArrayBase from "./arrayBase";
23
24
  import ArrayAddBtn from "./arrayAddBtn";
24
25
  import PreviewArrayTable from "./preview";
@@ -57,7 +58,8 @@ var ArrayTableInner = observer(function (props) {
57
58
  isAdd = _useState2[0],
58
59
  setIsAdd = _useState2[1];
59
60
  var apiRef = useRef();
60
- // 懒挂载激活态:存放已激活(点击/导航进过)的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读取。@author nokecy
61
+ // 懒挂载激活态:存放当前挂着真控件的单元格 key(`rowId:colId`),供 LazyAgField 经 context 读写:
62
+ // 点击/导航进来时这里写入,必填且为空的格自己写入,焦点离开且校验通过后自己删除。@author nokecy
61
63
  var activeCellsRef = useRef(new Set());
62
64
  // 无 _id/id 的外部数据按对象引用分配稳定 rowId(避免 getRowId 返回 undefined)。@author nokecy
63
65
  var rowIdMapRef = useRef(new WeakMap());
@@ -78,14 +80,16 @@ var ArrayTableInner = observer(function (props) {
78
80
  // 通过 JSON.stringify 比较内容,只有内容真正变化时才返回新引用
79
81
  var sourcesKey = useMemo(function () {
80
82
  return JSON.stringify(rawSources.map(function (s) {
81
- var _s$columnProps$header, _s$columnProps, _s$columnProps2;
83
+ var _s$columnProps$header, _s$columnProps, _s$columnProps2, _s$columnProps3;
82
84
  return {
83
85
  name: s.name,
84
86
  display: s.display,
85
87
  // 列标题可由 reactions 运行时改写,纳入 key 才能让 stableColumnDefs 跟着重建。
86
88
  // 宽度/隐藏/固定由 applyColumnState(方案)管,故意不纳入,避免行编辑时列状态被重置。
87
89
  // 可编辑性:带 x-reactions 的列在 buildArrayTableColumns 里已强制走 AgField 全树(尊重运行时 pattern),无需进 key。@author nokecy
88
- 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
90
+ 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,
91
+ // 分组名变了列组结构就变,同样要重建。@author nokecy
92
+ headerGroup: (_s$columnProps3 = s.columnProps) === null || _s$columnProps3 === void 0 ? void 0 : _s$columnProps3.headerGroup
89
93
  };
90
94
  }));
91
95
  }, [rawSources]);
@@ -146,6 +150,12 @@ var ArrayTableInner = observer(function (props) {
146
150
  return cols;
147
151
  }, [sources, props.onRowDrag]); // 移除 field 依赖,避免数据变化时重新计算
148
152
 
153
+ // 交给 AgGridReact 的列定义:按 x-component-props.headerGroup 把相邻同组的列合并成列组(多级表头)。
154
+ // stableColumnDefs 保持扁平,懒列识别、列方案还原等内部逻辑仍用它;只有喂给 AG-Grid 的这一份带分组。@author nokecy
155
+ var gridColumnDefs = useMemo(function () {
156
+ return groupColumnDefs(stableColumnDefs);
157
+ }, [stableColumnDefs]);
158
+
149
159
  // 懒挂载:把激活态 Set 通过 AG-Grid context 暴露给 LazyAgField(引用稳定,原地 mutate)。@author nokecy
150
160
  var gridContext = useMemo(function () {
151
161
  return {
@@ -165,15 +175,15 @@ var ArrayTableInner = observer(function (props) {
165
175
 
166
176
  /**
167
177
  * 激活某个懒挂载单元格:加入激活集合并强制重渲该格 -> LazyAgField 改挂 AgField 全树。
168
- * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/新增行/已激活)。@author nokecy
178
+ * 焦点离开后 LazyAgField 自己把它从集合里移除并卸回文本(见 utils.tsx)。
179
+ * 返回 true 表示「本次新激活」(调用方需等挂载完成再聚焦);false 表示无需激活(非懒列/已挂载着,
180
+ * 如必填且为空的格本就挂着)。新增行的格与存量行一视同仁:非必填的也是先文本、点了才挂。@author nokecy
169
181
  */
170
182
  var activateCell = useCallback(function (api, node, colId) {
171
- var _node$data;
172
183
  if (!api || !node || !colId) return false;
173
184
  if (!lazyColIds.has(colId)) return false; // 非懒列(已是 AgField/纯文本)
174
- if ((_node$data = node.data) !== null && _node$data !== void 0 && _node$data._isNew) return false; // 新增行本就 eager 挂着
175
185
  var key = "".concat(node.id, ":").concat(colId);
176
- if (activeCellsRef.current.has(key)) return false; // 已激活
186
+ if (activeCellsRef.current.has(key)) return false; // 已挂载
177
187
  activeCellsRef.current.add(key);
178
188
  api.refreshCells({
179
189
  rowNodes: [node],
@@ -222,8 +232,8 @@ var ArrayTableInner = observer(function (props) {
222
232
  if (hasGroups) {
223
233
  // 传入 stableColumnDefs 作为原始定义,保留 cellRenderer(AgField/LazyAgField)及其
224
234
  // cellRendererParams——否则分组方案重建出的是裸列,单元格退化为纯文本、编辑失效。@author nokecy
225
- var gridColumnDefs = flatConfigToGridState(currentScheme.columnConfig, stableColumnDefs);
226
- gridApiState.setGridOption('columnDefs', gridColumnDefs);
235
+ var _gridColumnDefs = flatConfigToGridState(currentScheme.columnConfig, stableColumnDefs);
236
+ gridApiState.setGridOption('columnDefs', _gridColumnDefs);
227
237
  }
228
238
 
229
239
  // 2. 应用列状态(列顺序、宽度、隐藏、固定等)
@@ -378,6 +388,8 @@ var ArrayTableInner = observer(function (props) {
378
388
  enableCellTextSelection: true,
379
389
  defaultColDef: {
380
390
  resizable: true,
391
+ // 输入框里的 Ctrl/Cmd+A 交给浏览器全选文字,不让 AG-Grid 当成「选中所有单元格」拦走
392
+ suppressKeyboardEvent: suppressSelectAllInInput,
381
393
  headerComponentParams: {
382
394
  template: '<div class="ag-cell-label-container" role="presentation">' +
383
395
  // ' <span data-ref="eMenu" class="ag-header-icon ag-header-cell-menu-button" aria-hidden="true"></span>' +
@@ -416,7 +428,7 @@ var ArrayTableInner = observer(function (props) {
416
428
  ,
417
429
  rowHeight: 38,
418
430
  pagination: false,
419
- columnDefs: stableColumnDefs,
431
+ columnDefs: gridColumnDefs,
420
432
  rowData: dataSource
421
433
  })), sources.map(function (column, key) {
422
434
  //专门用来承接对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;
@@ -14,7 +14,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
14
14
  *
15
15
  * @author nokecy
16
16
  */
17
-
17
+ import dayjs from 'dayjs';
18
18
  /** 从字段 schema / 列 props 里尽量取到 Select 的可选项(value->label 映射用) */
19
19
  var resolveOptions = function resolveOptions(ctx) {
20
20
  var _ref, _ref2, _ref3, _fieldSchema$enum, _fieldSchema$xCompon, _fieldSchema$xCompon2;
@@ -56,9 +56,11 @@ var dateFormatter = function dateFormatter(value, ctx) {
56
56
  var _ref6, _ctx$columnProps$form, _ctx$columnProps, _ctx$fieldSchema;
57
57
  if (value == null || value === '') return '';
58
58
  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';
59
- // 值多为 dayjs 对象(有 .format)或已格式化的字符串;不强引 dayjs,保持轻量
60
- if (value && typeof value.format === 'function') return value.format(fmt);
61
- return String(value);
59
+ if (typeof value.format === 'function') return value.format(fmt);
60
+ // 后端回来的多是 ISO 串(2024-10-19T00:00:00)或时间戳:与 Formily readPretty 一致,按 dayjs(value).format(fmt) 显示;
61
+ // 解析不了的文本(如「待定」)原样显示,不吞掉。dayjs 随 antd 传递依赖引入,本库其他模块已直接使用。
62
+ var parsed = dayjs(value);
63
+ return parsed.isValid() ? parsed.format(fmt) : String(value);
62
64
  };
63
65
  var booleanFormatter = function booleanFormatter(value, ctx) {
64
66
  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
  /**
@@ -1,19 +1,30 @@
1
- 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); }
2
- var _excluded = ["hide", "sort", "sortIndex", "forceAgField"];
1
+ var _excluded = ["hide", "sort", "sortIndex", "forceAgField", "headerGroup", "defaultHide"];
2
+ function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
3
+ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
3
4
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
5
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
6
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
7
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
8
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
8
- function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
9
- function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
10
- import React from 'react';
9
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
10
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
11
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
12
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
13
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
14
+ 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); }
15
+ 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; }
16
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
17
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
18
+ 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); }
19
+ import React, { useReducer, useRef } from 'react';
11
20
  import { theme } from 'antd';
12
21
  import { useField, useFieldSchema, RecursionField, Observer, useExpressionScope } from '@formily/react';
13
22
  import { isArr, lazyMerge } from '@formily/shared';
14
23
  import { Schema } from '@formily/json-schema';
15
24
  import ArrayBase from "./arrayBase";
16
25
  import { getReadOnlyFormatter } from "./readOnlyFormatters";
26
+ import { markHeaderGroup } from "./columnGroup";
27
+ import { CellFocusScope } from "./cellFocusScope";
17
28
  var isColumnComponent = function isColumnComponent(schema) {
18
29
  var _schema$xComponent;
19
30
  return ((_schema$xComponent = schema['x-component']) === null || _schema$xComponent === void 0 ? void 0 : _schema$xComponent.indexOf('Column')) > -1;
@@ -205,10 +216,12 @@ var resolveLazyCellPattern = function resolveLazyCellPattern(arrayField, record,
205
216
  try {
206
217
  var _arrayField$value$ind, _arrayField$value, _arrayField$value$ind2;
207
218
  var index = (_arrayField$value$ind = (_arrayField$value = arrayField.value) === null || _arrayField$value === void 0 || (_arrayField$value$ind2 = _arrayField$value.indexOf) === null || _arrayField$value$ind2 === void 0 ? void 0 : _arrayField$value$ind2.call(_arrayField$value, record)) !== null && _arrayField$value$ind !== void 0 ? _arrayField$value$ind : -1;
208
- var path = arrayField.address.concat(index).concat(dataKey);
219
+ // 与真字段一致:path 是数据路径(不含 void 容器),address 含 void 容器;表达式里 getValuesIn($self.path...) 靠前者
220
+ var path = arrayField.path.concat(index).concat(dataKey);
221
+ var address = arrayField.address.concat(index).concat(dataKey);
209
222
  var self = {
210
223
  path: path,
211
- address: path,
224
+ address: address,
212
225
  index: index,
213
226
  form: form,
214
227
  parent: arrayField,
@@ -232,21 +245,60 @@ var resolveLazyCellPattern = function resolveLazyCellPattern(arrayField, record,
232
245
  }
233
246
  };
234
247
 
248
+ /**
249
+ * 值算不算「没填」:null/undefined/空串,或成员全是这类值的数组/对象(如 labelInValue 的
250
+ * {value:undefined,label:undefined})。0 和 false 是有效值,不算空。@author nokecy
251
+ */
252
+ var isBlank = function isBlank(value) {
253
+ if (value == null || value === '') return true;
254
+ if (Array.isArray(value)) return value.every(isBlank);
255
+ if (Object.prototype.toString.call(value) === '[object Object]') return Object.values(value).every(isBlank);
256
+ return false;
257
+ };
258
+ var hasRequiredRule = function hasRequiredRule(rule) {
259
+ if (rule == null) return false;
260
+ if (rule === 'required') return true;
261
+ if (Array.isArray(rule)) return rule.some(hasRequiredRule);
262
+ if (_typeof(rule) === 'object') return rule.required != null && rule.required !== false;
263
+ return false;
264
+ };
265
+
266
+ /**
267
+ * 数据字段是否必填(静态判定):字段上写了 required,或 x-validator 里带 required 规则。
268
+ *
269
+ * 判不准(required 写成表达式串)时按必填算:误判必填只是多挂一个控件,
270
+ * 误判非必填会让空的必填格没人校验——与只读判定同样的安全不对称。
271
+ * 靠 x-reactions 动态改必填的列本就整列 eager,不走这里。@author nokecy
272
+ */
273
+ var isRequiredSchema = function isRequiredSchema(fieldSchema) {
274
+ if (!fieldSchema) return false;
275
+ var required = fieldSchema.required;
276
+ if (required === true || typeof required === 'string') return true;
277
+ return hasRequiredRule(fieldSchema['x-validator']);
278
+ };
279
+
235
280
  /**
236
281
  * 懒挂载单元格渲染器(可编辑列)。
237
282
  *
238
283
  * 已有行的可编辑格被 Editable 装饰器默认转成 readPretty 只读文本态(点击才进编辑),
239
- * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器:
240
- * - 新增行(_isNew,保持编辑态需即时校验) 或 已被激活(点击/导航进过)的格 -> 挂 AgField 全树;
241
- * - 其余(存量行、没碰过) -> 只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。
284
+ * 但仍挂着完整 Formily 树——这是行多列多滚动卡的主因。本渲染器让每个格在两种形态间切换:
285
+ * - 懒挂载文本:只渲染廉价纯文本(复用 readOnlyFormatters),不挂 Formily 树。默认形态。
286
+ * - 挂载态:挂 AgField 全树,可编辑、可校验。
242
287
  *
243
- * 激活态存放在 AG-Grid context.activeCells(Set),由 index.tsx 在点击/键盘导航时写入并 refreshCells。
244
- * 「挂了不卸」:一旦激活保持挂载(直到行被虚拟化销毁),不做失焦退回,避开一堆边界。
288
+ * 何时挂载:
289
+ * 1. 被激活(点击/键盘导航进来):激活态存放在 AG-Grid context.activeCells(Set),
290
+ * 由 index.tsx 写入并 refreshCells;
291
+ * 2. 必填且为空:空的必填格必须是真控件,否则没有字段节点,提交时没人校验它。
292
+ * 不区分新增行、存量行、initialValues 带进来的行——只看「必填且空」。
293
+ * 例外:Editable 装饰器的非新增行,Editable 自己会把它转成只读态,挂了也不校验,留在文本态。
294
+ * 何时卸回文本:焦点离开本格,且该字段校验通过(必填的已填上、没有格式错误)。
295
+ * 必填仍为空或校验有错则留着控件,让用户看见错误——与 Editable 失焦时的判断一致。
296
+ * 卸掉的只是渲染树,Formily 字段节点与值都还在,再次点击会接着用。
245
297
  *
246
- * 校验安全:未碰过的存量格本就是 readPretty、本就不参与校验,不挂树校验语义零变化。@author nokecy
298
+ * 校验安全:空的必填格不会卸成文本(见上);其余格卸载前已校验通过。@author nokecy
247
299
  */
248
300
  var LazyAgField = function LazyAgField(params) {
249
- var _context$activeCells, _context$activeCells$, _fieldSchema$name;
301
+ var _context$activeCells, _fieldSchema$name;
250
302
  var value = params.value,
251
303
  record = params.data,
252
304
  dataSource = params.dataSource,
@@ -257,32 +309,105 @@ var LazyAgField = function LazyAgField(params) {
257
309
  context = params.context,
258
310
  formatter = params.formatter,
259
311
  columnProps = params.columnProps,
260
- fieldSchema = params.fieldSchema;
312
+ fieldSchema = params.fieldSchema,
313
+ required = params.required;
261
314
  // hook 必须在下面的条件 return 之前调用
262
315
  var _theme$useToken = theme.useToken(),
263
316
  token = _theme$useToken.token;
264
317
  var scope = useExpressionScope();
318
+ var _useReducer = useReducer(function (n) {
319
+ return n + 1;
320
+ }, 0),
321
+ _useReducer2 = _slicedToArray(_useReducer, 2),
322
+ rerender = _useReducer2[1];
323
+ var localCells = useRef(new Set());
324
+ var activeCells = (_context$activeCells = context === null || context === void 0 ? void 0 : context.activeCells) !== null && _context$activeCells !== void 0 ? _context$activeCells : localCells.current;
265
325
  var key = "".concat(node === null || node === void 0 ? void 0 : node.id, ":").concat(colDef === null || colDef === void 0 ? void 0 : colDef.field);
266
- var activated = (record === null || record === void 0 ? void 0 : record._isNew) === true || (context === null || context === void 0 || (_context$activeCells = context.activeCells) === null || _context$activeCells === void 0 || (_context$activeCells$ = _context$activeCells.has) === null || _context$activeCells$ === void 0 ? void 0 : _context$activeCells$.call(_context$activeCells, key)) === true;
267
- if (activated) {
268
- return /*#__PURE__*/React.createElement(AgField, {
269
- value: value,
270
- data: record,
271
- dataSource: dataSource,
272
- schema: schema,
273
- field: field,
274
- node: node
275
- });
276
- }
326
+ // 内层字段名:Column key 与它可能不一致(设计器形态),取值/找字段节点都按它
327
+ var dataKey = (_fieldSchema$name = fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema.name) !== null && _fieldSchema$name !== void 0 ? _fieldSchema$name : colDef === null || colDef === void 0 ? void 0 : colDef.field;
277
328
 
278
- // Observer 包裹纯文本:让未激活的廉价文本格反应式跟随内层 Formily 字段值。
329
+ /** 焦点离开本格:校验通过才卸回懒挂载文本。isStale 为真说明校验期间焦点又回来了或格子已销毁 */
330
+ var handleLeave = /*#__PURE__*/function () {
331
+ var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(isStale) {
332
+ var _field$value$indexOf3, _field$value3, _field$value3$indexOf, _field$form;
333
+ var index, cellField;
334
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
335
+ while (1) switch (_context.prev = _context.next) {
336
+ case 0:
337
+ index = (_field$value$indexOf3 = field === null || field === void 0 || (_field$value3 = field.value) === null || _field$value3 === void 0 || (_field$value3$indexOf = _field$value3.indexOf) === null || _field$value3$indexOf === void 0 ? void 0 : _field$value3$indexOf.call(_field$value3, record)) !== null && _field$value$indexOf3 !== void 0 ? _field$value$indexOf3 : -1; // 字段节点按数据路径找(不含 void 容器);从没挂载过就没有节点,无需校验
338
+ cellField = index >= 0 && dataKey ? (_field$form = field.form) === null || _field$form === void 0 ? void 0 : _field$form.query(field.path.concat(index).concat(dataKey)).take() : undefined;
339
+ if (!cellField) {
340
+ _context.next = 11;
341
+ break;
342
+ }
343
+ _context.prev = 3;
344
+ _context.next = 6;
345
+ return cellField.validate();
346
+ case 6:
347
+ _context.next = 11;
348
+ break;
349
+ case 8:
350
+ _context.prev = 8;
351
+ _context.t0 = _context["catch"](3);
352
+ return _context.abrupt("return");
353
+ case 11:
354
+ if (!isStale()) {
355
+ _context.next = 13;
356
+ break;
357
+ }
358
+ return _context.abrupt("return");
359
+ case 13:
360
+ if (!(required && isBlank(dataKey != null ? readByFieldPath(record, dataKey) : undefined))) {
361
+ _context.next = 15;
362
+ break;
363
+ }
364
+ return _context.abrupt("return");
365
+ case 15:
366
+ activeCells.delete(key);
367
+ rerender();
368
+ case 17:
369
+ case "end":
370
+ return _context.stop();
371
+ }
372
+ }, _callee, null, [[3, 8]]);
373
+ }));
374
+ return function handleLeave(_x) {
375
+ return _ref3.apply(this, arguments);
376
+ };
377
+ }();
378
+
379
+ // Observer 让本格反应式跟随内层 Formily 字段值。
279
380
  // record 即 field.value[index] 的可观察对象;在 Observer 里按内层字段名直接读 record[dataKey]
280
381
  // (而非 AG-Grid 渲染时快照的 params.value)建立依赖后,onFieldValueChange/setValuesIn 程序化原地
281
382
  // 改值(行引用不变 → AG-Grid 不刷新该格、外层 observer 也不重渲)时,本格能自行重渲刷新——
282
383
  // 免去调用方加 forceAgField 逃生阀。字段名缺失才回退 params.value。@author nokecy
283
- var dataKey = (_fieldSchema$name = fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema.name) !== null && _fieldSchema$name !== void 0 ? _fieldSchema$name : colDef === null || colDef === void 0 ? void 0 : colDef.field;
284
384
  return /*#__PURE__*/React.createElement(Observer, null, function () {
285
- var liveValue = dataKey != null && record ? readByFieldPath(record, dataKey) : value;
385
+ var activated = activeCells.has(key);
386
+ // 已挂载时不读值:挂载后每敲一个字都会变,追踪它会让整棵字段树跟着重渲
387
+ var liveValue = activated ? undefined : dataKey != null && record ? readByFieldPath(record, dataKey) : value;
388
+ // Editable 装饰器会把非新增行的格转成只读态、不参与校验(见 editable/index.tsx),
389
+ // 这时挂了没用,还会把灰色「请输入」占位换成只读态的「N/A」,所以留在文本态
390
+ var editableWillReadPretty = (fieldSchema === null || fieldSchema === void 0 ? void 0 : fieldSchema['x-decorator']) === 'Editable' && (record === null || record === void 0 ? void 0 : record._isNew) !== true;
391
+ if (!activated && required && !editableWillReadPretty && isBlank(liveValue)) {
392
+ var _pattern = resolveLazyCellPattern(field, record, dataKey, fieldSchema, scope);
393
+ if (_pattern == null || _pattern === 'editable') {
394
+ // 记进激活集合:用户填了第一个字后值不再为空,但格子仍在编辑,要挂到失焦为止
395
+ activeCells.add(key);
396
+ activated = true;
397
+ }
398
+ }
399
+ if (activated) {
400
+ return /*#__PURE__*/React.createElement(CellFocusScope, {
401
+ onLeave: handleLeave
402
+ }, /*#__PURE__*/React.createElement(AgField, {
403
+ value: value,
404
+ data: record,
405
+ dataSource: dataSource,
406
+ schema: schema,
407
+ field: field,
408
+ node: node
409
+ }));
410
+ }
286
411
  var text = formatter ? formatter(liveValue, {
287
412
  columnProps: columnProps,
288
413
  fieldSchema: fieldSchema
@@ -393,14 +518,35 @@ var isColumnReadOnlyEligible = function isColumnReadOnlyEligible(arrayPattern, d
393
518
  return false;
394
519
  };
395
520
 
521
+ /** 明细单元格里的数字输入组件(antd InputNumber 及其 Formily 封装) */
522
+ var NUMBER_INPUT_COMPONENTS = new Set(['NumberPicker', 'InputNumber']);
523
+
524
+ /**
525
+ * 明细单元格里的数字输入默认去掉上下步进:不显示上下按钮、不响应方向键步进。
526
+ *
527
+ * 上下按钮会压住右对齐数字的末尾;方向键步进则与单元格间的方向键导航冲突——
528
+ * 在数字格里按 ↑/↓,导航到别的格的同时,被离开的那格数值也被悄悄 +1/-1。
529
+ * 字段的 x-component-props 里显式写了 controls/keyboard 的以使用方为准。
530
+ * 只对会挂真控件的列调用(只读快路径没有输入框)。幂等。@author nokecy
531
+ */
532
+ var applyNumberInputDefaults = function applyNumberInputDefaults(dataField) {
533
+ if (!dataField || !NUMBER_INPUT_COMPONENTS.has(dataField['x-component'])) return;
534
+ var props = dataField['x-component-props'];
535
+ if (props != null && _typeof(props) !== 'object') return; // 整个 props 写成了表达式串,不动
536
+ dataField['x-component-props'] = _objectSpread({
537
+ controls: false,
538
+ keyboard: false
539
+ }, props);
540
+ };
541
+
396
542
  // 重命名自 useArrayTableColumns:内部无任何 Hook 调用,use 前缀违反 Hook 命名约定且曾在 useMemo 内被调用 @author nokecy
397
543
  var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field, sources) {
398
544
  var arrayPattern = field === null || field === void 0 ? void 0 : field.pattern;
399
- return sources.reduce(function (buf, _ref3, key) {
400
- var name = _ref3.name,
401
- columnProps = _ref3.columnProps,
402
- schema = _ref3.schema,
403
- display = _ref3.display;
545
+ return sources.reduce(function (buf, _ref4, key) {
546
+ var name = _ref4.name,
547
+ columnProps = _ref4.columnProps,
548
+ schema = _ref4.schema,
549
+ display = _ref4.display;
404
550
  if (display && display !== 'visible') return buf;
405
551
  if (!isColumnComponent(schema)) return buf;
406
552
 
@@ -413,6 +559,8 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
413
559
  sort = columnProps.sort,
414
560
  sortIndex = columnProps.sortIndex,
415
561
  forceAgField = columnProps.forceAgField,
562
+ headerGroup = columnProps.headerGroup,
563
+ defaultHide = columnProps.defaultHide,
416
564
  safeColumnProps = _objectWithoutProperties(columnProps, _excluded);
417
565
  var base = _objectSpread(_objectSpread({}, safeColumnProps), {}, {
418
566
  // 保留 width 作为列的初始宽度
@@ -420,18 +568,27 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
420
568
  //@ts-ignore
421
569
  headerName: columnProps.headerName || columnProps.title,
422
570
  field: name
423
- });
571
+ }, defaultHide === true ? {
572
+ initialHide: true
573
+ } : {});
574
+
575
+ // 记下分组名再输出列定义;真正合并成列组在 index.tsx 交给 AG-Grid 之前(groupColumnDefs)做,
576
+ // 这里产出的仍是扁平列定义。@author nokecy
577
+ var emit = function emit(colDef) {
578
+ return buf.concat(markHeaderGroup(colDef, headerGroup));
579
+ };
424
580
 
425
581
  // 三分流渲染策略:@author nokecy
426
582
  // ① 只读纯文本(永不可编辑):只读列 + 有格式化器 -> valueFormatter,不挂 Formily 树。
427
583
  // ② 可编辑懒挂载:可编辑列 + 有格式化器 + 无 reactions + 未强制 eager -> LazyAgField,
428
- // 存量格先出廉价文本、点击/导航才挂全树(挂了不卸)。
584
+ // 格子先出廉价文本、点击/导航(或必填且为空)才挂全树,焦点离开且校验通过再卸回文本。
429
585
  // ③ 兜底全树:其余(无格式化器/带 reactions/forceAgField/复杂字段名/只读但无格式化器) -> AgField。
430
586
  var dataField = findDataFieldSchema(schema, name);
431
587
  var component = dataField === null || dataField === void 0 ? void 0 : dataField['x-component'];
432
588
  var formatter = getReadOnlyFormatter(component);
433
589
  var readOnly = isColumnReadOnlyEligible(arrayPattern, dataField, columnProps);
434
590
  var hasReactions = !!(dataField && dataField['x-reactions']);
591
+ if (!readOnly) applyNumberInputDefaults(dataField);
435
592
  var forceEager = (columnProps === null || columnProps === void 0 ? void 0 : columnProps.forceAgField) === true;
436
593
  // Switch/Checkbox 是「单击直控」组件:可编辑态本就该常驻渲染真控件(优化前即如此)。
437
594
  // 懒挂载会把它降成「是/否」纯文本(点击才挂出开关),用户会以为开关消失。故可编辑态强制 eager。
@@ -456,7 +613,7 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
456
613
  // 读 AG-Grid 缓存值不刷新 → 反应式只读渲染。整表 readPretty(明细/详情页,数据 setValues 整体加载、
457
614
  // 行引用整体替换、AG-Grid 自然刷新)仍走更廉价的静态 valueFormatter 快路径。@author nokecy
458
615
  if (arrayPattern !== 'readPretty') {
459
- return buf.concat(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
616
+ return emit(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
460
617
  valueGetter: valueGetter
461
618
  } : {}), {}, {
462
619
  cellRenderer: LazyReadOnlyText,
@@ -467,7 +624,7 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
467
624
  }
468
625
  }));
469
626
  }
470
- return buf.concat(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
627
+ return emit(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
471
628
  valueGetter: valueGetter
472
629
  } : {}), {}, {
473
630
  // 显式清掉可能从 safeColumnProps 漏带的 cellRenderer(AG-Grid 中 renderer 优先级高于 formatter)
@@ -483,7 +640,7 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
483
640
 
484
641
  // ② 可编辑懒挂载(排除单击直控的 Switch/Checkbox:须 eager 出真控件)
485
642
  if (!readOnly && formatter && !hasReactions && !forceEager && !isInlineToggle && canFastPath) {
486
- return buf.concat(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
643
+ return emit(_objectSpread(_objectSpread(_objectSpread({}, base), valueGetter ? {
487
644
  valueGetter: valueGetter
488
645
  } : {}), {}, {
489
646
  cellRenderer: LazyAgField,
@@ -494,13 +651,15 @@ var buildArrayTableColumns = function buildArrayTableColumns(dataSource, field,
494
651
  formatter: formatter,
495
652
  columnProps: columnProps,
496
653
  fieldSchema: dataField,
497
- lazyEditable: true // 供 index.tsx 识别懒列(激活/导航用)
654
+ lazyEditable: true,
655
+ // 供 index.tsx 识别懒列(激活/导航用)
656
+ required: isRequiredSchema(dataField) // 必填且为空的格必须挂真控件,见 LazyAgField
498
657
  }
499
658
  }));
500
659
  }
501
660
 
502
661
  // ③ 兜底全树
503
- return buf.concat(_objectSpread(_objectSpread({}, base), {}, {
662
+ return emit(_objectSpread(_objectSpread({}, base), {}, {
504
663
  cellRendererParams: {
505
664
  dataSource: dataSource,
506
665
  schema: schema,