@done-coding/admin-core 0.27.0 → 0.27.1-alpha.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.
Files changed (134) hide show
  1. package/es/bridge/index.mjs +11 -3
  2. package/es/components/app-layout/AppBody.vue.mjs +1 -1
  3. package/es/components/app-layout/AppBody.vue2.mjs +14 -5
  4. package/es/components/app-layout/AppHeader.vue.mjs +1 -1
  5. package/es/components/app-layout/AppHeader.vue2.mjs +7 -5
  6. package/es/components/app-layout/AppLayout.vue.mjs +1 -1
  7. package/es/components/app-layout/AppLayout.vue2.mjs +2 -1
  8. package/es/components/app-layout/AppPage.vue.mjs +1 -1
  9. package/es/components/app-layout/AppPage.vue2.mjs +35 -32
  10. package/es/components/app-layout/app-page-geometry.mjs +16 -16
  11. package/es/components/app-layout/app-page-recommended.mjs +10 -0
  12. package/es/components/data-view/DataGridView.vue.mjs +1 -1
  13. package/es/components/data-view/DataGridView.vue2.mjs +29 -17
  14. package/es/components/data-view/DataListView.vue.mjs +1 -1
  15. package/es/components/data-view/DataListView.vue2.mjs +10 -0
  16. package/es/components/data-view/InfiniteListView.vue.mjs +1 -1
  17. package/es/components/data-view/InfiniteListView.vue2.mjs +9 -3
  18. package/es/components/data-view/use-active-scroll.mjs +15 -0
  19. package/es/components/display/DragFloat.vue.mjs +7 -0
  20. package/es/components/display/DragFloat.vue2.mjs +109 -0
  21. package/es/components/display/TabsHeader.vue.mjs +1 -1
  22. package/es/components/display/TabsHeader.vue2.mjs +9 -10
  23. package/es/components/display/TabsMain.vue.mjs +22 -7
  24. package/es/components/display/tabs-variant.mjs +14 -0
  25. package/es/components/form/FormDivider.vue.mjs +7 -0
  26. package/es/components/form/FormDivider.vue2.mjs +14 -0
  27. package/es/components/form/FormGroupTitle.vue.mjs +7 -0
  28. package/es/components/form/FormGroupTitle.vue2.mjs +59 -0
  29. package/es/components/form/FormItemNestForm.vue.mjs +1 -1
  30. package/es/components/form/FormItemNestFormList.vue.mjs +1 -1
  31. package/es/components/form/FormMain.vue.mjs +1 -1
  32. package/es/components/form/FormMain.vue2.mjs +69 -9
  33. package/es/components/form/FormSearch.vue.mjs +1 -1
  34. package/es/components/form/FormSearch.vue2.mjs +15 -3
  35. package/es/components/form/FormSubmitPanel.vue.mjs +1 -1
  36. package/es/components/form/FormSubmitPanel.vue2.mjs +14 -1
  37. package/es/components/form/ai-schema.mjs +139 -0
  38. package/es/components/form/group.mjs +81 -0
  39. package/es/components/form/use-ai-fill.mjs +71 -0
  40. package/es/components/form/utils.mjs +15 -4
  41. package/es/components/list-layout/ListLayout.vue.mjs +1 -1
  42. package/es/components/list-layout/ListLayout.vue2.mjs +103 -62
  43. package/es/components/modal/ImagePreviewTrigger.vue.mjs +5 -1
  44. package/es/components/modal/ModalConfirm.vue.mjs +1 -1
  45. package/es/components/modal/ModalConfirm.vue2.mjs +8 -5
  46. package/es/components/modal/VideoPreviewTrigger.vue.mjs +1 -1
  47. package/es/components/modal/VideoPreviewTrigger.vue2.mjs +2 -1
  48. package/es/components/page-layout/AppPageDetail.vue.mjs +1 -1
  49. package/es/components/page-layout/AppPageDetail.vue2.mjs +17 -13
  50. package/es/components/page-layout/AppPageListDetailLayout.vue.mjs +7 -4
  51. package/es/components/page-layout/AppPageListDetailSheet.vue.mjs +12 -4
  52. package/es/components/page-layout/AppPageListDetailSplit.vue.mjs +1 -1
  53. package/es/components/page-layout/AppPageListDetailSplit.vue2.mjs +10 -3
  54. package/es/components/table/TableMain.vue.mjs +1 -1
  55. package/es/components/table/TableMain.vue2.mjs +45 -25
  56. package/es/components/view-layout/ViewLayout.vue.mjs +1 -1
  57. package/es/components/view-layout/ViewLayout.vue2.mjs +46 -15
  58. package/es/config/slot-region.mjs +1 -0
  59. package/es/hooks/use-active-record.mjs +5 -1
  60. package/es/hooks/use-surface.mjs +27 -0
  61. package/es/hooks/use-theme-apply.mjs +52 -18
  62. package/es/index.mjs +81 -59
  63. package/es/inject/key.mjs +4 -0
  64. package/es/store/app.mjs +51 -17
  65. package/es/style.css +361 -184
  66. package/es/utils/dom.mjs +7 -1
  67. package/es/utils/gap-scale.mjs +25 -0
  68. package/es/utils/theme-scale.mjs +27 -0
  69. package/package.json +2 -2
  70. package/src/bridge/docs/README.md +30 -0
  71. package/src/components/app-layout/docs/README-AppBody.md +21 -0
  72. package/src/components/app-layout/docs/README-AppPage.md +20 -3
  73. package/src/components/data-view/docs/README-DataGridView.md +15 -0
  74. package/src/components/data-view/docs/README-DataListView.md +13 -0
  75. package/src/components/data-view/docs/README-InfiniteListView.md +15 -0
  76. package/src/components/display/README.md +1 -0
  77. package/src/components/display/docs/README-DragFloat.md +68 -0
  78. package/src/components/form/README.md +4 -2
  79. package/src/components/form/docs/README-FormItemNestForm.md +5 -3
  80. package/src/components/form/docs/README-FormItemNestFormList.md +7 -1
  81. package/src/components/form/docs/README-FormMain.md +102 -1
  82. package/src/components/form/docs/README-FormSearch.md +5 -0
  83. package/src/components/list-layout/docs/README-ListLayout.md +61 -1
  84. package/src/components/modal/docs/README-ImagePreviewTrigger.md +2 -0
  85. package/src/components/modal/docs/README-VideoPreviewTrigger.md +4 -1
  86. package/src/components/page-layout/docs/README-AppPageDetail.md +11 -1
  87. package/src/components/page-layout/docs/README-AppPageListDetailLayout.md +64 -6
  88. package/src/components/table/docs/README-TableMain.md +22 -0
  89. package/src/components/view-layout/docs/README-ViewLayout.md +27 -2
  90. package/src/hooks/docs/README.md +30 -1
  91. package/types/bridge/index.d.ts +69 -10
  92. package/types/components/app-layout/AppPage.vue.d.ts +15 -7
  93. package/types/components/app-layout/app-page-geometry.d.ts +19 -10
  94. package/types/components/app-layout/app-page-recommended.d.ts +12 -0
  95. package/types/components/app-layout/index.d.ts +1 -0
  96. package/types/components/data-view/types.d.ts +18 -0
  97. package/types/components/data-view/use-active-scroll.d.ts +30 -0
  98. package/types/components/display/BadgeMark.vue.d.ts +2 -2
  99. package/types/components/display/DragFloat.vue.d.ts +35 -0
  100. package/types/components/display/TabsHeader.vue.d.ts +7 -0
  101. package/types/components/display/index.d.ts +8 -1
  102. package/types/components/display/tabs-variant.d.ts +17 -0
  103. package/types/components/display/types.d.ts +6 -0
  104. package/types/components/form/FormDivider.vue.d.ts +2 -0
  105. package/types/components/form/FormGroupTitle.vue.d.ts +22 -0
  106. package/types/components/form/FormItemNestForm.vue.d.ts +2 -0
  107. package/types/components/form/ai-schema.d.ts +24 -0
  108. package/types/components/form/group.d.ts +36 -0
  109. package/types/components/form/index.d.ts +2 -0
  110. package/types/components/form/types.d.ts +94 -3
  111. package/types/components/form/use-ai-fill.d.ts +59 -0
  112. package/types/components/form/utils.d.ts +19 -0
  113. package/types/components/list-layout/ListLayout.vue.d.ts +3 -0
  114. package/types/components/list-layout/types.d.ts +28 -0
  115. package/types/components/modal/ModalConfirm.vue.d.ts +1 -1
  116. package/types/components/modal/VideoPreviewTrigger.vue.d.ts +1 -1
  117. package/types/components/page-layout/AppPageDetail.vue.d.ts +2 -2
  118. package/types/components/page-layout/AppPageListDetailLayout.vue.d.ts +3 -7
  119. package/types/components/page-layout/AppPageListDetailSheet.vue.d.ts +4 -8
  120. package/types/components/page-layout/AppPageListDetailSplit.vue.d.ts +4 -8
  121. package/types/components/page-layout/types.d.ts +42 -0
  122. package/types/components/panel/PanelItemNestForm.vue.d.ts +2 -0
  123. package/types/components/table/types.d.ts +9 -0
  124. package/types/components/view-layout/ViewLayout.vue.d.ts +18 -0
  125. package/types/components/view-layout/types.d.ts +15 -0
  126. package/types/config/slot-region.d.ts +10 -1
  127. package/types/hooks/use-surface.d.ts +34 -0
  128. package/types/inject/key.d.ts +10 -0
  129. package/types/injectInfo.json.d.ts +1 -1
  130. package/types/store/app.d.ts +3 -0
  131. package/types/utils/dom.d.ts +31 -0
  132. package/types/utils/gap-scale.d.ts +46 -0
  133. package/types/utils/index.d.ts +1 -0
  134. package/types/utils/theme-scale.d.ts +33 -0
