@jc-times/business-ui 0.2.55 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/cascader.md CHANGED
@@ -1,58 +1,58 @@
1
- # 通用级联选择
2
-
3
- 0.2.48 起,桌面展开多列时按可见视口避让边缘,避免右侧窄栏的区县列越界;继续使用 RC 的定位与碰撞处理。
4
-
5
- 当前本地新增能力;消费方必须先核对安装版本导出,不能将本地源码视为已发布。
6
-
7
- `Cascader` 使用固定 `@rc-component/cascader@1.25.0` 内核,外观使用 `--ui-*` 令牌。桌面多列浮层,600px 以下底部面板,列过多时面板内部横向滚动;手机软键盘仍需真实设备验收。浮层归属最近 ModalShell,Escape 先关闭选择面板。
8
-
9
- ## 单选与多选
10
-
11
- ```tsx
12
- import { Cascader } from "@jc-times/business-ui";
13
-
14
- <Cascader label="所在地区" options={options} value={path} onValueChange={setPath} />
15
- <Cascader label="部门" options={options} multiple value={paths} onValueChange={setPaths} />
16
- ```
17
-
18
- - `options`:`{ value: string; label: string; children?: CascaderOption[]; disabled?: boolean; isLeaf?: boolean }[]`。同一层 value 唯一;使用 ID 索引适配器时要求全局唯一。
19
- - 单选 `value: string[]`,多选 `value: string[][]`;`onValueChange` 第二参数为对应节点路径或路径数组。清空统一为 `[]`。
20
- - 单选默认只提交叶子;`changeOnSelect` 允许选择中间层。
21
- - 多选默认 `checkStrictly=true`,父子独立勾选;设为 false 启用父子联动。点击父级文字展开,点击复选框选中。
22
- - `searchable` 默认开启;路径搜索忽略空格和 `/`,最多显示 50 项。不是远程搜索,也不宣称大列表虚拟化。
23
- - `loadData(path)` 用于按需加载,调用方更新 options 并处理网络错误与取消;提供 loadData 时默认关闭搜索。若显式启用搜索,只能查找已经加载的节点。
24
- - 点击标签或通过 Tab 聚焦不会自动展开;点击选择框、输入搜索词或按方向键可展开。
25
- - `open/onOpenChange` 控制浮层;`disabled`、`required`、`label/helpText/error`、`placeholder/emptyText`、`allowClear/clearLabel`、多选 `maxTagCount`(默认 3)用于常规表单。
26
- - `inputRef/inputProps` 指向真实搜索 input,可传 ARIA、data 属性及原生事件。搜索 input 的值是搜索词,已选路径以可见文本回填;提交使用 onValueChange,不能读取 input.value 或原生 FormData 作为已选路径。
27
-
28
- ## 部门 ID 适配
29
-
30
- 业务接口仍接收部门 ID,路径转换放在消费方。options 和索引应按数据引用 useMemo,避免每次输入重新建树。
31
-
32
- ```tsx
33
- const options = useMemo(() => buildCascaderOptions(departments), [departments]);
34
- const pathsById = useMemo(() => indexCascaderPaths(options), [options]);
35
- // departments: { id, label, parentId?, disabled? }[]
36
- // 单选可选择任意层级,清空后回调 null。
37
- <Cascader label="部门" options={options} changeOnSelect
38
- value={departmentId ? pathsById.get(departmentId) ?? [] : []}
39
- onValueChange={path => setDepartmentId(path.at(-1) ?? null)} />
40
-
41
- <Cascader label="参与部门" options={options} multiple
42
- value={departmentIds.flatMap(id => { const path = pathsById.get(id); return path ? [path] : []; })}
43
- onValueChange={paths => setDepartmentIds(paths.map(path => path[path.length - 1]))} />
44
- ```
45
-
46
- `buildCascaderOptions` 保持输入顺序,父节点未提供时作为根节点,拒绝重复/空 ID 与循环;`indexCascaderPaths` 要求全局唯一 value。调用方应补齐已选但不在当前候选集中的部门并提供其祖先,避免标签丢失。
47
-
48
- ## 地址与旧树形选择
49
-
50
- StructuredAddressInput 内部已复用 Cascader,保留 regions、StructuredAddressValue、regionInputRef/Props、详细地址和自由地址接口。点中间地区仍立即提交路径,点叶子后聚焦详细地址;支持清除地区。原先 region input 中显示的摘要改为选择框的可见文本,自动化定位应使用标签与可见文本,保存继续使用 value.regionIds。
51
-
52
- TreeSelect 保持原有树形体验和 API,不会自动改变已安装消费项目的部门控件;需要多列级联的消费点按上面的适配方式迁移。
53
-
54
- ## 可运行案例
55
-
56
- 运行 `pnpm dev:cascader`,打开 http://127.0.0.1:4187/examples/cascader-demo/ 。源码位于 examples/cascader-demo/main.tsx。案例使用模拟部门与内置全国地区数据,展示任意层级单选、独立/联动多选、结构化/自由地址、弹窗选择、实时摘要与提交快照;不发送接口请求。
57
-
58
- 默认不展示地址类型,国内选省市区,海外选顶级“海外”并在详细地址填写国家/城市/街道。旧数据的 freeform 模式如需保留编辑,可显式 showModeSelector=true(复用公共 Select);默认模式交互统一提交 mode=structured,历史 freeform 数据由消费方迁移到海外路径与 detail。
1
+ # 通用级联选择
2
+
3
+ 0.2.48 起,桌面展开多列时按可见视口避让边缘,避免右侧窄栏的区县列越界;继续使用 RC 的定位与碰撞处理。
4
+
5
+ 当前本地新增能力;消费方必须先核对安装版本导出,不能将本地源码视为已发布。
6
+
7
+ `Cascader` 使用固定 `@rc-component/cascader@1.25.0` 内核,外观使用 `--ui-*` 令牌。桌面多列浮层,600px 以下底部面板,列过多时面板内部横向滚动;手机软键盘仍需真实设备验收。浮层归属最近 ModalShell,Escape 先关闭选择面板。
8
+
9
+ ## 单选与多选
10
+
11
+ ```tsx
12
+ import { Cascader } from "@jc-times/business-ui";
13
+
14
+ <Cascader label="所在地区" options={options} value={path} onValueChange={setPath} />
15
+ <Cascader label="部门" options={options} multiple value={paths} onValueChange={setPaths} />
16
+ ```
17
+
18
+ - `options`:`{ value: string; label: string; children?: CascaderOption[]; disabled?: boolean; isLeaf?: boolean }[]`。同一层 value 唯一;使用 ID 索引适配器时要求全局唯一。
19
+ - 单选 `value: string[]`,多选 `value: string[][]`;`onValueChange` 第二参数为对应节点路径或路径数组。清空统一为 `[]`。
20
+ - 单选默认只提交叶子;`changeOnSelect` 允许选择中间层。
21
+ - 多选默认 `checkStrictly=true`,父子独立勾选;设为 false 启用父子联动。点击父级文字展开,点击复选框选中。
22
+ - `searchable` 默认开启;路径搜索忽略空格和 `/`,最多显示 50 项。不是远程搜索,也不宣称大列表虚拟化。
23
+ - `loadData(path)` 用于按需加载,调用方更新 options 并处理网络错误与取消;提供 loadData 时默认关闭搜索。若显式启用搜索,只能查找已经加载的节点。
24
+ - 点击标签或通过 Tab 聚焦不会自动展开;点击选择框、输入搜索词或按方向键可展开。
25
+ - `open/onOpenChange` 控制浮层;`disabled`、`required`、`label/helpText/error`、`placeholder/emptyText`、`allowClear/clearLabel`、多选 `maxTagCount`(默认 3)用于常规表单。
26
+ - `inputRef/inputProps` 指向真实搜索 input,可传 ARIA、data 属性及原生事件。搜索 input 的值是搜索词,已选路径以可见文本回填;提交使用 onValueChange,不能读取 input.value 或原生 FormData 作为已选路径。
27
+
28
+ ## 部门 ID 适配
29
+
30
+ 业务接口仍接收部门 ID,路径转换放在消费方。options 和索引应按数据引用 useMemo,避免每次输入重新建树。
31
+
32
+ ```tsx
33
+ const options = useMemo(() => buildCascaderOptions(departments), [departments]);
34
+ const pathsById = useMemo(() => indexCascaderPaths(options), [options]);
35
+ // departments: { id, label, parentId?, disabled? }[]
36
+ // 单选可选择任意层级,清空后回调 null。
37
+ <Cascader label="部门" options={options} changeOnSelect
38
+ value={departmentId ? pathsById.get(departmentId) ?? [] : []}
39
+ onValueChange={path => setDepartmentId(path.at(-1) ?? null)} />
40
+
41
+ <Cascader label="参与部门" options={options} multiple
42
+ value={departmentIds.flatMap(id => { const path = pathsById.get(id); return path ? [path] : []; })}
43
+ onValueChange={paths => setDepartmentIds(paths.map(path => path[path.length - 1]))} />
44
+ ```
45
+
46
+ `buildCascaderOptions` 保持输入顺序,父节点未提供时作为根节点,拒绝重复/空 ID 与循环;`indexCascaderPaths` 要求全局唯一 value。调用方应补齐已选但不在当前候选集中的部门并提供其祖先,避免标签丢失。
47
+
48
+ ## 地址与旧树形选择
49
+
50
+ StructuredAddressInput 内部已复用 Cascader,保留 regions、StructuredAddressValue、regionInputRef/Props、详细地址和自由地址接口。点中间地区仍立即提交路径,点叶子后聚焦详细地址;支持清除地区。原先 region input 中显示的摘要改为选择框的可见文本,自动化定位应使用标签与可见文本,保存继续使用 value.regionIds。
51
+
52
+ TreeSelect 保持原有树形体验和 API,不会自动改变已安装消费项目的部门控件;需要多列级联的消费点按上面的适配方式迁移。
53
+
54
+ ## 可运行案例
55
+
56
+ 运行 `pnpm dev:cascader`,打开 http://127.0.0.1:4187/examples/cascader-demo/ 。源码位于 examples/cascader-demo/main.tsx。案例使用模拟部门与内置全国地区数据,展示任意层级单选、独立/联动多选、结构化/自由地址、弹窗选择、实时摘要与提交快照;不发送接口请求。
57
+
58
+ 默认不展示地址类型,国内选省市区,海外选顶级“海外”并在详细地址填写国家/城市/街道。旧数据的 freeform 模式如需保留编辑,可显式 showModeSelector=true(复用公共 Select);默认模式交互统一提交 mode=structured,历史 freeform 数据由消费方迁移到海外路径与 detail。
@@ -1,94 +1,94 @@
1
- # 公共组件参考与采用策略
2
-
3
- 整理日期:2026-09-11
4
-
5
- 组件使用从 [Agent 指南](agent-guide.md) 开始,参数与示例见 [API](api.md)。本文保留设计依据与历史采用记录;当前内核已包含 Radix Dialog/Toast、React Aria Calendar/MultiSelect/SortableList,具体行为见 [成熟度迁移](maturity-migration.md)。下方参考表为早期选型依据,不表示这些内核尚未集成。
6
-
7
- ## 结论
8
-
9
- 公共包采用“自己的语义主题与稳定 API + 按需引入无样式交互内核”的路线。参考成熟项目的行为和设计原则,但不复制源码,也不把完整视觉组件库叠加进来。
10
-
11
- | 参考项目 | 借鉴或使用内容 | 当前策略 |
12
- | --- | --- | --- |
13
- | [Ant Design](https://ant.design/components/overview/) | 企业后台的信息层级、即时反馈、短而克制的动效、Alert / Empty / Skeleton 等组件分层 | 作为设计基准,不新增 `antd` 依赖,避免主题、包体和 DOM 结构被绑定 |
14
- | [Radix Primitives](https://www.radix-ui.com/primitives/docs/overview/accessibility) | Dialog、Popover、Tooltip、Dropdown、Select 的焦点管理、键盘行为与组合式 API | MIT;出现第二个复杂使用场景时按单包引入,并由本包封装稳定 API |
15
- | [Floating UI](https://floating-ui.com/docs/react) | 浮层的翻转、偏移、碰撞规避、滚动和 resize 跟随 | MIT;日期、Tooltip、Popover 需要处理嵌套滚动容器时优先采用 `@floating-ui/react-dom` |
16
- | [React Aria](https://react-aria.adobe.com/getting-started) | Calendar、ComboBox、ListBox 等高复杂度控件的无障碍行为与国际化 | Apache-2.0;仅在复杂日期/搜索选择器进入公共包时评估,避免与 Radix 重复引入 |
17
-
18
- ## 2026-09-04 P0 信息与布局组件核验
19
-
20
- - [Radix Themes Badge](https://www.radix-ui.com/themes/docs/components/badge) 以 `span` 为基础并提供尺寸与色彩变体;本包保留同样轻量的原生语义,但只暴露项目已有语义令牌能稳定表达的 tone,不引入可点击、删除或业务状态行为。
21
- - [Ant Design Statistic](https://ant.design/components/statistic/) 将标题、数值、前后缀和说明分开;MetricCard 采用稳定的 label/value/description/icon 插槽,但刻意不复制格式化、精度、倒计时等业务或高频更新逻辑。
22
- - [PatternFly Card](https://www.patternfly.org/components/card/) 与 [Page Header](https://www.patternfly.org/component-groups/content-containers/page-header/) 都把标题、说明、正文和 actions 作为明确结构;SectionCard 与 PageHeader 采用相同信息层级,并在窄屏允许标题和操作区自然换行。
23
- - [W3C 页面标题与区域指南](https://www.w3.org/WAI/tutorials/page-structure/headings/) 建议标题反映内容层级,并用可见标题关联页面区域;因此 PageHeader 输出 `h1`,SectionCard 提供 2–6 级标题并自动用 `aria-labelledby` 命名 section。
24
- - [React `useMemo` 指南](https://react.dev/reference/react/useMemo) 只建议为已识别的昂贵计算添加缓存。四个组件只有常量级字符串合并和元素组合,所以不增加手工 memo;通过无内部状态、无 Effect、无测量、纯 CSS 响应式和零新增依赖保持低开销。
25
-
26
- ## 2026-09-11 分段控制器
27
-
28
- 在既有 `SegmentedControl` 上扩展浅底选中、图标、收缩宽度、纵向和胶囊外观,保留默认实色整行行为。沿用原生 radio 的分组、键盘与表单语义,使用本库主题令牌,不新增交互依赖。
29
-
30
- 少量互斥字段、时间粒度或视图模式选择使用分段控制器;开关使用 ToggleSwitch,多项选择使用 Checkbox/MultiSelect,内容页签使用 RovingTabList/RovingTabPanel。选项、业务状态、权限和数据刷新由消费方提供。
31
-
32
- 本次扩展不承诺与其他组件库 API 兼容,也不提供滑块动效、非受控状态或数字值。完整参数、默认值及接入示例统一维护在 [API](api.md#分段控制器-segmentedcontrol),消费时核对实际安装版本。
33
-
34
- ## 动效规范
35
-
36
- - 动效只解释状态变化、空间层级或操作反馈,不作为装饰。
37
- - 快速反馈使用 `--ui-motion-fast`(120ms),普通浮层使用 `--ui-motion-normal`(180ms),强层级弹窗使用 `--ui-motion-slow`(240ms)。
38
- - 进入使用后缓动,退出使用前缓动;优先只动画 `opacity` 和 `transform`。
39
- - 所有动画必须在 `prefers-reduced-motion: reduce` 下停用。
40
- - 业务项目可以覆盖时长令牌,但不应自行给同类组件建立另一套缓动曲线。
41
-
42
- ## 公共组件优先级
43
-
44
- 已纳入基础层:Badge、MetricCard、PageHeader、SectionCard、Button、Alert、EmptyState、Skeleton、Dialog、ConfirmDialog、FormField、DateInput、Pagination、RovingTabList/RovingTabPanel、ToggleSwitch、SegmentedControl、AsyncState、Tooltip、Popover、Popconfirm、Select、Combobox、AutoComplete、AsyncSearchPicker、DropdownMenu、Toast 和 DataTable。
45
-
46
- 本轮已集成:
47
-
48
- 1. Tooltip / Popover / Popconfirm:统一浮层定位、碰撞规避、延时、Escape 与焦点行为。
49
- 2. Select / Combobox / AutoComplete:统一键盘导航与移动端触控;Combobox/AutoComplete 支持异步受控输入和大列表虚拟化,AutoComplete 额外允许自由文本。
50
- 3. Toast:统一队列、停留时长、读屏播报;危险错误默认不自动消失。
51
- 4. DataTable:当前候选采用MRT内核,保留旧列/排序/选择契约并提供独立入口和高级配置;通用行合并在库内,查询、服务端分页与业务字段继续留在消费项目。见 [DataTable迁移](data-table.md)。
52
- 5. AsyncSearchPicker:只负责查询输入、显式触发、状态与候选交互;网络请求、取消和 DTO 映射由消费方持有。
53
- 6. DropdownMenu:操作集合必须使用 menu/menuitem 语义和标准键盘模型;Popover 不得冒充菜单。
54
- 7. ConfirmDialog:用于非锚点、强层级确认;锚点附近的轻量确认继续使用 Popconfirm。
55
- 8. TreeSelect:以 WAI-ARIA combobox + tree 交互为基准,支持业务方提供任意层级节点;不内置部门 DTO、权限状态或远程请求。
56
- 9. StructuredAddressInput:抽象结构化路径、路径搜索、详细地址和自由地址回退;行政区划数据、版本更新、字符串解析与领域字段定位仍属于消费方。
57
-
58
- 服务端排序、批量操作插槽与列显隐已提供,详见成熟度迁移。Notification 历史中心等后续能力仍须由真实场景驱动。
59
-
60
- ## 不进入公共包
61
-
62
- - Ant Design ProTable、ProForm 一类“组件即页面”的高层封装。
63
- - 合同审批、客户状态、ERP 字段等业务语义。
64
- - 为单项目存在的视觉特效或布局外壳。
65
- - 为同一交互重复引入多套 headless primitive,造成焦点与 Portal 规则冲突;现有混合内核由公共包统一封装。
66
-
67
- ## 消费项目门禁
68
-
69
- `business-ui-gate` 除基础按钮/输入和手写 dialog 外,也把原生 `role="menu"/"menuitem"` 计为 `handwrittenMenu`,把原生 `role="combobox"/"listbox"/"option"` 计为 `handwrittenCombobox`。消费项目应使用 DropdownMenu、Select、Combobox/AutoComplete 或 AsyncSearchPicker;确有历史实现时只能通过审核后的 baseline 暂存,不应关闭对应门禁。
70
-
71
- 门禁还会把从 `@jc-times/business-ui` 导入的 ModalShell/Dialog 中“未提供 `footer` 属性却直接放置原生 `<footer>` child”的调用计为 `legacyModalFooter`。命名别名与 namespace 导入均受支持;普通业务组件中的 footer 不计数。迁移时把固定操作区移到 `footer`,样式改用 `footerClassName`,不要扩大 baseline 掩盖新调用。
72
-
73
- ## 引入开源依赖门槛
74
-
75
- 必须同时满足:许可证允许内部商业使用;仍在维护;支持 React 18/19;可 tree-shake;SSR 安全;有键盘和读屏行为;能通过语义令牌完全接管视觉;锁定精确版本并记录升级验证。许可证文件随最终制品保留,新增依赖需在变更记录中说明。
76
-
77
- ## 2026-09-06 数字输入参考与统一尺寸契约
78
-
79
- - 对照 [Ant Design InputNumber](https://ant.design/components/input-number/) 的 stringMode 高精度字符串、formatter/parser 展示与值分离、changeOnBlur 和 focus cursor=all;本包用 NumericInput 字符串、内部草稿、onValueCommit 与明确全选配置承接这些设计原则。
80
- - 对照 [Base UI Number Field](https://base-ui.com/react/components/number-field) 的 onValueChange/onValueCommitted 分层、空值与可访问名称;本包区分草稿观察和规范化提交,沿用 FormField 标签与错误关联。当前没有增减按钮、箭头步进或拖动调值,不宣称具备完整 spinbutton 行为。
81
- - 成熟组件库作为行为/API 的评估依据;数字输入的视觉尺寸必须与本包同档控件一致。NumericInput 和 ScaledNumericInput 继续复用 TextInput,不设置独立高度、宽度、字体、padding 或圆角。
82
- - 默认输入高度共用 --ui-control-height(40px),粗指针触屏最小高度共用 --ui-touch-height(44px);TextInput、SelectInput 与数字输入均为 border-box、width:100%、水平 padding 11px、font:inherit、--ui-radius 圆角。标签/帮助/错误继续使用 FormField 的间距与排版;比较高度时以输入框本体为准,提示文本可自然增加字段总高度。
83
- - 消费方如需调整密度,统一修改公共尺寸令牌并回归同行控件;不能只对数字输入添加局部高度、字体或内边距。原生 size 属性表示字符宽度,不作为本包 small/medium/large 的视觉规格。
84
- - 验证沿用 control-size-styles.test.mjs 尺寸/触屏门禁、NumericInput.test.tsx 的公共样式与表单属性契约,以及视觉矩阵同排对照。后续出现步进/国际化需求时再评估扩展或引入交互内核。
85
-
86
- ## 2026-09-06 输入装饰参考与采用决定
87
-
88
- - 对照 Ant Design Input 的 prefix/suffix 与 allowClear、MUI InputAdornment 的 position,以及 Base UI Field/Input 的组合思路,采用 TextInput 起止 ReactNode 插槽;公共包只管理组合外框、输入焦点和共享状态,不接管搜索请求、数值格式或业务单位。
89
- - 无装饰调用保持既有原生 input DOM;有装饰调用增加稳定外框,并让 ref、className、事件和原生属性继续落在 input。非交互装饰点击聚焦输入,按钮和链接保留自身行为。
90
- - TextInput、NumericInput 与 ScaledNumericInput 使用同一组合样式:默认 40px、粗指针最小 44px、border-box、语义令牌、圆角和焦点环一致。紧凑清除按钮适配在外框内部,消费方不得用局部高度覆盖单位场景。
91
-
92
- ## 2026-09-11 Cascader 内核采用
93
-
1
+ # 公共组件参考与采用策略
2
+
3
+ 整理日期:2026-09-11
4
+
5
+ 组件使用从 [Agent 指南](agent-guide.md) 开始,参数与示例见 [API](api.md)。本文保留设计依据与历史采用记录;当前内核已包含 Radix Dialog/Toast、React Aria Calendar/MultiSelect/SortableList,具体行为见 [成熟度迁移](maturity-migration.md)。下方参考表为早期选型依据,不表示这些内核尚未集成。
6
+
7
+ ## 结论
8
+
9
+ 公共包采用“自己的语义主题与稳定 API + 按需引入无样式交互内核”的路线。参考成熟项目的行为和设计原则,但不复制源码,也不把完整视觉组件库叠加进来。
10
+
11
+ | 参考项目 | 借鉴或使用内容 | 当前策略 |
12
+ | --- | --- | --- |
13
+ | [Ant Design](https://ant.design/components/overview/) | 企业后台的信息层级、即时反馈、短而克制的动效、Alert / Empty / Skeleton 等组件分层 | 作为设计基准,不新增 `antd` 依赖,避免主题、包体和 DOM 结构被绑定 |
14
+ | [Radix Primitives](https://www.radix-ui.com/primitives/docs/overview/accessibility) | Dialog、Popover、Tooltip、Dropdown、Select 的焦点管理、键盘行为与组合式 API | MIT;出现第二个复杂使用场景时按单包引入,并由本包封装稳定 API |
15
+ | [Floating UI](https://floating-ui.com/docs/react) | 浮层的翻转、偏移、碰撞规避、滚动和 resize 跟随 | MIT;日期、Tooltip、Popover 需要处理嵌套滚动容器时优先采用 `@floating-ui/react-dom` |
16
+ | [React Aria](https://react-aria.adobe.com/getting-started) | Calendar、ComboBox、ListBox 等高复杂度控件的无障碍行为与国际化 | Apache-2.0;仅在复杂日期/搜索选择器进入公共包时评估,避免与 Radix 重复引入 |
17
+
18
+ ## 2026-09-04 P0 信息与布局组件核验
19
+
20
+ - [Radix Themes Badge](https://www.radix-ui.com/themes/docs/components/badge) 以 `span` 为基础并提供尺寸与色彩变体;本包保留同样轻量的原生语义,但只暴露项目已有语义令牌能稳定表达的 tone,不引入可点击、删除或业务状态行为。
21
+ - [Ant Design Statistic](https://ant.design/components/statistic/) 将标题、数值、前后缀和说明分开;MetricCard 采用稳定的 label/value/description/icon 插槽,但刻意不复制格式化、精度、倒计时等业务或高频更新逻辑。
22
+ - [PatternFly Card](https://www.patternfly.org/components/card/) 与 [Page Header](https://www.patternfly.org/component-groups/content-containers/page-header/) 都把标题、说明、正文和 actions 作为明确结构;SectionCard 与 PageHeader 采用相同信息层级,并在窄屏允许标题和操作区自然换行。
23
+ - [W3C 页面标题与区域指南](https://www.w3.org/WAI/tutorials/page-structure/headings/) 建议标题反映内容层级,并用可见标题关联页面区域;因此 PageHeader 输出 `h1`,SectionCard 提供 2–6 级标题并自动用 `aria-labelledby` 命名 section。
24
+ - [React `useMemo` 指南](https://react.dev/reference/react/useMemo) 只建议为已识别的昂贵计算添加缓存。四个组件只有常量级字符串合并和元素组合,所以不增加手工 memo;通过无内部状态、无 Effect、无测量、纯 CSS 响应式和零新增依赖保持低开销。
25
+
26
+ ## 2026-09-11 分段控制器
27
+
28
+ 在既有 `SegmentedControl` 上扩展浅底选中、图标、收缩宽度、纵向和胶囊外观,保留默认实色整行行为。沿用原生 radio 的分组、键盘与表单语义,使用本库主题令牌,不新增交互依赖。
29
+
30
+ 少量互斥字段、时间粒度或视图模式选择使用分段控制器;开关使用 ToggleSwitch,多项选择使用 Checkbox/MultiSelect,内容页签使用 RovingTabList/RovingTabPanel。选项、业务状态、权限和数据刷新由消费方提供。
31
+
32
+ 本次扩展不承诺与其他组件库 API 兼容,也不提供滑块动效、非受控状态或数字值。完整参数、默认值及接入示例统一维护在 [API](api.md#分段控制器-segmentedcontrol),消费时核对实际安装版本。
33
+
34
+ ## 动效规范
35
+
36
+ - 动效只解释状态变化、空间层级或操作反馈,不作为装饰。
37
+ - 快速反馈使用 `--ui-motion-fast`(120ms),普通浮层使用 `--ui-motion-normal`(180ms),强层级弹窗使用 `--ui-motion-slow`(240ms)。
38
+ - 进入使用后缓动,退出使用前缓动;优先只动画 `opacity` 和 `transform`。
39
+ - 所有动画必须在 `prefers-reduced-motion: reduce` 下停用。
40
+ - 业务项目可以覆盖时长令牌,但不应自行给同类组件建立另一套缓动曲线。
41
+
42
+ ## 公共组件优先级
43
+
44
+ 已纳入基础层:Badge、MetricCard、PageHeader、SectionCard、Button、Alert、EmptyState、Skeleton、Dialog、ConfirmDialog、FormField、DateInput、Pagination、RovingTabList/RovingTabPanel、ToggleSwitch、SegmentedControl、AsyncState、Tooltip、Popover、Popconfirm、Select、Combobox、AutoComplete、AsyncSearchPicker、DropdownMenu、Toast 和 DataTable。
45
+
46
+ 本轮已集成:
47
+
48
+ 1. Tooltip / Popover / Popconfirm:统一浮层定位、碰撞规避、延时、Escape 与焦点行为。
49
+ 2. Select / Combobox / AutoComplete:统一键盘导航与移动端触控;Combobox/AutoComplete 支持异步受控输入和大列表虚拟化,AutoComplete 额外允许自由文本。
50
+ 3. Toast:统一队列、停留时长、读屏播报;危险错误默认不自动消失。
51
+ 4. DataTable:当前候选采用MRT内核,保留旧列/排序/选择契约并提供独立入口和高级配置;通用行合并在库内,查询、服务端分页与业务字段继续留在消费项目。见 [DataTable迁移](data-table.md)。
52
+ 5. AsyncSearchPicker:只负责查询输入、显式触发、状态与候选交互;网络请求、取消和 DTO 映射由消费方持有。
53
+ 6. DropdownMenu:操作集合必须使用 menu/menuitem 语义和标准键盘模型;Popover 不得冒充菜单。
54
+ 7. ConfirmDialog:用于非锚点、强层级确认;锚点附近的轻量确认继续使用 Popconfirm。
55
+ 8. TreeSelect:以 WAI-ARIA combobox + tree 交互为基准,支持业务方提供任意层级节点;不内置部门 DTO、权限状态或远程请求。
56
+ 9. StructuredAddressInput:抽象结构化路径、路径搜索、详细地址和自由地址回退;行政区划数据、版本更新、字符串解析与领域字段定位仍属于消费方。
57
+
58
+ 服务端排序、批量操作插槽与列显隐已提供,详见成熟度迁移。Notification 历史中心等后续能力仍须由真实场景驱动。
59
+
60
+ ## 不进入公共包
61
+
62
+ - Ant Design ProTable、ProForm 一类“组件即页面”的高层封装。
63
+ - 合同审批、客户状态、ERP 字段等业务语义。
64
+ - 为单项目存在的视觉特效或布局外壳。
65
+ - 为同一交互重复引入多套 headless primitive,造成焦点与 Portal 规则冲突;现有混合内核由公共包统一封装。
66
+
67
+ ## 消费项目门禁
68
+
69
+ `business-ui-gate` 除基础按钮/输入和手写 dialog 外,也把原生 `role="menu"/"menuitem"` 计为 `handwrittenMenu`,把原生 `role="combobox"/"listbox"/"option"` 计为 `handwrittenCombobox`。消费项目应使用 DropdownMenu、Select、Combobox/AutoComplete 或 AsyncSearchPicker;确有历史实现时只能通过审核后的 baseline 暂存,不应关闭对应门禁。
70
+
71
+ 门禁还会把从 `@jc-times/business-ui` 导入的 ModalShell/Dialog 中“未提供 `footer` 属性却直接放置原生 `<footer>` child”的调用计为 `legacyModalFooter`。命名别名与 namespace 导入均受支持;普通业务组件中的 footer 不计数。迁移时把固定操作区移到 `footer`,样式改用 `footerClassName`,不要扩大 baseline 掩盖新调用。
72
+
73
+ ## 引入开源依赖门槛
74
+
75
+ 必须同时满足:许可证允许内部商业使用;仍在维护;支持 React 18/19;可 tree-shake;SSR 安全;有键盘和读屏行为;能通过语义令牌完全接管视觉;锁定精确版本并记录升级验证。许可证文件随最终制品保留,新增依赖需在变更记录中说明。
76
+
77
+ ## 2026-09-06 数字输入参考与统一尺寸契约
78
+
79
+ - 对照 [Ant Design InputNumber](https://ant.design/components/input-number/) 的 stringMode 高精度字符串、formatter/parser 展示与值分离、changeOnBlur 和 focus cursor=all;本包用 NumericInput 字符串、内部草稿、onValueCommit 与明确全选配置承接这些设计原则。
80
+ - 对照 [Base UI Number Field](https://base-ui.com/react/components/number-field) 的 onValueChange/onValueCommitted 分层、空值与可访问名称;本包区分草稿观察和规范化提交,沿用 FormField 标签与错误关联。当前没有增减按钮、箭头步进或拖动调值,不宣称具备完整 spinbutton 行为。
81
+ - 成熟组件库作为行为/API 的评估依据;数字输入的视觉尺寸必须与本包同档控件一致。NumericInput 和 ScaledNumericInput 继续复用 TextInput,不设置独立高度、宽度、字体、padding 或圆角。
82
+ - 默认输入高度共用 --ui-control-height(40px),粗指针触屏最小高度共用 --ui-touch-height(44px);TextInput、SelectInput 与数字输入均为 border-box、width:100%、水平 padding 11px、font:inherit、--ui-radius 圆角。标签/帮助/错误继续使用 FormField 的间距与排版;比较高度时以输入框本体为准,提示文本可自然增加字段总高度。
83
+ - 消费方如需调整密度,统一修改公共尺寸令牌并回归同行控件;不能只对数字输入添加局部高度、字体或内边距。原生 size 属性表示字符宽度,不作为本包 small/medium/large 的视觉规格。
84
+ - 验证沿用 control-size-styles.test.mjs 尺寸/触屏门禁、NumericInput.test.tsx 的公共样式与表单属性契约,以及视觉矩阵同排对照。后续出现步进/国际化需求时再评估扩展或引入交互内核。
85
+
86
+ ## 2026-09-06 输入装饰参考与采用决定
87
+
88
+ - 对照 Ant Design Input 的 prefix/suffix 与 allowClear、MUI InputAdornment 的 position,以及 Base UI Field/Input 的组合思路,采用 TextInput 起止 ReactNode 插槽;公共包只管理组合外框、输入焦点和共享状态,不接管搜索请求、数值格式或业务单位。
89
+ - 无装饰调用保持既有原生 input DOM;有装饰调用增加稳定外框,并让 ref、className、事件和原生属性继续落在 input。非交互装饰点击聚焦输入,按钮和链接保留自身行为。
90
+ - TextInput、NumericInput 与 ScaledNumericInput 使用同一组合样式:默认 40px、粗指针最小 44px、border-box、语义令牌、圆角和焦点环一致。紧凑清除按钮适配在外框内部,消费方不得用局部高度覆盖单位场景。
91
+
92
+ ## 2026-09-11 Cascader 内核采用
93
+
94
94
  新增固定 @rc-component/cascader@1.25.0(MIT),公共包装提供业务主题、表单元信息、空值归一、最近弹窗 portal、窄屏底部面板与默认独立多选。StructuredAddressInput 复用通用组件;TreeSelect 保留兼容。无 antd 依赖;依赖外置,发行包保留 RC 许可证。当前为本地实现,消费接入与发布独立验证。
@@ -1,127 +1,127 @@
1
- # DataTable:MRT 公共表格与迁移
2
-
3
- 0.2.31采用MRT替代原表格渲染内核;消费服务需单独升级验收。实际安装版本必须包含本文列出的导出;不能用旧版本直接导入新增入口。
4
-
5
- ## 导入与按需加载
6
-
7
- ```tsx
8
- import { DataTable } from '@jc-times/business-ui/data-table';
9
- import '@jc-times/business-ui/styles.css';
10
- ```
11
-
12
- 主入口仍导出DataTable以兼容旧调用。独立 `data-table` 入口导出DataTable、useDataTable、DataTableView、DataTableTheme、DataTableMergedRows及相关类型。组件库采用ESM并将MRT/MUI作为外部依赖交给业务构建器处理;这些依赖已列为正式dependencies。
13
-
14
- 页面懒加载仍由消费服务负责。推荐把完整列表页面或业务表格包装作为动态import边界;仅换成独立入口不等于浏览器自动延迟加载。样式仍为共享CSS。构建验证已确认仅导入Button的消费产物不包含MRT/MUI实现,但使用DataTable的页面必然需要表格依赖。
15
-
16
- ## 旧参数兼容
17
-
18
- ```tsx
19
- <DataTable rows={rows} columns={columns} getRowId={row => row.id}
20
- selectedRowIds={selectedIds} onSelectedRowIdsChange={setSelectedIds}/>
21
- ```
22
-
23
- 旧DataTableProps和DataTableColumn保留:columns使用id/header/cell,排序使用sortValue或服务端sortable;sort/defaultSort/onSortChange、跨页选择、禁选、hiddenColumnIds、加载/空态、caption、ref与原生属性保持兼容。mobileLayout仍支持scroll/cards。
24
-
25
- 此入口已通过MRT_Table渲染表格,兼容层保留旧排序语义及memo行渲染。为避免旧页面行为改变,不自动增加工具栏、筛选或分页;也不自动把旧cell返回的ReactNode当作可搜索原始值。需要完整MRT交互时改用下述options入口。
26
-
27
- ## 完整表格配置
28
-
29
- ```tsx
30
- import { DataTable, type DataTableOptions } from '@jc-times/business-ui/data-table';
31
- type Order = { id: string; customer: string; amount: number };
32
-
33
- const options: DataTableOptions<Order> = {
34
- data: orders,
35
- columns: [
36
- { accessorKey: 'id', header: '编号' },
37
- { accessorKey: 'customer', header: '客户' },
38
- { accessorKey: 'amount', header: '金额' },
39
- ],
40
- getRowId: row => row.id,
41
- enableColumnOrdering: true,
42
- enableColumnResizing: true,
43
- enableColumnPinning: true,
44
- enableRowSelection: true,
45
- };
46
- <DataTable options={options}/>;
47
- ```
48
-
49
- 表格列较多且页面本身纵向滚动较长时,可显式开启底部吸附横向滚动条。它仅在内容确实超出表格容器时显示,并与 MRT 原生滚动容器双向同步;固定列仍通过 MRT 的 `columnPinning` 配置:
50
-
51
- ```tsx
52
- <DataTable options={options} stickyHorizontalScrollbar
53
- horizontalScrollbarLabel="订单表横向滚动" />
54
- ```
55
-
56
- `horizontalScrollbarLabel` 应描述当前业务表格,供键盘和辅助技术用户辨认。该能力同样可传给 `DataTableView`;默认关闭,以避免改变既有页面的滚动布局。
57
-
58
- options与旧rows/columns参数是两种互斥调用形式,不要混传。DataTableOptions、DataTableColumnDef、DataTableInstance分别对应MRT的options/column/instance类型;目前公开此底层契约,没有假称跨组件库兼容。默认中文文案和公共Lucide图标,可由options覆盖。
59
-
60
- 需要从工具栏、列设置等多个位置操作实例时:
61
-
62
- ```tsx
63
- const table = useDataTable(options); // 在React组件内部调用
64
- return <DataTableView table={table}/>;
65
- ```
66
-
67
- DataTableView与DataTable的配置入口共用MRT渲染和DataTableTheme;主题使用--ui-*,嵌套浮层继承OverlayContainer。独立放在表格外的MRT工具栏控件可用DataTableTheme包裹。ModalShell中的Escape层级仍由业务组合协调,参考合同样例;公共表格不会替业务关闭弹窗。
68
-
69
- ## 合并行与展开明细
70
-
71
- 模式由业务服务选择,不固化合同/产品字段,也不要求最终用户看到切换器。
72
-
73
- - 展开:options.renderDetailPanel渲染调用方提供的明细,沿用MRT展开状态。
74
- - 合并:options使用layoutMode='semantic'、enableRowVirtualization=false,在muiTableBodyProps中调用DataTableMergedRows。按主记录分页,每条明细生成真实tr,其他可见列使用rowSpan跨行。
75
-
76
- ```tsx
77
- const table = useDataTable({
78
- ...options,
79
- layoutMode: 'semantic',
80
- enableRowVirtualization: false,
81
- muiTableBodyProps: ({ table }) => ({ children:
82
- <DataTableMergedRows table={table}
83
- getDetails={row => row.lines}
84
- detailColumnIds={['product', 'quantity']}
85
- getDetailValue={(line, id) => id === 'product' ? line?.name ?? '暂无产品' : line?.quantity ?? '—'}/>
86
- }),
87
- });
88
- ```
89
-
90
- 上述示意要求业务Row包含lines,并在columns定义product/quantity这两个明细列。getDetails返回只读数组,getDetailValue接受空明细null。主行用getRowId稳定标识;列的排序、筛选应基于主记录定义,明细列应关闭没有明确语义的排序/筛选。隐藏所有明细列时每主记录一行。合并不支持跨主记录、任意colSpan、分组/树形/行固定组合;不与虚拟化同时使用。
91
-
92
- 合同样例已通过公共DataTableMergedRows生成合并行;合同字段、金额区间弹层、搜索记忆及列设置布局仍由业务包装组合,详见 [合同样例](mrt-poc.md)。这些业务特定能力不会作为DataTable旧参数静默启用。
93
-
94
- ## 迁移与验收
95
-
96
- 1. 旧页面可保留DataTable旧参数,先验证布局、排序、选择及加载状态。
97
- 2. 复杂页面改用options或useDataTable/DataTableView,明确服务端查询和分页,不同时使用旧排序参数。
98
- 3. 为重型页面设置动态import边界,验证仅使用按钮的页面不含MRT/MUI代码。
99
- 4. 长页面可启用stickyHorizontalScrollbar;确认无溢出时隐藏,拖动吸附滚动条和原生滚动区时位置同步。
100
- 5. 合并模式按主记录分页;虚拟化只减少DOM,不减少全量数据筛选开销。
101
- 6. 发布新版本后,消费服务精确升级并验证真实接口、权限、用户隔离和设备;本地完成不等于生产已替换。
102
-
103
- 不修改业务数据,不自动迁移所有服务,不提供旧包版本的新能力。MRT3.2.1和MUI7.3.11及配套依赖已固定在package.json/锁文件中。
104
-
105
- ## 按需打包回归与项目管理复验(2026-09-12)
106
-
107
- 构建现使用preserveModules保留组件边界。原合并产物中工厂调用等初始化代码会阻止整段未用组件被删除;仅保留ESM导出和sideEffects声明不足以确保合并模块内部纯度。修复后消费构建可跳过完整未使用模块,不在业务端改写包代码。
108
-
109
- `pnpm test:tree-shaking` 在 `pnpm build` 后对真实dist产物构建Button、TextInput、DateInput、DataTable四个消费者。前两者必须没有日期/MRT/MUI实现;后两者为正向对照。该检查已纳入pnpm verify。门禁统计传递模块,不仅检查包文件名或入口长度。
110
-
111
- 读取项目管理0.1.103当前工作树(依赖正式business-ui0.2.30),以相同Vite配置、同一业务入口构建隔离产物,未修改其package/lockfile/源码、未写其dist:
112
-
113
- | 入口静态依赖闭包 | 正式0.2.30 | 本地候选 |
114
- | --- | ---: | ---: |
115
- | JS gzip | 178.51 KiB | 83.31 KiB |
116
- | CSS gzip | 17.17 KiB | 17.17 KiB |
117
- | 日期组件/Calendar模块 | 17 | 0 |
118
- | MRT模块 | 0 | 0 |
119
-
120
- 证据在本库忽略产物 `artifacts/project-ui-check/results.json`。这是候选dist别名复验,不是消费服务正式安装、完整验收或部署。项目门禁JS80 KiB、CSS14.5 KiB仍失败;不得提高预算或把日期泄漏修复记作整项发布阻塞解除。剩余首屏代码与共享CSS需另行按真实使用拆分。MRT替代带来的路由体积也需在消费方完整门禁中验收。
121
-
122
- 公共库136项测试、14项MRT浏览器回归、类型/公共库/矩阵/样例构建和按需打包门禁通过。本地候选已可打包;对应公共库版本为0.2.31,不能覆盖已经发布的0.2.30。
123
-
124
-
125
- ## 0.2.47 横向滚动性能
126
-
127
- 同步条忽略程序写入产生的异步回传,每帧最多镜像一次滚动位置,保留浏览器原生滚动惯性;卸载取消待执行帧。对于带固定高度的业务列表,优先通过 MRT 的 `muiTableContainerProps.sx.maxHeight` 和 `enableStickyHeader` 使用容器原生滚动条,避免额外吸附条覆盖长表格数据行。
1
+ # DataTable:MRT 公共表格与迁移
2
+
3
+ 0.2.31采用MRT替代原表格渲染内核;消费服务需单独升级验收。实际安装版本必须包含本文列出的导出;不能用旧版本直接导入新增入口。
4
+
5
+ ## 导入与按需加载
6
+
7
+ ```tsx
8
+ import { DataTable } from '@jc-times/business-ui/data-table';
9
+ import '@jc-times/business-ui/styles.css';
10
+ ```
11
+
12
+ 主入口仍导出DataTable以兼容旧调用。独立 `data-table` 入口导出DataTable、useDataTable、DataTableView、DataTableTheme、DataTableMergedRows及相关类型。组件库采用ESM并将MRT/MUI作为外部依赖交给业务构建器处理;这些依赖已列为正式dependencies。
13
+
14
+ 页面懒加载仍由消费服务负责。推荐把完整列表页面或业务表格包装作为动态import边界;仅换成独立入口不等于浏览器自动延迟加载。样式仍为共享CSS。构建验证已确认仅导入Button的消费产物不包含MRT/MUI实现,但使用DataTable的页面必然需要表格依赖。
15
+
16
+ ## 旧参数兼容
17
+
18
+ ```tsx
19
+ <DataTable rows={rows} columns={columns} getRowId={row => row.id}
20
+ selectedRowIds={selectedIds} onSelectedRowIdsChange={setSelectedIds}/>
21
+ ```
22
+
23
+ 旧DataTableProps和DataTableColumn保留:columns使用id/header/cell,排序使用sortValue或服务端sortable;sort/defaultSort/onSortChange、跨页选择、禁选、hiddenColumnIds、加载/空态、caption、ref与原生属性保持兼容。mobileLayout仍支持scroll/cards。
24
+
25
+ 此入口已通过MRT_Table渲染表格,兼容层保留旧排序语义及memo行渲染。为避免旧页面行为改变,不自动增加工具栏、筛选或分页;也不自动把旧cell返回的ReactNode当作可搜索原始值。需要完整MRT交互时改用下述options入口。
26
+
27
+ ## 完整表格配置
28
+
29
+ ```tsx
30
+ import { DataTable, type DataTableOptions } from '@jc-times/business-ui/data-table';
31
+ type Order = { id: string; customer: string; amount: number };
32
+
33
+ const options: DataTableOptions<Order> = {
34
+ data: orders,
35
+ columns: [
36
+ { accessorKey: 'id', header: '编号' },
37
+ { accessorKey: 'customer', header: '客户' },
38
+ { accessorKey: 'amount', header: '金额' },
39
+ ],
40
+ getRowId: row => row.id,
41
+ enableColumnOrdering: true,
42
+ enableColumnResizing: true,
43
+ enableColumnPinning: true,
44
+ enableRowSelection: true,
45
+ };
46
+ <DataTable options={options}/>;
47
+ ```
48
+
49
+ 表格列较多且页面本身纵向滚动较长时,可显式开启底部吸附横向滚动条。它仅在内容确实超出表格容器时显示,并与 MRT 原生滚动容器双向同步;固定列仍通过 MRT 的 `columnPinning` 配置:
50
+
51
+ ```tsx
52
+ <DataTable options={options} stickyHorizontalScrollbar
53
+ horizontalScrollbarLabel="订单表横向滚动" />
54
+ ```
55
+
56
+ `horizontalScrollbarLabel` 应描述当前业务表格,供键盘和辅助技术用户辨认。该能力同样可传给 `DataTableView`;默认关闭,以避免改变既有页面的滚动布局。
57
+
58
+ options与旧rows/columns参数是两种互斥调用形式,不要混传。DataTableOptions、DataTableColumnDef、DataTableInstance分别对应MRT的options/column/instance类型;目前公开此底层契约,没有假称跨组件库兼容。默认中文文案和公共Lucide图标,可由options覆盖。
59
+
60
+ 需要从工具栏、列设置等多个位置操作实例时:
61
+
62
+ ```tsx
63
+ const table = useDataTable(options); // 在React组件内部调用
64
+ return <DataTableView table={table}/>;
65
+ ```
66
+
67
+ DataTableView与DataTable的配置入口共用MRT渲染和DataTableTheme;主题使用--ui-*,嵌套浮层继承OverlayContainer。独立放在表格外的MRT工具栏控件可用DataTableTheme包裹。ModalShell中的Escape层级仍由业务组合协调,参考合同样例;公共表格不会替业务关闭弹窗。
68
+
69
+ ## 合并行与展开明细
70
+
71
+ 模式由业务服务选择,不固化合同/产品字段,也不要求最终用户看到切换器。
72
+
73
+ - 展开:options.renderDetailPanel渲染调用方提供的明细,沿用MRT展开状态。
74
+ - 合并:options使用layoutMode='semantic'、enableRowVirtualization=false,在muiTableBodyProps中调用DataTableMergedRows。按主记录分页,每条明细生成真实tr,其他可见列使用rowSpan跨行。
75
+
76
+ ```tsx
77
+ const table = useDataTable({
78
+ ...options,
79
+ layoutMode: 'semantic',
80
+ enableRowVirtualization: false,
81
+ muiTableBodyProps: ({ table }) => ({ children:
82
+ <DataTableMergedRows table={table}
83
+ getDetails={row => row.lines}
84
+ detailColumnIds={['product', 'quantity']}
85
+ getDetailValue={(line, id) => id === 'product' ? line?.name ?? '暂无产品' : line?.quantity ?? '—'}/>
86
+ }),
87
+ });
88
+ ```
89
+
90
+ 上述示意要求业务Row包含lines,并在columns定义product/quantity这两个明细列。getDetails返回只读数组,getDetailValue接受空明细null。主行用getRowId稳定标识;列的排序、筛选应基于主记录定义,明细列应关闭没有明确语义的排序/筛选。隐藏所有明细列时每主记录一行。合并不支持跨主记录、任意colSpan、分组/树形/行固定组合;不与虚拟化同时使用。
91
+
92
+ 合同样例已通过公共DataTableMergedRows生成合并行;合同字段、金额区间弹层、搜索记忆及列设置布局仍由业务包装组合,详见 [合同样例](mrt-poc.md)。这些业务特定能力不会作为DataTable旧参数静默启用。
93
+
94
+ ## 迁移与验收
95
+
96
+ 1. 旧页面可保留DataTable旧参数,先验证布局、排序、选择及加载状态。
97
+ 2. 复杂页面改用options或useDataTable/DataTableView,明确服务端查询和分页,不同时使用旧排序参数。
98
+ 3. 为重型页面设置动态import边界,验证仅使用按钮的页面不含MRT/MUI代码。
99
+ 4. 长页面可启用stickyHorizontalScrollbar;确认无溢出时隐藏,拖动吸附滚动条和原生滚动区时位置同步。
100
+ 5. 合并模式按主记录分页;虚拟化只减少DOM,不减少全量数据筛选开销。
101
+ 6. 发布新版本后,消费服务精确升级并验证真实接口、权限、用户隔离和设备;本地完成不等于生产已替换。
102
+
103
+ 不修改业务数据,不自动迁移所有服务,不提供旧包版本的新能力。MRT3.2.1和MUI7.3.11及配套依赖已固定在package.json/锁文件中。
104
+
105
+ ## 按需打包回归与项目管理复验(2026-09-12)
106
+
107
+ 构建现使用preserveModules保留组件边界。原合并产物中工厂调用等初始化代码会阻止整段未用组件被删除;仅保留ESM导出和sideEffects声明不足以确保合并模块内部纯度。修复后消费构建可跳过完整未使用模块,不在业务端改写包代码。
108
+
109
+ `pnpm test:tree-shaking` 在 `pnpm build` 后对真实dist产物构建Button、TextInput、DateInput、DataTable四个消费者。前两者必须没有日期/MRT/MUI实现;后两者为正向对照。该检查已纳入pnpm verify。门禁统计传递模块,不仅检查包文件名或入口长度。
110
+
111
+ 读取项目管理0.1.103当前工作树(依赖正式business-ui0.2.30),以相同Vite配置、同一业务入口构建隔离产物,未修改其package/lockfile/源码、未写其dist:
112
+
113
+ | 入口静态依赖闭包 | 正式0.2.30 | 本地候选 |
114
+ | --- | ---: | ---: |
115
+ | JS gzip | 178.51 KiB | 83.31 KiB |
116
+ | CSS gzip | 17.17 KiB | 17.17 KiB |
117
+ | 日期组件/Calendar模块 | 17 | 0 |
118
+ | MRT模块 | 0 | 0 |
119
+
120
+ 证据在本库忽略产物 `artifacts/project-ui-check/results.json`。这是候选dist别名复验,不是消费服务正式安装、完整验收或部署。项目门禁JS80 KiB、CSS14.5 KiB仍失败;不得提高预算或把日期泄漏修复记作整项发布阻塞解除。剩余首屏代码与共享CSS需另行按真实使用拆分。MRT替代带来的路由体积也需在消费方完整门禁中验收。
121
+
122
+ 公共库136项测试、14项MRT浏览器回归、类型/公共库/矩阵/样例构建和按需打包门禁通过。本地候选已可打包;对应公共库版本为0.2.31,不能覆盖已经发布的0.2.30。
123
+
124
+
125
+ ## 0.2.47 横向滚动性能
126
+
127
+ 同步条忽略程序写入产生的异步回传,每帧最多镜像一次滚动位置,保留浏览器原生滚动惯性;卸载取消待执行帧。对于带固定高度的业务列表,优先通过 MRT 的 `muiTableContainerProps.sx.maxHeight` 和 `enableStickyHeader` 使用容器原生滚动条,避免额外吸附条覆盖长表格数据行。