@@ -58,6 +58,29 @@ const { listPageConfig } = useSpaceConfig({
58
58
  - **`showSwitchView`(双视图切换):默认关(表格单一视图)。** 仅当需要「列表 / 卡片」双视图切换才开(配 `#custom-view-item` 卡片渲染)。
59
59
  - **`isAutoRefresh` / `refreshInterval`:默认关(不自动刷新)。** 仅当列表需轮询刷新(任务态实时变化等)才开。
60
60
  - **高度链 prop(`refine` / `viewportHeight` / `parentChannel`):默认 ListLayout 内部已接管**(refine 默认 true)——常规无需干预,仅特殊高度场景覆写。
61
+ - **两卡模型:默认已分配好,不用配。** 灰底上**两块白卡**:
62
+ ① **搜索 + 操作**(合并为一块,中间只有分段档,无缝)② **数据区**(toolbar + 表体 + 分页同属一块)。
63
+ **header 槽恒透明**(页面标题带,与正文一样露底)。
64
+ - 🔴 面**只有底色 + 呼吸 + 圆角,没有描边、没有投影** —— 层级由面/底一档色差表达。
65
+ [MUST NOT] 补描边或投影,也 [MUST NOT] 改用 `ElCard` 包(见下方反模式)。
66
+ - **为什么数据区也成面**:EP 表体自带白底(`--el-table-bg-color` = `--el-fill-color-blank`,
67
+ core 未接管)。壳不成面时,白表体浮在灰底上、而它上方的 toolbar 与下方的分页悬空在灰底
68
+ ⇒ 视觉断层。给壳上面色后三者收进同一块白面。
69
+ - **`gap`(间距基数):默认跟随布局 gap 体系,不用配。** 缺省取
70
+ `bridge.APP_LAYOUT_GAP_CONFIG.size`(核内默认 8);取不到 bridge(脱离 AppLayout 挂载)退 8。
71
+ 它是**一个基数派生四档**(`utils/gap-scale.ts`):
72
+
73
+ | 档 | 倍率 | 默认值 | 用在哪 |
74
+ |---|---|---|---|
75
+ | 外圈 | ×2 | 16 | 内容 ↔ chrome / viewport 边(由 `AppBody` 给,不在本组件) |
76
+ | 呼吸 | ×1.5 | 12 | 卡片内边距 |
77
+ | 模块 | ×1 | 8 | 卡 ↔ 卡 |
78
+ | 分段 | ×0.5 | 4 | 合并卡内「搜索段 ↔ 操作段」 |
79
+
80
+ **改基数 = 四档按比例同步缩放**(单旋钮);基数还随密度档缩放(compact ×.75 / cozy ×1 /
81
+ comfortable ×1.5),故**顺序在每个密度档都成立** —— 四档都是同一个数的倍数,不可能乱。
82
+ 🔴 [MUST NOT] 让卡内呼吸改回读 `--dc-core-content-gap`:那是第二个来源,与 gap 各自随
83
+ 不同轴变动,最松档实测会出现「卡内 ≥ 外圈」的乱序。
61
84
  - **完整能力演示**(toolbar 全形态 + 粘性三件套 + keepAlive 组合):`apps/reference/src/pages/list-layout/guide/`(最佳实践)、`pages/list-layout/sticky/`(粘性)、`pages/list-layout/module-demo/`(keepAlive 模块组合)——能力展示,非推荐默认。
62
85
 
63
86
  ## 上下文默认值(直系父上下文)
@@ -67,11 +90,38 @@ const { listPageConfig } = useSpaceConfig({
67
90
  | 场景 | prop | 自动默认 |
68
91
  | --- | --- | --- |
69
92
  | AppPage `#left` / `#right` 窄栏 | `tableMainProps` | 分页窄栏化:`{ pageLayout: "prev, pager, next", paginationBackground: false }` |
70
- | 其他容器 / 独立使用 | `tableMainProps` | 不处理(透传 TableMain 默认分页) |
93
+ | AppPage `#left` / `#right` 窄栏 | `formSearchProps.layoutByContainer` | `true`(搜索表单按**容器**断点定档)——窄栏里 `el-col` window 断点解析必然挤爆,是结构性的、不是口味 |
94
+ | 其他容器 / 独立使用 | 两者 | 不处理(透传各自默认) |
95
+
96
+ > `formSearchProps` 的上下文默认**逐键合并**:显式传 `formSearchProps` 不会整袋覆盖掉 `layoutByContainer`,
97
+ > 只有显式写 `formSearchProps: { layoutByContainer: false }` 才关掉它。
98
+ > ⚠️ `maxRows` **不**参与上下文默认 —— 它是**内容量**的函数(收起态最多几行、且含操作区所在行),不是容器宽的函数;
99
+ > 窄栏下 `maxRows: 1` 会把整块搜索区折叠塌成 0。该值 [MUST] 由消费方按页面内容判。
71
100
 
72
101
  > ⚠️ 需上下文动态默认的 prop 不写 `withDefaults` 默认(详见 spec:`docs/specs/2026-08-14-080509-moderate-usecontextdefaults直系父上下文默认/spec.md`)。
73
102
  > `ListLayout.refine` 默认恒 `true`(withDefaults),不参与上下文解析。
74
103
 
104
+ ## 各块的面由谁给
105
+
106
+ - **搜索 + 操作合并卡**(`.list-layout-chrome`):由 `ListLayout` 自己成面。
107
+ - **header 槽**:不成面,露底(甲方定调)。
108
+ - **数据区**(`TableMain` / `DataGridView` / `InfiniteListView`):**由它们自己持有**,
109
+ `ListLayout` [MUST NOT] 再经 `:deep()` 伸手刷 padding / 背景。
110
+ 要关掉数据区的面,走它们自己的 `:surface="false"`。
111
+
112
+ 🔴 原因:面与**可用高账**必须在同一个组件里闭环。此前 padding 由本组件从外面刷、
113
+ 可用高在 `TableMain` 里算,两边各管一半 ⇒ 每页恒漏一个 padding。
114
+
115
+ 同理,`.list-layout-chrome` 的高度由挂在**卡本身**的 `WatchSize` 量得(含卡自身
116
+ padding 与内部分段档),[MUST NOT] 退回「量内部两段再拼」。
117
+
118
+ ## 标题(`title` / `#header`)
119
+
120
+ **优先级:`#header` 插槽 > `title` prop > 不渲染**。常见场景传 `title` 即可,
121
+ 复杂头部用插槽接管。与 `ViewLayout.title` 同口径 —— 列表页与详情页头部结构对齐。
122
+
123
+ 标题行**露底不成面**(两卡模型里 header 不是卡),锚点类 `.dc-list-layout__header`。
124
+
75
125
  ## API
76
126
  > ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
77
127
 
@@ -107,6 +157,8 @@ const { listPageConfig } = useSpaceConfig({
107
157
  | `separateSearchConfig` | `Pick<SlotLayoutFlowAsideProps, "type"\|"gap"\|"asideWidth">` | 未设 | 分离侧栏布局 |
108
158
  | `searchSticky` / `toolbarSticky` / `pagerSticky` | `boolean` | 均 `false` | 三件套粘性(分离态下 searchSticky 被无视) |
109
159
  | `headerObserveResize` / `operationObserveResize` | `boolean` | 均 `false` | header/操作槽量高 ResizeObserver |
160
+ | `gap` | `number` | `bridge.APP_LAYOUT_GAP_CONFIG.size`(核内 8) | **模块间距**(header / 搜索 / 操作区各自的下间距 px)。缺省跟随布局 gap 体系,改布局 gap 即同步;脱离 AppLayout 挂载时退 8 |
161
+ | ~~`moduleBg`~~ | `boolean \| string` | `false` | 🚫 **已废弃**(`akw76r`)。它是**全开关**:一开四块全上色(**含数据区**),与「数据区恒透」直接冲突。它原本要解决的「让模块间距显出来」现已是**默认行为**(三块 chrome 默认成面)⇒ 无需再开。仍可用、行为不变(零 breaking),但 [MUST NOT] 用于新代码;移除走后续 breaking 窗口 |
110
162
  | `initialSearch` | `boolean` | `true` | 挂载后是否立即触发初始化查询(默认查);关闭后表格空态显示「欢迎触发查询」、KeepAlive 激活不自动刷新(TableMain 经「已发生过查询」自判,见 table docs) |
111
163
 
112
164
  **v-model**:`isAutoRefresh`、`refreshInterval`、`customView`(配合 `#custom-view-item` 卡片渲染)、`activeId`(`string | number | undefined`)/ `activeData`(行对象 | undefined)—— **「当前项」双 model**(= 用户点击/激活的那一条,[MUST NOT] 与勾选 selection 混同)。两者**同生同灭**:`activeId` 有值 ⇒ `activeData` 必有值且恒为**列表数据原样**;列表刷新后当前项不在新数据内 → 两者一并清空;外部写入不在列表内的 id → 忽略 + `console.warn`(不清空既有选中)。⚠️ `activeId` 的 `string` / `number` **两种都是一等公民**:出口值**保业务数据里的原始类型**(`rowKey` 为字段名时即 `row[rowKey]`,数字 id 出数字);外部用另一种类型写入(如 `"1"` 对数字 id)同样命中——比对时内部统一 `String()` 归一,且**保留调用方写入的原值**不回写覆盖。本组件是**纯透传层**,状态机在下游(table 态 → TableMain / DataListView;grid 态 → DataGridView);⚠️ `viewMode` 切换会换载下游组件 → 当前项随之丢失(既有换载语义)
@@ -141,6 +193,14 @@ const { listPageConfig } = useSpaceConfig({
141
193
  - **keepAlive 页返回刷新**:core 内置(onActivated refresh 留页码),app 侧 [MUST NOT] 补偿
142
194
  - **formSearchProps 塞 staticQuery/compact**:类型已 Omit,编译即拦
143
195
  - 搜索/表格的双子 props 与 TableMain 直属 prop 重复时,**以 ListLayout 直属为准**(TableMain 恒收 refine=false)
196
+ - 🔴 **[MUST NOT] 给数据区与 header 槽上背景色**(`.dc-table-main` / 网格视图 / `.list-layout-header`)。
197
+ 数据区是**主内容区**、header 是**页面标题带**,两者都该让底透上来——成面是为了把
198
+ 「要读的辅助块」(搜索 / 操作)从底上分出来,标题与正文不需要这件事,
199
+ 给它们上色会让「哪块是辅助、哪块是正文」的视觉层级塌掉。已废弃的 `moduleBg` 正因此被废。
200
+ - 🔴 **[MUST NOT] 用 `ElCard` 包这两块**(即便它看着正好是"卡片")。三条实证理由:
201
+ ⓐ 它的底读 `--el-card-bg-color: var(--el-fill-color-blank)`,**是第四个色来源**,
202
+ 绕开刚统一的底/面/浮层三档;ⓑ 自带 shadow / border / header 结构,逐条覆盖的成本
203
+ 高于直接写两条 CSS;ⓒ 多一层 DOM,而本族已有 `dc-` 锚点类可直接定向。
144
204
 
145
205
  ## 关联
146
206
 
@@ -31,6 +31,7 @@
31
31
  - `#placeholder` / `#error` 插槽:仅当需要自定义加载中/失败兜底图(小图 / base64)时传;缺省 EP 默认观感(灰底加载中 + 失败图标)。
32
32
  - `lazy={false}`:仅当图片必须立即加载(首屏无滚动场景)时关。
33
33
  - `preview={false}`:关闭点击放大(纯展示缩略图)——仍保留懒加载 + 占位兜底。
34
+ - **点击不冒泡(仅 `preview` 开启时)**:打开放大的同时 `stopPropagation`——缩略图常嵌在表格行 / 卡片里,点图看大图 [MUST NOT] 连带触发行点击 / 卡片跳转。`preview={false}`(纯展示)**不拦**,事件照常上抛给外层(此时组件不做任何事,吞事件反而会让外层行点击失效)。
34
35
  - **完整能力演示**:`apps/reference/src/pages/modal/preview/showcase/`——能力展示,非推荐默认。
35
36
 
36
37
  ## API
@@ -69,6 +70,7 @@
69
70
  - **`previewSrc` 才是预览路径**:`src` 是展示路径——缩略图/原图分离时别把大图塞 `src`(懒加载白拉全尺寸图)
70
71
  - **`#error` 是插槽不是属性**:传字符串路径是 EP 常见误区(加载失败显示空白)——兜底图放插槽内 `<img :src>`(或 base64)
71
72
  - **`preview=false` 只关放大**:仍渲染缩略图 + 懒加载 + 占位兜底——「图片展示」能力不随「预览」关掉
73
+ - **[MUST NOT] 外层再包 `@click.stop` 兜底**:`preview` 开启时组件内部已阻止冒泡;外层再包会把**纯展示态**(`preview=false`)的点击一并吞掉,外层行点击 / 卡片跳转随之失效
72
74
 
73
75
  ## 关联
74
76
 
@@ -43,6 +43,8 @@
43
43
  - `coverUrl`:仅当业务有封面图(如上传时单独传了封面)时传——有封面走 img 路径白拿懒加载 + 图标叠加。
44
44
  - `frameCover`(默认 `true`):无 `coverUrl` 时是否用 `video#t=0.01` 抠首帧作封面——开 = 首帧封面(视觉接近封面图,代价是**每实例一个 metadata 小请求**,`video` 无原生 lazy);关 = 深色占位(**零请求**)。视频素材库大列表介意请求量 → 关掉或按需补懒加载(YAGNI,暂未内置)。
45
45
  - **`#default="{ open }"`**:整体自定义触发 UI(按钮 / 卡片 / 列表行)——`open` 即「打开预览弹窗」,核心决策链与兜底不变;`placeholder` / `error` 插槽仅内置封面内容使用。
46
+ - **点击不冒泡(仅 `preview` 开启时)**:唤起预览的同时 `stopPropagation`——封面常嵌在表格行 / 卡片里,点封面看视频 [MUST NOT] 连带触发行点击 / 卡片跳转。`preview={false}`(纯展示)**不拦**,事件照常上抛。
47
+ **自定义默认插槽同样受益**:`open` 接收事件对象,`#default="{ open }"` 里写 `@click="open"` 即自动阻止冒泡(写成 `@click="() => open()"` 丢掉事件则不阻止,需自行 `.stop`)。
46
48
  - 弹窗端行为(`coverUrl` 为空时 `#t=0.01` 首帧兜底等)在 `ModalVideoPreview` 上,见 README-ModalVideoPreview——**触发端首帧由 `frameCover` 控制**。
47
49
  - **完整能力演示**:`apps/reference/src/pages/modal/preview/showcase/`——能力展示,非推荐默认。
48
50
 
@@ -70,7 +72,7 @@
70
72
 
71
73
  | 槽 | scope | 语义 |
72
74
  | --- | --- | --- |
73
- | `#default` | `{ open: () => void }` | **可整体自定义触发 UI**(按钮「播放」/卡片/列表行):`open` = 打开预览弹窗(内部决策链 + 自带兜底不变);缺省 = 封面图/占位 + 播放图标(两形态统一),自带点击唤起 |
75
+ | `#default` | `{ open: (event?: Event) => void }` | **可整体自定义触发 UI**(按钮「播放」/卡片/列表行):`open` = 打开预览弹窗(内部决策链 + 自带兜底不变);缺省 = 封面图/占位 + 播放图标(两形态统一),自带点击唤起 |
74
76
  | `#placeholder` | 无 | 封面加载中占位(透传 ElImage;仅内置封面内容) |
75
77
  | `#error` | 无 | 封面加载失败兜底图(透传 ElImage;仅内置封面内容) |
76
78
 
@@ -85,6 +87,7 @@
85
87
  - **`$attrs` 只落封面层**:想配「弹窗端视频」(`autoplay` / `muted` 等)[MUST NOT] 经触发组件透传——去 `ModalVideoPreview` 上配
86
88
  - **决策链静默回退是硬行为**:任何环境(无遥控器/未注册/未开启全局)都不许报错——自带弹窗兜底保证触发组件永远可用
87
89
  - **`preview=false` 只关点击**:仍渲染封面/占位——「展示」能力不随「预览」关掉
90
+ - **`open` 要把事件透给它**:`#default="{ open }"` 里写 `@click="open"`(自动阻止冒泡)优于 `@click="() => open()"`(丢事件 → 不阻止冒泡,外层行点击被连带触发)
88
91
 
89
92
  ## 关联
90
93
 
@@ -21,6 +21,16 @@
21
21
 
22
22
  **② 被门面内嵌(`embedded=true`)** —— 由 `AppPageListDetailLayout` 内部使用,宿主已有一层 `AppPage`,故透传不再套;门面下发完整 `ctx`(含序列导航句柄)。业务一般不直接这么用。
23
23
 
24
+ ## 标题渲染在哪(`detailTitle`)
25
+
26
+ - **传了 `detailApi`**(内置 `ViewLayout`)⇒ 标题走 **`ViewLayout` 的 `#header` 槽**,
27
+ 即「容器的标题位」,与 `ListLayout` 的 header 对齐、锚点 `.dc-view-layout__header`。
28
+ - **没传 `detailApi`**(详情数据业务自持、无 `ViewLayout` 容器)⇒ 由本组件自画,
29
+ 锚点 `.dc-app-page-detail__title`,排版与前者同口径。
30
+
31
+ 🔴 [MUST NOT] 退回「在 `#default` 里自画一个 div」:那样标题成了「内容的第一行」,
32
+ 位置与 `ListLayout` 对不齐、主题 persona 无从定向(原实现即如此,已归位)。
33
+
24
34
  ## API
25
35
 
26
36
  > ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
@@ -42,7 +52,7 @@
42
52
 
43
53
  | 槽 | scope | 语义 |
44
54
  | --------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
45
- | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情内容。`data` / `loading` 仅在传了 `detailApi` 时有值;独立使用时序列字段为零值 |
55
+ | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading, fetchSeq }` | 详情内容。`data` / `loading` / `fetchSeq` 仅在传了 `detailApi` 时有值(`fetchSeq` = 成功拉取次数,失败不递增,未传 `detailApi` 恒 0);独立使用时序列字段为零值。⚠️ `resolvedMode` 只有门面下发,独立成页时无此键(没有形态概念) |
46
56
  | `#empty` | `{ listEmpty }` | 无当前项时的占位 |
47
57
 
48
58
  ## 反模式 / 注意
@@ -15,10 +15,10 @@
15
15
  AppPageListDetailLayout 【L1 门面】无 DOM 输出:持状态 + 形态定档 + provide 上下文
16
16
  ├─ split → AppPageListDetailSplit 【L2】AppPage:#left = 列表 · 默认槽 = 详情区
17
17
  └─ sheet → AppPageListDetailSheet 【L2】AppPage:默认槽 = 列表 · 抽屉 = 详情区
18
- └─ AppPageListDetailPane 【L3】有当前项 → 详情;无 → 空态
18
+ └─ AppPageDetail 【L3】有当前项 → 详情;无 → 空态
19
19
  ```
20
20
 
21
- 三个实现件(Split / Sheet / Pane)**不对外导出**,是门面的实现细节。
21
+ 两个形态件(Split / Sheet)**不对外导出**,是门面的实现细节;L3 `AppPageDetail` **对外导出**(可独立成页,见 [README-AppPageDetail.md](./README-AppPageDetail.md))。
22
22
 
23
23
  ## 快速上手
24
24
 
@@ -118,11 +118,69 @@ watch(isOutOfSync, (v) => v && refreshList());
118
118
 
119
119
  | 槽 | scope | 语义 |
120
120
  | ----------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
121
- | `#list` | `{ listProps }` | 列表。`listProps` [MUST] 整体 `v-bind` 到列表组件 |
122
- | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情。`data` / `loading` 仅在传了 `detailApi` 时有值 |
123
- | `#empty` | `{ listEmpty }` | 未选中占位。`listEmpty=true` 列表本身无数据 / `false` 有数据但未选中,**两种文案 [MUST] 分流** |
121
+ | `#list` | `{ listProps, listLayoutRecommended, resolvedMode }` | 列表。`listProps` [MUST] 整体 `v-bind` 到列表组件;`listLayoutRecommended` 是 `AppPage#left` 的窄栏推荐配置(split 档才有值,见下) |
122
+ | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading, fetchSeq, resolvedMode }` | 详情。`data` / `loading` / `fetchSeq` 仅在传了 `detailApi` 时有值 |
123
+ | `#empty` | `{ listEmpty, resolvedMode }` | 未选中占位。`listEmpty=true` 列表本身无数据 / `false` 有数据但未选中,**两种文案 [MUST] 分流** |
124
124
  | `#top` / `#bottom` / `#right` | 无 | 直通 `AppPage` 同名悬浮槽(**两形态都支持,`auto` 自动挡切档后依旧在**) |
125
125
 
126
+ ### `resolvedMode` —— 实际档位(三槽都有)
127
+
128
+ `mode` 是**入参**("我要什么"),`auto` 档实际落在哪一档只有门面知道。三个插槽 scope 都下发只读的
129
+ `resolvedMode: "split" | "sheet"` ——**值是实际档位**,`mode="auto"` 下随视口跨过 `splitAt` 翻转,
130
+ 强制档恒为对应值。
131
+
132
+ ```vue
133
+ <template #list="{ listProps, resolvedMode }">
134
+ <ListLayout v-bind="listProps" :api="api" :columns="resolvedMode === 'split' ? narrowCols : fullCols" />
135
+ </template>
136
+ ```
137
+
138
+ - **[MUST NOT] 自己再判一次视口宽** —— 与门面的 `splitAt` 形成两个事实源,调用方改 `splitAt` 时静默不一致。
139
+ - **纯按宽度变现**(列数 / 间距)优先用 CSS `@container` 容器断点,不必经此值;`resolvedMode` 是给**行为分支**用的。
140
+
141
+ ### `fetchSeq` —— 详情重拉计数(`#detail`)
142
+
143
+ 传了 `detailApi` 时门面内置 `ViewLayout`,每次 `detailApi` **成功返回** `fetchSeq` +1(**失败不递增**)。
144
+ 详情里的懒加载下游块(点开才拉的 tab 等)`watch` 它即可选择性重拉:
145
+
146
+ ```vue
147
+ <template #detail="{ data, fetchSeq }">
148
+ <ElTabs v-model="tab">
149
+ <ElTabPane name="trace" label="执行步骤">
150
+ <!-- tab 打开过才拉;详情整体刷新后跟着重拉 -->
151
+ <TracePanel v-if="tab === 'trace'" :id="data?.id" :refresh-token="fetchSeq" />
152
+ </ElTabPane>
153
+ </ElTabs>
154
+ </template>
155
+ ```
156
+
157
+ - **[MUST NOT] 用 `loading` 的 `true→false` 代替** —— 失败也会落回 `false`,下游会基于旧数据白拉一次。
158
+ - **[MUST NOT] watch `data` 的对象身份** —— 「每次返回新引用」从不是承诺,内部一旦做缓存 / 就地合并即静默失效。
159
+ - 未传 `detailApi` 时恒 `0`(无内置 `ViewLayout`、无拉取)。
160
+
161
+ ### ⚠️ `#list` 里包一层 div 会打断上下文默认
162
+
163
+ `ListLayout` / `ViewLayout` / `TabsMain` 的上下文默认(`useContextDefaults`)读的是**直系父 DOM** 的槽位标记。
164
+ 门面已在 split 档的列宽容器上挂了 `AppPage#left` 的标记,**但消费方在 `#list` 内自己包一层**
165
+ (做卡片网格几乎必然要包)就会把链打断 —— 表现是分页不窄栏化、搜索表单按 window 断点被挤爆,**且不报错**。
166
+
167
+ 两条出路,按场景选:
168
+
169
+ - **ⓐ 不包裹**:直接把 `ListLayout` 作为 `#list` 的根,样式挂到 `ListLayout` 自己身上(`class` / `style` 原样透传)。
170
+ - **ⓑ 包裹了就显式传**:用 `listLayoutRecommended`(split 档下发的窄栏推荐配置)+ 显式 `formSearchProps`:
171
+
172
+ ```vue
173
+ <template #list="{ listProps, listLayoutRecommended }">
174
+ <div class="card-grid">
175
+ <ListLayout v-bind="{ ...listProps, ...listLayoutRecommended }"
176
+ :form-search-props="{ layoutByContainer: true }" :api="api" :columns="columns" />
177
+ </div>
178
+ </template>
179
+ ```
180
+
181
+ 🔴 **[MUST NOT] 自己在包裹层挂 `data-dc-slot`** —— 那个 attr 的契约是「宿主在槽内容直系父上挂」,
182
+ 消费方挂它等于冒充宿主;且上游一旦调整标记位置,这种写法**不报错、只是某天悄悄不生效**。
183
+
126
184
  > **为什么 `#right` 可用、`#left` 不可用**:门面对 `AppPage` 槽的占用是——Split 用 `#left` 放列表、**默认槽**放详情;Sheet 用**默认槽**放列表、**抽屉**放详情。两形态都**没占 `#right`**,故它原样开放给业务;`#left` 则是 Split 自用,不开放。
127
185
  >
128
186
  > ⚠️ **Sheet 形态下详情打开时抽屉会盖住右栏**:抽屉是 EP 弹层(z-index 2000+ 档)且默认 70% 宽,而 AppPage 悬浮槽是 `APP_PAGE_SLOT_Z_INDEX`=1。这是形态固有结果、**不视为缺陷**——侧栏承载的本就是次要信息(说明 / 辅助入口一类),被临时遮住无妨;[MUST NOT] 为此加避让或降级逻辑。
@@ -140,7 +198,7 @@ watch(isOutOfSync, (v) => v && refreshList());
140
198
 
141
199
  ## 关联
142
200
 
143
- - 内部件(不导出):`AppPageListDetailSplit` / `AppPageListDetailSheet` / `AppPageListDetailPane` / `use-list-detail.ts`
201
+ - 内部件(不导出):`AppPageListDetailSplit` / `AppPageListDetailSheet` / `use-list-detail.ts`;L3 `AppPageDetail` 对外导出
144
202
  - 不变式内核:`hooks/use-active-record.ts`(`useActiveRecord`,列表族三家共用)
145
203
  - 上下文 key:`APP_PAGE_LIST_DETAIL_CONTEXT`(导航句柄 + `refreshList`,`ViewLayout` 可选 inject 探测)
146
204
  - 详情容器:`ViewLayout`(`refreshToken` 外部重拉口 + 内置导航 `showNavigation`)
@@ -55,6 +55,26 @@ const toolbarConfig: TableToolbarConfig<Row> = {
55
55
  - `showPager`/`pageSizeInit`/`pageSizeOptions`:仅分页形态不满足时调;常规列表不动。
56
56
  - **完整能力演示**(工具栏按钮组 + 骨架屏 + fillHeight + 网格视图):`apps/reference/src/pages/table/toolbar/`、`apps/reference/src/pages/table/skeleton/`——能力展示,非推荐默认。
57
57
 
58
+ ## 自持面(`surface`)
59
+
60
+ `TableMain` **自己持有那块面**(面色 `--el-bg-color` + 内呼吸 + 圆角),
61
+ 不依赖外层容器伸手来刷 —— 单独用 `TableMain`(不套 `ListLayout`)时观感一致。
62
+
63
+ 🔴 **内呼吸计入可用高**:`surface` 开启时自身 padding 会从数据区可用高里扣掉。
64
+ 这是「面归组件自己持有」的**根本理由** —— 样式与高度账在同一个组件内闭环。
65
+ 此前 padding 由 `ListLayout` 经 `:deep()` 从外面刷、可用高在 `TableMain` 里算,
66
+ 两边各管一半,结果每页恒漏一个 padding(甲方 2026-09-15 报障)。
67
+ **[MUST NOT] 退回「外层刷面、内层算高」。**
68
+
69
+ **弹窗内不二次成面**:置于 `ModalConfirm` 正文(`data-dc-slot=ModalConfirm_Body`)
70
+ 时,经 `useContextDefaults` 自动取 `false`。亮色下 `--el-bg-color` 与
71
+ `--el-bg-color-overlay` 同为 `#fff`、**看不出区别**;暗色下 overlay `#1d1e1f`
72
+ 上盖 `#141414` 才会露出「弹窗里凹下去一块」。
73
+
74
+ ⚠️ **已知边界**:`useContextDefaults` 只读**直系父** DOM。若弹窗正文里放的是
75
+ `ListLayout`(它包着 `TableMain`),直系父不是弹窗正文 ⇒ 推导不到、仍会成面,
76
+ 此时 [MUST] 显式传 `:surface="false"`。
77
+
58
78
  ## API
59
79
  > ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
60
80
 
@@ -66,6 +86,7 @@ const toolbarConfig: TableToolbarConfig<Row> = {
66
86
  | `columns` | `ElTableColumnProps<T,F>[]`(必传) | — | 列配置数组 |
67
87
  | `rowKey` | `Extract<keyof T,string> \| ((row:T)=>string)`(必传) | — | 行主键 |
68
88
  | `query` | `SQ` | — | 静态查询参数;变化时重置到第 1 页重新拉取 |
89
+ | `surface` | `boolean` | `true` | **自持面**:面色 + 内呼吸 + 圆角。置于 `ModalConfirm` 正文里时经上下文自动转 `false`(那儿已是一块面);显式传值恒优先。见下「自持面」 |
69
90
  | `showPager` | `boolean` | `true` | 展示分页器 |
70
91
  | `pageSizeInit` | `number` | `20` | 初始每页条数 |
71
92
  | `pageSizeOptions` | `number[]` | `[10,20,30,40]` | 分页器每页条数选项 |
@@ -132,6 +153,7 @@ const toolbarConfig: TableToolbarConfig<Row> = {
132
153
  - **`skeleton` 只覆盖冷启**:热刷不出骨架(外层遮罩盖旧数据),勿误判为骨架失效
133
154
  - **customView 需配套插槽**:`customView=true` 显式切 DataListView 时 [MUST] 配 `#custom-view-item` 插槽,否则无渲染
134
155
  - **组件 props 版列 scope 是 `_index`**:`$index` 仅插槽 scope 可用;组件 props 版 `$` 开头属性 vue 报错
156
+ - **[MUST NOT] 从外面给 `.dc-table-main` 刷 padding / 背景**:面归组件自持,外面刷会让内呼吸游离在高度账之外(这正是被修掉的老毛病)。要关面用 `:surface="false"`
135
157
  - **TableSkeleton / TableToolbar / ToolbarButtons 是内部件**:index.ts 不导出,[MUST NOT] 外部直接消费;对外只认 `TableMain` + types/constants
136
158
 
137
159
  ## 关联
@@ -8,7 +8,7 @@
8
8
  - **空间一等公民**:展示空间布局首选(页级数据展示的布局壳)——承接 PanelMain 废弃的页级展示场景
9
9
  - **何时用**:页级数据展示(详情 / 预览 / 摘要展示)——api 拉数 + 自动刷新 + 内容自由编排
10
10
  - **何时不用**:结构化详情字段面板用 `PanelMain`(仅表单预览 / 就地编辑保留场景);列表用 `ListLayout`
11
- - 内部组合:`ActionBtnGroup`(左/右 extra 按钮,`default-size="small"` 对齐 table-toolbar 视觉惯例)+ `AutoRefreshGroup`(右侧,refreshFn=api 拉取,经 `HeightProvider` #header 槽 WatchSize 量高)+ 默认插槽(`{ data, loading, refresh, viewportHeight }` 注入)
11
+ - 内部组合:`ActionBtnGroup`(左/右 extra 按钮,`default-size="small"` 对齐 table-toolbar 视觉惯例)+ `AutoRefreshGroup`(右侧,refreshFn=api 拉取,经 `HeightProvider` #header 槽 WatchSize 量高)+ 默认插槽(`{ data, loading, refresh, viewportHeight, fetchSeq }` 注入)
12
12
 
13
13
  ## 快速上手(最小可用)
14
14
 
@@ -54,6 +54,30 @@
54
54
 
55
55
  > ⚠️ 需上下文动态默认的 prop 不写 `withDefaults` 默认(详见 spec:`docs/specs/2026-08-14-080509-moderate-usecontextdefaults直系父上下文默认/spec.md`)。
56
56
 
57
+ ## 标题行(`title` / `#header`)
58
+
59
+ 标题渲染在**操作条之上的独立一行**,与 `ListLayout` 的 header 槽结构对齐 ——
60
+ 列表页与详情页头部一致,主题 persona 也只需定向一个锚点 `.dc-view-layout__header`。
61
+
62
+ **优先级:`#header` 插槽 > `title` prop > 不渲染**(不渲染 = 不留空盒占高)。
63
+
64
+ ```vue
65
+ <!-- 常见场景:一个字符串 -->
66
+ <ViewLayout :api="api" title="订单详情" />
67
+
68
+ <!-- 标题跟着详情数据走:用插槽,scope 带 data / loading -->
69
+ <ViewLayout :api="api">
70
+ <template #header="{ data }">{{ data?.name ?? "加载中" }}</template>
71
+ </ViewLayout>
72
+ ```
73
+
74
+ 🔴 为什么标题要交给容器、[MUST NOT] 在内容插槽里自画一个 div:自画的标题是
75
+ 「内容的第一行」而非「容器的标题位」——位置与 `ListLayout` 对不齐,主题也无从定向。
76
+ `AppPageDetail` 原先即如此,已归位(见 migrations)。
77
+
78
+ 样式口径同 `ListLayout` 的 header:**露底不成面**(两卡模型里 header 不是卡),
79
+ 只给字号 / 字重 / 外距,[MUST NOT] 加背景与描边。
80
+
57
81
  ## API
58
82
  > ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
59
83
 
@@ -79,7 +103,7 @@
79
103
 
80
104
  | 槽 | scope | 语义 |
81
105
  | --- | --- | --- |
82
- | 默认 | `{ data, loading, refresh, viewportHeight }`(`ViewLayoutScope<R>`) | 展示内容;PanelMain 或自由编排皆可;viewportHeight refine 时给可用高、自然流 undefined |
106
+ | 默认 | `{ data, loading, refresh, viewportHeight, fetchSeq }`(`ViewLayoutScope<R>`) | 展示内容;PanelMain 或自由编排皆可;viewportHeight refine 时给可用高、自然流 undefined;`fetchSeq` = **成功**拉取次数(失败不递增),供下游懒加载块判「该重拉了」 |
83
107
 
84
108
  ### 内置上/下一条导航(仅列表详情范式内出现)
85
109
 
@@ -99,6 +123,7 @@
99
123
  - **纯展示套 PanelMain 作页级根**:PanelMain 已废弃页级展示场景(见 panel 族文档)——纯展示在插槽内自由编排
100
124
  - **channel / parentChannel 响应式变更**:[MUST NOT]——setup 期快照,后续变更无效;缺 refine 的 channel 链静默失效
101
125
  - **api 抛错不吞**:api 内部自行处理错误(ViewLayout 只保证 loading 复位,不拦截异常)
126
+ - **[MUST NOT] 用 `loading` 的 `true→false` 当「拉完了」的信号**:失败也会落回 `false`。要判「成功拉到新数据」用 `fetchSeq`(只在成功返回时 +1)
102
127
  - **要重拉不要改 `params` 指望它自动生效**:`params` 响应式变化**刻意**不重拉(防请求风暴的既有设计)。「换了参数要重拉」= 改 `params` **并**动 `refreshToken`(或调 `scope.refresh()`)
103
128
  - **[MUST NOT] 把触发值塞进 `params` 当刷新开关**:那会把纯前端的令牌发给后端、污染 API 契约——这正是 `refreshToken` 独立成 prop 的原因
104
129
  - **[MUST NOT] 用 `:key="token"` 重建来实现重拉**:会连 toolbar / 按钮一起重建,连续切换时闪烁,且语义错位(chrome 不该随内容重建)——用 `refreshToken`
@@ -10,7 +10,7 @@
10
10
  | `useCustomBreakpoint` | 容器宽断点:`useCustomBreakpoint()` + `resolveBreakpointByWidth(width)` + `resolveRootEl`(EP 栅格同源阈值) | **容器断点**(非视口):FormMain `layoutByContainer`、compact 搜索等 |
11
11
  | `useObserveSize` | 激活态感知 RO 观测原语:`getTarget` / `onResize` / `enabled` / `debounce`(默认 100ms,leading+trailing) | 需要「捕获渲染后尺寸变化」(CSS 折叠/异步撑开),WatchSize `observeResize` 底层 |
12
12
  | `useChannelViewportHeight` | 视口可用最大高解析:`viewportHeight` > scope 链(parentChannel 定向/就近)> fallback;返回 `viewportHeightFinal` + `nearestScope` | 列表/表格/弹窗的高度计算核心消费契约 |
13
- | `useCoreThemeApply` | 主题挂值:JS → root CSS var(`--dc-core-*` + `--el-*` 双命名空间)+ `buildRhythmCss`(density/motion 节奏) | 由 AppLayout 启动;业务一般不直接调 |
13
+ | `useCoreThemeApply` | 主题挂值:JS → root CSS var(`--dc-core-*` + `--el-*` 双命名空间)+ **全局底**(`:root{background}`)+ elevation 三档(底 `--el-bg-color-page` / 面 `--el-bg-color`=bodyColor / 抬升面 `--dc-core-surface`+`--el-bg-color-overlay`)+ `buildRhythmCss`(density/motion 节奏) | 由 AppLayout 启动;业务一般不直接调 |
14
14
  | `useCoreViewportApply` | viewport 边框 CSS var 挂值(`--dc-core-viewport-*` / `--dc-core-gap`) | 由 AppLayout 启动 |
15
15
  | `useMenusDataDispatch` | 菜单数据分发:`menus` / `menuFlatList` / `extractLevel1ToHeader` + 当前路由聚焦菜单 | AppHeader/AppSidebar 消费;业务接 meta.menu 树 |
16
16
  | `useTimeout` | 定时器 `[setTimer, clearTimer]`,`autoClear` 随激活态/卸载清理 | 组件内延时逻辑(替代裸 setTimeout,防泄漏) |
@@ -47,3 +47,32 @@ const { viewportHeightFinal } = useChannelViewportHeight({ scope: "my-scope" });
47
47
 
48
48
  - `useActivated` 详细语义见 `docs/specs/` 相关任务文档与 CLAUDE.md 规则 21
49
49
  - 断点体系:`useBreakpoint`(视口)与 `useCustomBreakpoint`(容器)双源,见 FormMain `layoutByContainer` 文档
50
+
51
+ ## elevation 三档怎么取值(`useCoreThemeApply`)
52
+
53
+ | 档 | CSS var | 取值 |
54
+ |---|---|---|
55
+ | **底**(layout ground) | `--el-bg-color-page`(+ `--dc-core-ground`) | `ground` **显式优先**;缺省由 `bodyColor` 向黑压一步派生 |
56
+ | **面**(surface / 默认背景色) | `--el-bg-color`(+ `--dc-core-body-color`) | = `bodyColor` |
57
+ | **抬升面**(overlay) | `--el-bg-color-overlay`(+ `--dc-core-surface`) | `surface` **显式优先**;缺省暗色由面向白提一步、亮色 = 面 |
58
+
59
+ ⚠️ **`bodyColor` 是「面」不是「底」** —— 名字骗人(听起来像 `<body>` 的颜色),
60
+ 实际语义是**默认背景色**。要设底 [MUST] 用 `ground`,[MUST NOT] 改 `bodyColor` 去凑。
61
+
62
+ ### 近黑翻转(余量不足时的兜底)
63
+
64
+ `bodyColor` 的最亮通道 `< 12` 时(如 `#000000` / `#060606`),"向黑压一步"没有余量可压
65
+ (`#000` 压出来还是 `#000` ⇒ **底 = 面、缝彻底不可见**)。此时**翻转方向**:
66
+
67
+ - 底 = `bodyColor` 原值(把它当地面看)
68
+ - 面 = `bodyColor` **向白提一步**、抬升面再提一步
69
+
70
+ 并 `console.warn` 提示作者改用 `ground` 显式指定。**显式给了 `ground` 就不会触发翻转**
71
+ (有显式值就不需要猜)。
72
+
73
+ 判据用**余量**(最亮通道)不用「亮/暗」:后者需要外部告知 `isDark`,且会在
74
+ 「深灰但不算暗色」的主题上判错;余量是纯可计算的。
75
+
76
+ **方向不随亮暗翻转**:无论亮暗,**面恒比底亮** —— 这是业界共识(EP 亮 `#f2f3f5 < #fff`、
77
+ EP 暗 `#0a0a0a < #141414 < #1d1e1f`、M2 白 overlay 递增、M3 surface-container tone 递增)。
78
+ 近黑翻转翻的是**谁来充当基准**,不是"底比面亮"。
@@ -1,4 +1,4 @@
1
- import { App, CSSProperties, DeepReadonly, Ref } from 'vue';
1
+ import { App, Component, CSSProperties, DeepReadonly, Ref, ShallowRef } from 'vue';
2
2
  import { RouteRecordRaw } from 'vue-router';
3
3
  import { createUseState } from './state';
4
4
  import { createGenerateRouteMetaRawTree } from './route';
@@ -46,6 +46,20 @@ export interface CoreBridgeRegisterOptions<UserInfo = unknown, LoginParams = unk
46
46
  getUserInfoApi?: () => Promise<Partial<UserInfo>>;
47
47
  /** 刷新 token api(#C 期签名最简化:core 不感知业务 shape,直传 refreshToken string;业务方 wrap 内自打包业务约定的入参 shape,如 `{Authorization: rt}`),增量可选 */
48
48
  refreshTokenApi?: (refreshToken: string) => Promise<string>;
49
+ /**
50
+ * AI 填充触发组件(gf9z3w 控制反转:注册一次全站根表单可用并驱动入口显隐)。
51
+ * core 渲染时喂 duck props(`getSchema` / `onData → { appliedCount, undo }`),
52
+ * 组件来源任意(如 forge-agent `LlmJsonTrigger` 经应用薄 wrapper 预绑定传输参数)——
53
+ * core 与提供方互不知晓。增量可选。
54
+ */
55
+ aiFillTrigger?: Component;
56
+ /**
57
+ * AI 填充策略位:表单未配 `description`(表单目标描述)时是否**不予出触点**
58
+ * (默认 `false` = 照出 + dev 提示——实证 description 是映射质量增强项而非可用性
59
+ * 前提,字段 label/类型/enum 已够模型映射)。严肃交付场景想强管控(逼业务写清
60
+ * 表单用途)→ 置 `true`。`FormSearch` 自报家门恒有描述,天然豁免。增量可选。
61
+ */
62
+ aiFillRequireDescription?: boolean;
49
63
  }
50
64
  export type { CoreBridgePlugin, CoreBridgePluginBridgeSlice, CoreBridgePluginContext, CoreBridgePluginInstall, CoreBridgePluginLike, } from './plugin';
51
65
  /**
@@ -102,8 +116,25 @@ export interface AppThemeColor {
102
116
  blur?: string;
103
117
  /** 聚焦环(风格可选;core 仅挂 --dc-core-focus-ring,EP 无对应) */
104
118
  focusRing?: string;
105
- /** 表面色(风格可选;core 仅挂 --dc-core-surface,EP 无对应) */
119
+ /**
120
+ * 抬升面色(overlay 档,风格可选;挂 --dc-core-surface + --el-bg-color-overlay)。
121
+ * 缺省由 bodyColor 派生(暗色向白一步、亮色 = bodyColor,见 use-theme-apply
122
+ * buildElevationDecls);显式给值以显式为准。[MUST NOT] 与 bodyColor(面)语义重叠。
123
+ */
106
124
  surface?: string;
125
+ /**
126
+ * 布局底色(page 档,风格可选;挂 --dc-core-ground + --el-bg-color-page)。
127
+ * 缺省由 bodyColor 向黑压一步派生(`themeScalePageColor`);显式给值以显式为准。
128
+ *
129
+ * 🔴 [MUST NOT] 与 bodyColor 语义重叠:**bodyColor 是「面」(默认背景色),本字段是「底」**
130
+ * (页面地面 —— 模块间距的缝、header 上方滚动遮挡条露出的就是它)。
131
+ *
132
+ * 显式给值能解决派生的三个退化点(派生 Δ = min(0.5×最亮通道, 18)):
133
+ * - 近黑(`#000000` ⇒ Δ=0、`#0c0c0c` ⇒ Δ=6):底≈面、缝不可见 —— 不给 ground 时走近黑翻转兜底;
134
+ * - 非 hex 的 bodyColor(`var()` / `rgba()` / 渐变):派生原样返回 ⇒ 底 = 面;
135
+ * - 品牌规范指定了具体的底色灰阶。
136
+ */
137
+ ground?: string;
107
138
  }
108
139
  /**
109
140
  * 布局模块 `style` 逃生舱排除的「引擎托管键」联合(甲方终锁键集)。
@@ -199,19 +230,43 @@ export interface AppConfig {
199
230
  };
200
231
  /** 应用布局-页面主体配置(store/app 仅取 shimPadding) */
201
232
  APP_LAYOUT_BODY_CONFIG: {
202
- /** 页面主体 padding - 默认值(px) */
203
- shimPadding: number;
233
+ /**
234
+ * @deprecated huhyx7 起**不再读取、不影响任何渲染**;配了非 0 值只会得到一条
235
+ * `console.warn`。字段保留一个版本供下游过渡,之后移除。
236
+ *
237
+ * **替代**:内容四周留白改由**间距阶梯的外圈档**(`APP_LAYOUT_GAP_CONFIG.size × 2`)
238
+ * 一处给出 —— 此前这段空白由 `gap + shimPadding` 两个来源各给一半,
239
+ * `akw76r` 让容器层露底后两者在视觉上就是同一段空白,冗余由此产生。
240
+ * 要调留白 ⇒ 改 `APP_LAYOUT_GAP_CONFIG.size`(一个旋钮按比例缩放
241
+ * 外圈 ×2 / 呼吸 ×1.5 / 模块 ×1 / 分段 ×0.5 四档,见 `utils/gap-scale.ts`)。
242
+ *
243
+ * 已由必填改为**可选**(类型放宽,非 breaking):下游删掉该键即可,不会 TS 报错。
244
+ */
245
+ shimPadding?: number;
246
+ /**
247
+ * 内容主体背景(color / image / gradient 等 CSS background)。
248
+ *
249
+ * **缺省不铺**(akw76r)——`.app-body` 是容器、容器一律透明;布局底由全局 `html`
250
+ * 一处铺(core 在 `:root{}` 挂 `background: var(--el-bg-color-page)`),一路透到这里。
251
+ * **模块间距(`APP_LAYOUT_GAP_CONFIG`)的缝**与 **header 上方的滚动遮挡条**
252
+ * 露出的都是全局那层底。
253
+ *
254
+ * 显式给值 = 在 `.app-body` 这一层铺一块面(只影响内容区,不影响 chrome 模块与外框)。
255
+ * 🔴 [MUST NOT] 拿它铺「与全局底同色」的面 —— 那是把一个会变的值复制到第二处,
256
+ * 换底 / 换主题时必留补丁。要改底色 → 改**主题的 `ground` / `bodyColor`**(r8kge2)。
257
+ */
258
+ background?: string;
204
259
  };
205
260
  /**
206
- * 应用布局-模块间距配置(内缝;模块间露 size px 底色条带,漏出 .app-body 的 bodyColor)。
261
+ * 应用布局-模块间距配置(内缝;模块间露 size px 条带,漏出 `.app-body` 的布局底)。
207
262
  *
208
- * 与 viewport 边框(整体外框)正交:gap 是模块**之间**的缝。默认 `{ size: 0 }` = 模块贴合、
209
- * 现状不变;`size>0` store 几何 calc 在相邻模块间叠加该间距(缺位侧不留悬空缝)。
210
- * 分离观感需模块 `background` surface 色(≠ bodyColor),由 preset/消费方设。
211
- * 布局插件经 `bridge.update("APP_LAYOUT_GAP_CONFIG", { size })` 设值。
263
+ * 与 viewport 边框(整体外框)正交:gap 是模块**之间**的缝。默认 `{ size: 8 }` = 开缝
264
+ * (底/面分离后缝天然可见:模块面 = bodyColor、`.app-body` = 派生 page 档);
265
+ * 要模块贴合显式传 `{ size: 0 }`。`size>0` store 几何 calc 在相邻模块间叠加该间距
266
+ * (缺位侧不留悬空缝)。布局插件经 `bridge.update("APP_LAYOUT_GAP_CONFIG", { size })` 设值。
212
267
  */
213
268
  APP_LAYOUT_GAP_CONFIG: {
214
- /** 模块间距(px);缺省 0 = 贴合 */
269
+ /** 模块间距(px);缺省 8 = 开缝,0 = 贴合 */
215
270
  size: number;
216
271
  };
217
272
  /**
@@ -533,6 +588,10 @@ export interface CoreBridge<UserInfo = unknown, LoginParams = unknown> {
533
588
  getUserInfoApi: () => Promise<Partial<UserInfo>>;
534
589
  /** 刷新 token api(createUserStore refreshTokenFn 内调;#C 期签名最简化:core 直传 refreshToken string,业务 wrap 内自打包业务 shape;未注册被调 reject 原生 Error) */
535
590
  refreshTokenApi: (refreshToken: string) => Promise<string>;
591
+ /** AI 填充触发组件槽位(**响应式** shallowRef 只读——表单入口显隐依赖;[MUST NOT] 学 auth 槽位裸闭包 let) */
592
+ readonly aiFillTrigger: Readonly<ShallowRef<Component | undefined>>;
593
+ /** AI 填充「无 description 不出触点」策略位(响应式只读,默认 false;见 register 选项注释) */
594
+ readonly aiFillRequireDescription: Readonly<Ref<boolean>>;
536
595
  /** 应用基础信息(per #2.5 期 REQ-1 直接挂载形态) */
537
596
  readonly APP_BASE_INFO: DeepReadonly<AppBaseInfo>;
538
597
  /** 应用环境信息(per #2.5 期 REQ-1) */
@@ -1,4 +1,3 @@
1
- import { AppPageListLayoutRecommended } from './types';
2
1
  type __VLS_Props = {
3
2
  /** 充满视口 */
4
3
  fullViewport?: boolean;
@@ -8,13 +7,18 @@ type __VLS_Props = {
8
7
  background?: string;
9
8
  /** 四向悬浮插槽与内容主体 / 插槽间的间隔(px),默认 8 */
10
9
  gap?: number;
11
- /** top 插槽 shim 容器背景 */
10
+ /**
11
+ * 四向悬浮槽 shim 容器背景,**缺省即面色** `--el-bg-color`(akw76r)——
12
+ * 槽是 page 级 chrome **小块**,本就该浮在底上成一块面。传 `"transparent"` 可关。
13
+ * 🔴 与根 `background` 的分界:根是**容器**、恒缺省透明(容器一律不铺底)。
14
+ * [MUST NOT] 拿这几个去「对齐全局底色」补缝 —— 缝不是面,是真空。
15
+ */
12
16
  topBg?: string;
13
- /** bottom 插槽 shim 容器背景 */
17
+ /** bottom 插槽 shim 容器背景(缺省面色,见 {@link topBg}) */
14
18
  bottomBg?: string;
15
- /** left 插槽 shim 容器背景 */
19
+ /** left 插槽 shim 容器背景(缺省面色,见 {@link topBg}) */
16
20
  leftBg?: string;
17
- /** right 插槽 shim 容器背景 */
21
+ /** right 插槽 shim 容器背景(缺省面色,见 {@link topBg}) */
18
22
  rightBg?: string;
19
23
  /**
20
24
  * 各向插槽尺寸量测是否启用 ResizeObserver(逐槽透传对应 WatchSize 的
@@ -50,10 +54,10 @@ declare function __VLS_template(): {
50
54
  top?(_: {}): any;
51
55
  bottom?(_: {}): any;
52
56
  left?(_: {
53
- listLayoutRecommended: AppPageListLayoutRecommended;
57
+ listLayoutRecommended: import('./types').AppPageListLayoutRecommended;
54
58
  }): any;
55
59
  right?(_: {
56
- listLayoutRecommended: AppPageListLayoutRecommended;
60
+ listLayoutRecommended: import('./types').AppPageListLayoutRecommended;
57
61
  }): any;
58
62
  };
59
63
  refs: {
@@ -67,6 +71,10 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}
67
71
  fullViewport: boolean;
68
72
  fullMode: "min-height" | "height";
69
73
  nextViewportMaxHeight: string;
74
+ topBg: string;
75
+ bottomBg: string;
76
+ leftBg: string;
77
+ rightBg: string;
70
78
  topObserveResize: boolean;
71
79
  bottomObserveResize: boolean;
72
80
  leftObserveResize: boolean;