@done-coding/admin-core 0.29.1-alpha.0 → 0.30.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.
- package/es/bridge/badge.mjs +7 -1
- package/es/components/app-layout/AppPage.vue.mjs +1 -1
- package/es/components/app-layout/AppPage.vue2.mjs +15 -8
- package/es/components/app-layout/app-page-geometry.mjs +7 -3
- package/es/components/app-layout/use-slot-misuse-warn.mjs +40 -0
- package/es/components/data-view/DataGridView.vue.mjs +1 -1
- package/es/components/data-view/DataGridView.vue2.mjs +83 -32
- package/es/components/data-view/DataGridViewCard.vue.mjs +41 -0
- package/es/components/data-view/DataGridViewCard.vue2.mjs +4 -0
- package/es/components/data-view/InfiniteListView.vue.mjs +1 -1
- package/es/components/data-view/InfiniteListView.vue2.mjs +38 -11
- package/es/components/display/BadgeMark.vue.mjs +1 -1
- package/es/components/display/BadgeMark.vue2.mjs +31 -33
- package/es/components/display/use-badge.mjs +37 -33
- package/es/components/form/FormGroupTitle.vue.mjs +1 -1
- package/es/components/form/FormItem.vue.mjs +1 -1
- package/es/components/form/FormItemNestForm.vue.mjs +1 -1
- package/es/components/form/FormItemNestFormList.vue.mjs +1 -1
- package/es/components/form/FormTree.vue.mjs +5 -30
- package/es/components/form/FormTree.vue2.mjs +30 -1
- package/es/components/form/FormUpload.vue.mjs +1 -1
- package/es/components/modal/ModalPorter.vue.mjs +2 -0
- package/es/components/modal/use-slot-misuse-warn.mjs +22 -0
- package/es/components/page-layout/AppPageListDetailSplit.vue.mjs +1 -1
- package/es/components/page-layout/AppPageListDetailSplit.vue2.mjs +7 -7
- package/es/components/view-layout/ViewLayout.vue.mjs +1 -1
- package/es/components/view-layout/ViewLayout.vue2.mjs +71 -91
- package/es/components/view-layout/ViewLayoutToolbar.vue.mjs +7 -0
- package/es/components/view-layout/ViewLayoutToolbar.vue2.mjs +189 -0
- package/es/config/slot-region.mjs +23 -1
- package/es/hooks/use-custom-breakpoint.mjs +12 -2
- package/es/hooks/use-is-dev.mjs +16 -0
- package/es/hooks/use-theme-apply.mjs +2 -2
- package/es/index.mjs +90 -88
- package/es/style.css +138 -87
- package/package.json +2 -2
- package/src/bridge/docs/README.md +34 -4
- package/src/components/app-layout/docs/README-AppPage.md +68 -0
- package/src/components/data-view/docs/README-DataGridView.md +83 -1
- package/src/components/data-view/docs/README-DataListView.md +5 -2
- package/src/components/display/docs/README-BadgeMark.md +102 -41
- package/src/components/form/README.md +14 -0
- package/src/components/form/docs/README-FormItemNestForm.md +3 -0
- package/src/components/form/docs/README-FormItemNestFormList.md +3 -0
- package/src/components/form/docs/README-FormUpload.md +3 -0
- package/src/components/modal/docs/README-ModalPorter.md +26 -0
- package/src/components/page-layout/docs/README-AppPageListDetailLayout.md +1 -1
- package/src/components/view-layout/docs/README-ViewLayout.md +152 -9
- package/src/hooks/docs/README.md +37 -1
- package/types/bridge/badge.d.ts +38 -2
- package/types/components/app-layout/AppPage.vue.d.ts +16 -0
- package/types/components/app-layout/app-page-geometry.d.ts +26 -2
- package/types/components/app-layout/use-slot-misuse-warn.d.ts +19 -0
- package/types/components/data-view/DataGridView.vue.d.ts +9 -0
- package/types/components/data-view/DataGridViewCard.vue.d.ts +25 -0
- package/types/components/data-view/types.d.ts +63 -0
- package/types/components/display/BadgeMark.vue.d.ts +9 -8
- package/types/components/display/use-badge.d.ts +22 -14
- package/types/components/modal/use-slot-misuse-warn.d.ts +21 -0
- package/types/components/view-layout/ViewLayout.vue.d.ts +16 -0
- package/types/components/view-layout/ViewLayoutToolbar.vue.d.ts +52 -0
- package/types/components/view-layout/types.d.ts +56 -0
- package/types/config/slot-region.d.ts +12 -0
- package/types/hooks/use-custom-breakpoint.d.ts +1 -12
- package/types/hooks/use-is-dev.d.ts +24 -0
- package/types/injectInfo.json.d.ts +1 -1
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- **数据路径与 TableMain 不同构**:TableMain = 分页替换当前页;DataGridView = **存量累加**(滚动到底 append 下一页,直到 hasMore=false)。两者是两套数据状态机,故**不共存于同一组件内**——切换在 `ListLayout` 的 `viewMode` 层
|
|
13
13
|
- 泛型三参:`T` 行 / `SQ` 静态查询参数 / `F` 注入对象类型;`query` 变化时清空存量从第 1 页重新累加
|
|
14
14
|
- 内部结构:数据容器(本组件)→ InfiniteListView(IO 哨兵触发 + 虚拟化 + 底部三态)→ gridColumns 卡片网格
|
|
15
|
-
-
|
|
15
|
+
- **瀑布流本组件也有**(`masonry` + `estimateItemHeight` 同开同关):虚拟化与瀑布流**不是天然冲突**(早期文档那个措辞偏强),两者共存**需要一个前提** —— **高度可预先计算**。给了逐项估高即走「算高 → 装箱 → 虚拟化」;不给则保持等高行。与 `DataListView`(分页路径、直接量高、零额外输入)是两套机制,见下「瀑布流」段
|
|
16
16
|
|
|
17
17
|
## 快速上手(最小可用)
|
|
18
18
|
|
|
@@ -139,8 +139,90 @@
|
|
|
139
139
|
|
|
140
140
|
`refresh(silent?: boolean): Promise<unknown>`(清空存量重拉第 1 页)、`reload(silent?: boolean): Promise<unknown>`(回第 1 页重拉,同 refresh)、`getTableInstance(): TableInstance | undefined`(虚拟网格实例——选中/滚动能力对齐 TableInstance 子集,未实现方法为「not supported」抛错桩)。
|
|
141
141
|
|
|
142
|
+
## 瀑布流(`masonry` + `estimateItemHeight`,opt-in)
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<DataGridView
|
|
146
|
+
:api="api"
|
|
147
|
+
:grid-columns="4"
|
|
148
|
+
masonry
|
|
149
|
+
:estimate-item-height="(row, columnWidth) =>
|
|
150
|
+
columnWidth * (row.coverHeight / row.coverWidth) + 72"
|
|
151
|
+
>
|
|
152
|
+
<template #custom-view-item="{ row }"><SocialCard :row="row" /></template>
|
|
153
|
+
</DataGridView>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 🔴 为什么要 `estimateItemHeight`(不是啰嗦)
|
|
157
|
+
|
|
158
|
+
虚拟化与瀑布流各自的硬需求撞在一起:
|
|
159
|
+
|
|
160
|
+
| | 硬需求 |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| 虚拟列表 | **不渲染**也要知道每项的位置与总高(撑滚动条、算视口渲染谁) |
|
|
163
|
+
| 瀑布流 | 第 N 张的 y = 它所在列的**累计高度** ⇒ 取决于前面所有卡的高度 |
|
|
164
|
+
|
|
165
|
+
⇒ 循环是「要装箱就得知道高度 → 要虚拟化就不能渲染 → 不渲染就量不到高度」。
|
|
166
|
+
**这个循环能打破,只要高度是【算出来】的而不是【量出来】的**
|
|
167
|
+
(masonic / Pinterest 走的都是这条)。
|
|
168
|
+
`DataGridView` 是通用容器、不知道你插槽里塞了什么 ⇒ 这个「算」只能由**你**提供。
|
|
169
|
+
|
|
170
|
+
⚠️ 早期文档说两者「天然冲突」,那个措辞**偏强了** ——
|
|
171
|
+
准确说法是**需要「高度可预先计算」这一前提**。
|
|
172
|
+
|
|
173
|
+
### 同开同关,[MUST NOT] 半开
|
|
174
|
+
|
|
175
|
+
`masonry` 与 `estimateItemHeight` **两者都给**才生效;只给其一 ⇒ **保持等高行**
|
|
176
|
+
(与本能力引入前逐字一致)。
|
|
177
|
+
|
|
178
|
+
🔴 `gridColumns` 取 **`{ min }` 流体档时恒不生效** —— 那条的列数是浏览器算的、
|
|
179
|
+
JS 拿不到,而装箱必须先知道列数。用**断点表**或**定列数**。
|
|
180
|
+
|
|
181
|
+
### `columnWidth` 由组件回传
|
|
182
|
+
|
|
183
|
+
`estimateItemHeight(row, columnWidth)` 的第二参是组件算好的**列宽**
|
|
184
|
+
(`(容器内容宽 − (列数−1) × 间距) / 列数`)。
|
|
185
|
+
🔴 断点解析在组件内部,**你算不出来** ⇒ [MUST NOT] 自己去猜容器宽。
|
|
186
|
+
|
|
187
|
+
### 估不准会怎样([MUST] 知情)
|
|
188
|
+
|
|
189
|
+
估值只是**起点**:**渲染到的项会被量准**并替换掉估值
|
|
190
|
+
(virtual-core 的 `measureElement` → `itemSizeCache`)⇒ **不会长期留白**。
|
|
191
|
+
|
|
192
|
+
| | |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| ✅ 卡片**不会**在量准后跳到另一列 | 列归属按估值定下来就缓存住(`laneAssignmentMode: "estimate"`)——换列比一点留白刺眼得多 |
|
|
195
|
+
| ⚠️ 视口**下方**未渲染项的位置会轻微浮动 | 上方项被量准后,同列后续项的 y 跟着微调。这是「估算 + 校准」的**固有代价**,[MUST NOT] 期待零跳动 |
|
|
196
|
+
|
|
197
|
+
⚠️ **最容易估偏的不是图、是文字区**(标题 1~3 行浮动)——
|
|
198
|
+
消费方实测栽过:估值偏大 ⇒ 每张卡底下多一截空白。
|
|
199
|
+
封面给 `aspect-ratio` 能让图那部分在加载前就定高,文字区按**最常见行数**估即可。
|
|
200
|
+
|
|
201
|
+
### 顺序仍是**行优先**
|
|
202
|
+
|
|
203
|
+
前 N 项(N = 列数)按 `i % N` 铺满第一行 ⇒ 第 1、2、3 条就在第一行;
|
|
204
|
+
之后每项落**当前最矮的那一列**。
|
|
205
|
+
🔴 [MUST NOT] 期待 CSS `columns` 那种**竖向填充**(1,2,3 落第一列)——
|
|
206
|
+
按时间 / 热度排序的列表会被打乱。
|
|
207
|
+
|
|
208
|
+
### 与 `DataListView` 的瀑布流是两套机制
|
|
209
|
+
|
|
210
|
+
| | `DataGridView`(本组件,滚动路径) | `DataListView`(分页路径) |
|
|
211
|
+
| --- | --- | --- |
|
|
212
|
+
| 虚拟化 | **有** | 无 |
|
|
213
|
+
| 高度从哪来 | **消费方算**(`estimateItemHeight`)+ 渲染后校准 | **直接量**(RO 量卡片实际高) |
|
|
214
|
+
| 消费方额外输入 | 需要 | **零** |
|
|
215
|
+
|
|
216
|
+
⇒ 数据量不大、走分页就够的场景用 `DataListView` 更省事;
|
|
217
|
+
要「上拉加载 + 瀑布流」两者兼得才用本条。
|
|
218
|
+
|
|
142
219
|
## 反模式 / 注意
|
|
143
220
|
|
|
221
|
+
- **只给 `masonry` 不给 `estimateItemHeight`**:[MUST NOT] 指望它半开 —— 虚拟化路径缺了「不渲染也知道多高」这个前提就装不了箱,会保持等高行
|
|
222
|
+
- **`gridColumns` 用 `{ min }` 流体档 + 瀑布流**:[MUST NOT]——流体档的列数是浏览器算的、JS 拿不到,装箱必须先知道列数
|
|
223
|
+
- **在 `estimateItemHeight` 里自己猜容器宽**:[MUST NOT]——`columnWidth` 由组件回传,猜的必然与断点解析脱钩
|
|
224
|
+
- **期待瀑布流零跳动**:[MUST NOT]——视口下方未渲染项会随上方项被量准而轻微浮动,这是「估算 + 校准」的固有代价(换来的是不会长期留白)
|
|
225
|
+
|
|
144
226
|
- **非分页形态**:[MUST NOT] 期待分页器 / 页码跳转;数据到底即停,无"下一页"按钮
|
|
145
227
|
- **批次固定**:`pageSizeInit` 运行期不改;`pageSizeChange` 不触发
|
|
146
228
|
- **`currentPage` 语义**:`pageInfoChange` 的 `currentPage` = 已拉最大页,消费方勿当"当前展示第几页"
|
|
@@ -100,8 +100,11 @@ DOM 层级恒定 ⇒ 消费方写的 `:deep()` 选择器不会随 prop 变;瀑
|
|
|
100
100
|
|
|
101
101
|
- **需要 JS ⇒ 默认关**(规则:需要 JS 就默认不开)。
|
|
102
102
|
- 与 `gridColumns` 的 `{ min }` / 断点表都能叠。
|
|
103
|
-
- `DataGridView`
|
|
104
|
-
|
|
103
|
+
- **与 `DataGridView` 的瀑布流是两套机制**(都提供,但前提不同):
|
|
104
|
+
本条(分页路径)**没有虚拟化** ⇒ 可以**直接量**卡片实际高,消费方零额外输入;
|
|
105
|
+
`DataGridView`(滚动路径)是虚拟化的 ⇒ 必须**先算**,故要消费方给 `estimateItemHeight`。
|
|
106
|
+
⚠️ 两者**不是「一个能一个不能」** —— 早期文档说「天然冲突」措辞偏强,
|
|
107
|
+
准确说法是后者**需要「高度可预先计算」这一前提**。
|
|
105
108
|
- 量不到高度时每项退**兜底跨度**(CSS 给的),表现为「不够好看」而**不会重叠**——
|
|
106
109
|
增强失败 [MUST NOT] 变成布局坏掉。
|
|
107
110
|
|
|
@@ -46,11 +46,13 @@ bridge.badge.set("/order/list", { kind: "new" });
|
|
|
46
46
|
|
|
47
47
|
| 内置标 | 宽 | 逃逸 | 后果 |
|
|
48
48
|
| --- | --- | --- | --- |
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
49
|
+
| 新上线(3 字) | 50px | 37px | 右缘可能顶到水平相邻宿主 |
|
|
50
|
+
| 已更新 / 内测中(3 字) | 50px | 37px | 同上 |
|
|
51
|
+
| 开发中 / 规划中 / 待下线 / 未开通(3 字) | 50px | 37px | 同上 |
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
⚠️ **原「内置标文案 [MUST] ≤2 个汉字」那条约束已作废**(甲方 2026-09-19 明示「不用关心太长」)。
|
|
54
|
+
逃逸量最终由消费方的布局间距决定,core [MUST NOT] 声称能保证零重叠;文案长度的后果由布局承担。
|
|
55
|
+
[MUST NOT] 因为这张表回推「所以该用图标 / 该截断」—— 见下文「内置档一律文字」。
|
|
54
56
|
|
|
55
57
|
### 尺寸:固定 12px / 18px,不随 density 变
|
|
56
58
|
|
|
@@ -64,7 +66,7 @@ bridge.badge.set("/order/list", { kind: "new" });
|
|
|
64
66
|
|
|
65
67
|
⚠️ **不随主题 density 缩放**:core 的 `THEME_FIELD_SPECS` 只往 `:root` 挂变量且未映射任何 badge 变量,故 density 档(compact / cozy / comfortable)不改角标尺寸。这是刻意的——角标应恒为全局最小号,跟着 density 缩放可能小到不可读。消费方要改自行覆盖 `--el-badge-font-size` / `--el-badge-size`。
|
|
66
68
|
|
|
67
|
-
### ⚠️ 换英文**不能**减宽(已实测,[MUST NOT]
|
|
69
|
+
### ⚠️ 换英文**不能**减宽(已实测,[MUST NOT] 再试;本节留作依据备查)
|
|
68
70
|
|
|
69
71
|
12px 字号 + pill 内 padding 实测:
|
|
70
72
|
|
|
@@ -75,7 +77,9 @@ bridge.badge.set("/order/list", { kind: "new" });
|
|
|
75
77
|
| 内测 | 38 | BETA | 45 | **+7** |
|
|
76
78
|
| 开发中 | 50 | WIP | 36 | −14 |
|
|
77
79
|
|
|
78
|
-
汉字在小字号下表意密度远高于大写字母——除「开发中→WIP
|
|
80
|
+
汉字在小字号下表意密度远高于大写字母——除「开发中→WIP」外全部变宽。
|
|
81
|
+
⚠️ 原结论「压宽度的正确手段是减字数」**已不再适用**(甲方明示不用关心太长);
|
|
82
|
+
本节只保留一条仍然有效的结论:**[MUST NOT] 为「更短」而把内置文案改成英文**。
|
|
79
83
|
|
|
80
84
|
## API
|
|
81
85
|
|
|
@@ -102,53 +106,108 @@ bridge.badge.set("/order/list", { kind: "new" });
|
|
|
102
106
|
|
|
103
107
|
文案与语义色由 core 统一定(跨 app 一致);语义档解析到 `--el-color-*`,**主题风格 / 亮暗切换自动跟随**(规则 14,[MUST NOT] 写死色值)。
|
|
104
108
|
|
|
105
|
-
| kind |
|
|
109
|
+
| kind | 文案 | 语义色 | 为什么 |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `count` | **数字** | `danger` 红 | 待办数量要人行动,未读红点是全行业最强共识 |
|
|
112
|
+
| `new` | 新上线 | `success` 绿 | 正面资讯。[MUST NOT] 复用红——会与 `count` 撞色 |
|
|
113
|
+
| `updated` | 已更新 | `primary` 主色 | 中性被动通知,不喧宾夺主 |
|
|
114
|
+
| `beta` | 内测中 | `warning` 橙 | 能用但不稳定需谨慎 |
|
|
115
|
+
| `wip` | 开发中 | `info` 灰 | 尚不完善,中性、不引导点击。**仅提示,不拦点击** |
|
|
116
|
+
| `planned` | 规划中 | `info` 灰 | 还没开工,比 `wip` 更靠前。**不进遮挡档** |
|
|
117
|
+
| `deprecated` | 待下线 | `danger` 红 | 「要迁走」需要用户采取行动 |
|
|
118
|
+
| `locked` | 未开通 | `warning` 橙 | 权限边界,**唯一拦点击的档**;也是唯一**拒绝隐藏**的档 |
|
|
119
|
+
| `custom` | 自定义文案 | 缺省 `primary` | 内置档不够用时才用;`type` 只收语义档 |
|
|
120
|
+
| `anchor` | —— | —— | **伪标 / 纯锚点**:恒隐藏角标,只留 DOM 挂点供引导等消费方定位 |
|
|
121
|
+
|
|
122
|
+
### 🔴 内置档**一律文字**,[MUST NOT] 再改图标
|
|
123
|
+
|
|
124
|
+
甲方 2026-09-19(附菜单实拍图):**「badge 的图标撤掉,还是直接展示文字,两个字就是两个字、
|
|
125
|
+
三个字就是三个字(不用关心太长)」**。图标态在真实菜单里是一排看不懂的小圈。
|
|
126
|
+
|
|
127
|
+
**原「≤2 汉字」的宽度约束随之作废** —— 它当初的依据是「三字宽 50px、逃逸 36px 会压到水平
|
|
128
|
+
相邻宿主」,而甲方**明确说不用关心太长**。⇒ 实施方 [MUST NOT] 拿那条依据回推「所以还是该用
|
|
129
|
+
图标」,也 [MUST NOT] 自行折中(截断 / 省略号 / tooltip 代文字)—— 那是用没被授权的方案
|
|
130
|
+
绕过已下达的决定。逃逸的代价由消费方的布局间距承担,core 照旧不声称能保证零重叠。
|
|
131
|
+
|
|
132
|
+
⚠️ 这**不是「把 v0.30 revert 掉」**:状态四档(`wip` / `planned` / `deprecated` / `locked`)
|
|
133
|
+
在 ≤v0.29 时本来就是图标,本次是它们**首次**以文字出现。三档是新的一致态:
|
|
134
|
+
|
|
135
|
+
| 版本 | 资讯三档 `new`/`updated`/`beta` | 状态四档 `wip`/`planned`/`deprecated`/`locked` |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| ≤ v0.29 | 文字 | 图标 |
|
|
138
|
+
| v0.30 | 图标 | 图标 |
|
|
139
|
+
| **本版** | **文字** | **文字** |
|
|
140
|
+
|
|
141
|
+
图标是**全族退场**:`ElIcon` / `ElTooltip` / 预设的 `icon` 与 `iconScale` 字段一并删除。
|
|
142
|
+
悬浮气泡随之去掉 —— 它当初存在只为「语义随文字消失后补回来」,文字回来了它就是重复。
|
|
143
|
+
|
|
144
|
+
## 展示开关:逐条 `visible` + 全局默认
|
|
145
|
+
|
|
146
|
+
徽标当初是拿来做新手引导的;有了 `forge-plugin-guide` 之后,**徽标不再必须显示**。
|
|
147
|
+
|
|
148
|
+
| 层 | 写法 | 默认 |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| 逐条 | `bridge.badge.set(key, { kind: "new", visible: false })` | `undefined` = 跟随全局 |
|
|
151
|
+
| 全局 | `createCoreBridge({ badge: { defaultVisible: false } })` 或 `bridge.badge.setDefaultVisible(false)` | **`true`(展示)** |
|
|
152
|
+
|
|
153
|
+
**逐条显式值恒优先于全局默认**(与本仓所有 config-hook 同口径)。
|
|
154
|
+
全局默认取 `true` 是为了不改变现有下游观感;把它关成不展示是消费方的选择。
|
|
155
|
+
|
|
156
|
+
### 🔴 「配置了但不展示」与「没配置」是两回事
|
|
157
|
+
|
|
158
|
+
| | 注册表 | DOM | `data-dc-badge-key` | 引导能否定位 |
|
|
106
159
|
| --- | --- | --- | --- | --- |
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
| `updated` | 已更新 | `RefreshRight`(闭合**环** + 箭头) | `primary` 主色 | 中性被动通知,不喧宾夺主 |
|
|
110
|
-
| `beta` | 内测中 | `Opportunity`(钟形**灯泡**) | `warning` 橙 | 能用但不稳定需谨慎 |
|
|
111
|
-
| `wip` | 开发中 | `MoonNight`(**月牙**,`iconScale` 0.8) | `info` 灰 | 尚不完善,中性、不引导点击。**仅提示,不拦点击** |
|
|
112
|
-
| `planned` | 规划中 | `Calendar`(**方框 + 两耳**) | `info` 灰 | 还没开工,比 `wip` 更靠前。**不进遮挡档** |
|
|
113
|
-
| `deprecated` | 待下线 | `Warning`(**三角**) | `danger` 红 | 「要迁走」需要用户采取行动 |
|
|
114
|
-
| `locked` | 未开通 | `Lock`(**拱 + 方体**) | `warning` 橙 | 权限边界,**唯一拦点击的档** |
|
|
115
|
-
| `custom` | —— | 自定义文案 | 缺省 `primary` | 内置档不够用时才用;`type` 只收语义档 |
|
|
116
|
-
| `anchor` | —— | —— | —— | **伪标 / 纯锚点**:不渲染角标,只留 DOM 挂点供引导等消费方定位 |
|
|
160
|
+
| **没配置** | 无 entry | 渲染**裸插槽**(无 `ElBadge`) | ❌ 无 | ❌ |
|
|
161
|
+
| **配置了但不展示** | 有 entry | 渲染 `ElBadge`,角标被藏 | ✅ 在 | ✅ |
|
|
117
162
|
|
|
118
|
-
|
|
163
|
+
⇒ 隐藏 [MUST NOT] 用 `v-if` 掉整个 `ElBadge` —— 那会连挂点一起没掉,
|
|
164
|
+
`forge-plugin-guide` 当场失去锚点。**把徽标全部设为不展示时,引导仍能正常串出并定位。**
|
|
119
165
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
166
|
+
### 藏法 = 0 尺寸 + `overflow:hidden` + 事件屏蔽(甲方裁定)
|
|
167
|
+
|
|
168
|
+
```less
|
|
169
|
+
width: 0; height: 0; min-width: 0; padding: 0; border-width: 0;
|
|
170
|
+
overflow: hidden; pointer-events: none;
|
|
171
|
+
```
|
|
124
172
|
|
|
125
|
-
|
|
126
|
-
|
|
173
|
+
文字仍在 DOM 里但被裁掉,故 [MUST] 同时给 `aria-hidden="true"`(0 尺寸只是**视觉**裁掉,
|
|
174
|
+
读屏照念)。
|
|
127
175
|
|
|
128
|
-
|
|
176
|
+
🔴 **[MUST NOT] 用 `display: none`,也 [MUST NOT] 沿用 `ElBadge` 的 `hidden` prop**
|
|
177
|
+
(它内部就是把 sup 从布局里摘掉)。理由**不是**「保住锚点」—— 那是错的因果:
|
|
178
|
+
`data-dc-badge-key` 挂在 `ElBadge` **根元素**(包着宿主内容的 wrapper,有真实尺寸)上,
|
|
179
|
+
不是那个十几 px 的 pill,两种藏法都不断锚。
|
|
129
180
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
181
|
+
**真正的理由是「仍然占位可测」**:`display:none` 的元素 `getClientRects().length === 0`,
|
|
182
|
+
而这正是 `plugin-guide/src/anchor.ts` 的可见性判据(以及 `kq2m8w` 引导整页卡死 P0 的成因)
|
|
183
|
+
所依赖的那条线。保持「0 尺寸但仍在布局里」,可以让**任何**按 rect 判可见性的下游逻辑
|
|
184
|
+
不被隐藏态坑到。
|
|
133
185
|
|
|
134
|
-
|
|
135
|
-
- `new` **不取 `Star`** —— `wip` 的 `MoonNight` 自带小星星,12px 下会互相干扰;
|
|
136
|
-
- `beta` **不取 `Aim`(靶心)** —— 同心圆与 `Sunny` 同属「圆团」,七档里会出现两个团。
|
|
186
|
+
### 🔴 遮挡档(`locked`)**拒绝隐藏**
|
|
137
187
|
|
|
138
|
-
|
|
188
|
+
`BADGE_MASK_KINDS`(现仅 `locked`)**不接受隐藏**:逐条 `visible: false` 对它无效,
|
|
189
|
+
全局默认的「不展示」也不作用到它;dev 下会 `console.warn` 一句(静默忽略 = 消费方
|
|
190
|
+
以为设了其实没设)。
|
|
139
191
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
192
|
+
依据:**遮挡是功能不是资讯**。藏了会落进两种更坏的形态之一 ——
|
|
193
|
+
「隐藏了但仍然挡」⇒ 用户看到一个点不动又没有任何说明的按钮;
|
|
194
|
+
「隐藏了就不挡」⇒ 未开通的功能变成可点,功能性回退。
|
|
143
195
|
|
|
144
196
|
### `anchor`:只要挂点、不要角标
|
|
145
197
|
|
|
146
198
|
想给某个菜单项 / 按钮做新手引导,但它本身没有「新 / 更新」之类的标要显示时用它。
|
|
147
199
|
[MUST NOT] 为此硬挂一个 `new` —— 那会在界面上多出一个用户看不懂的角标。
|
|
148
200
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
201
|
+
**与 `visible: false` 的关系([MUST] 按此口径,[MUST NOT] 留两套说法):两者都保留,语义不同。**
|
|
202
|
+
|
|
203
|
+
| | 语义 | 藏法 |
|
|
204
|
+
| --- | --- | --- |
|
|
205
|
+
| `kind: "anchor"` | 「这里**本来就没有标**」—— `kind` 无资讯语义 + 恒隐藏 | 同一套 |
|
|
206
|
+
| `visible: false` | 「**有标**(new / beta / …),只是此刻不显示」 | 同一套 |
|
|
207
|
+
|
|
208
|
+
⇒ `anchor` **不是**新开关的语法糖:它已是发布契约,且语义是「无标」而非「有标先不显示」。
|
|
209
|
+
两者**共用同一套藏法**(0 尺寸 + `overflow:hidden` + 事件屏蔽),
|
|
210
|
+
[MUST NOT] 让它继续走 `ElBadge` 的 `hidden` —— 那就是「两种隐藏机制」。
|
|
152
211
|
|
|
153
212
|
## 遮挡:只有 `locked` 一档拦点击
|
|
154
213
|
|
|
@@ -169,16 +228,18 @@ v6odvs 起一并图标化,更安全。
|
|
|
169
228
|
- **包纯文字却用默认 `corner`**:角标会悬在文字上方并溢出相邻行
|
|
170
229
|
- **指望它做聚合**:本组件只按 key 查表渲染,core [MUST NOT] 把子级的标上卷到父级;聚合由 app 自算后往父级 key 写 entry
|
|
171
230
|
- **以为角标是实心底白字**:**已改镂空**(见下节)。旧描述作废,[MUST NOT] 照旧假设
|
|
231
|
+
- **以为内置档是图标 + 气泡**:v0.30 那一版的形态,**本版已全档改回文字**,气泡随之去掉
|
|
232
|
+
- **拿 `v-if` 藏徽标**:会连 `data-dc-badge-key` 挂点一起没掉,引导插件当场定位不到;隐藏一律走 `visible`
|
|
233
|
+
- **给 `locked` 配 `visible: false`**:**无效**(遮挡是功能不是资讯),dev 下有 `console.warn`
|
|
172
234
|
|
|
173
235
|
## 镂空(outline)观感
|
|
174
236
|
|
|
175
|
-
角标走**镂空**:浅底 + 语义色 1px 边框 +
|
|
237
|
+
角标走**镂空**:浅底 + 语义色 1px 边框 + 语义色文字(原先是语义色实心底 + 白字)。
|
|
176
238
|
|
|
177
239
|
- 落点在 `use-theme-apply` 的 `EP_STRUCTURAL_CSS`(与其余 EP 结构性修正同源)——
|
|
178
240
|
角标由 `ElBadge` 渲染在它自己的 DOM 上,[MUST NOT] 在 `BadgeMark.vue` 写 scoped(打不中)。
|
|
179
241
|
- 颜色全走 `--el-color-<档>` / `--dc-core-*` 双源(规则 14),[MUST NOT] 写死。
|
|
180
242
|
- **底不是纯透明**:角标叠在宿主内容上,全透会透出下面的字 ⇒ 用 `--el-bg-color`(面色)打底。
|
|
181
|
-
- 图标档随之从白色变**语义色线条**(`currentColor`)。
|
|
182
243
|
|
|
183
244
|
要退回实心:在消费方侧覆盖 `.el-badge__content`(`background-color` / `color` / `border`)。
|
|
184
245
|
[MUST NOT] 指望有 prop 开关 —— 观感是全局一致口径,不做成逐处可调。
|
|
@@ -186,5 +247,5 @@ v6odvs 起一并图标化,更安全。
|
|
|
186
247
|
## 关联
|
|
187
248
|
|
|
188
249
|
- 数据真源:`bridge.badge`(`bridge/badge.ts`)
|
|
189
|
-
- 内部 hook:`use-badge`(同级、不出桶;查表 + `expireAt` 判定 + 全 app 共享 tick)
|
|
250
|
+
- 内部 hook:`use-badge`(同级、不出桶;查表 + `expireAt` 判定 + 展示开关解析 + 全 app 共享 tick)
|
|
190
251
|
- 已接入的生产驱动点:`MenuTree` / `MenuItemSub` / `AppHeader` 模块条 / `ActionBtn`
|
|
@@ -20,6 +20,20 @@
|
|
|
20
20
|
- [FormUploadAudio](./docs/README-FormUploadAudio.md) — 音频上传(薄封装:accept audio/* + 内联 `<audio controls>` 播放条即时试听;**不建弹窗**——业界音频无 lightbox 形态)
|
|
21
21
|
- FormRadioGroup / FormTree / FormVerifyImage / FormVerifyCode — 文档待补
|
|
22
22
|
|
|
23
|
+
## 表单项内容的宽度约定(族级硬约定)
|
|
24
|
+
|
|
25
|
+
EP 的 `.el-form-item__content` 是 **flex 容器**(`display:flex; flex-wrap:wrap; flex:1; align-items:center; min-width:0`)。
|
|
26
|
+
⇒ **凡要当表单项内容渲染的组件,其根盒 [MUST] 显式声明宽度行为**,
|
|
27
|
+
[MUST NOT] 依赖「块级元素会自己撑满」——**那条规则在 flex 容器里不成立**,
|
|
28
|
+
根盒会按 `flex:0 1 auto` 收缩到内容宽。
|
|
29
|
+
|
|
30
|
+
- 想占整行 → 根盒 `width:100%` + **`min-width:0`**(flex item 的 `min-width` 默认 `auto`,长文本 / 宽表格会把它顶出容器,两条 [MUST] 成对给)
|
|
31
|
+
- 想按内容宽(chip / 按钮组 / 单选组)→ 显式 `display:inline-flex` 之类,**也算声明**
|
|
32
|
+
- 本族现状:`FormItemNestForm` / `FormItemNestFormList` / `FormUpload` / `FormGroupTitle` / `FormTree` 走 `width:100%`;
|
|
33
|
+
`FormRadioGroup`(EP `inline-flex`)、`FormSortSwitch(Group)` 按内容宽;`FormInput` / `FormSelect` / `FormDivider` 由 EP 自带 `width:100%`
|
|
34
|
+
- ⚠️ **内容宽的地方坏了也看不出来**:一行里只要有个 `el-input`,收缩的根盒照样撑满——
|
|
35
|
+
自查 / 守卫 [MUST] 用**窄内容**样例(如 148px 方卡)
|
|
36
|
+
|
|
23
37
|
## 跨族依赖
|
|
24
38
|
|
|
25
39
|
- form 族 → display 族(单向):`FormSort` 依赖 `ShadowClone`(分身机制)、`FormActiveFilter` 依赖 `ActionBtn`(清除全部按钮)
|
|
@@ -75,6 +75,9 @@ const data = ref(generateFormData(list));
|
|
|
75
75
|
|
|
76
76
|
## 反模式 / 注意
|
|
77
77
|
|
|
78
|
+
- **根盒恒占整行**(`width:100%` + `min-width:0`):它落在 EP 的 `.el-form-item__content` flex 容器里,不显式给宽会收缩到内容宽 —— 行内只有窄内容(如 148px 方卡)时右边会留一大片空白。[MUST NOT] 在消费方用 `wrapProps` 补 `width:100%`(已由组件自带)
|
|
79
|
+
|
|
80
|
+
|
|
78
81
|
- **手写嵌套子表单配置**:直接手写 render 挂子 FormMain 会绕过注册机制,级联校验断裂——[MUST] 用 `nestFormItem` / 本组件
|
|
79
82
|
- **nestKey 与父 key 不一致**:nestKey 是父 item key,注册索引对不上则级联失效
|
|
80
83
|
- **shadowColor 显式传才内联覆盖**:默认零投影(去框化,层级靠背景明度差);除非确需跨主题固定投影,别传
|
|
@@ -86,6 +86,9 @@ const data = ref(generateFormData(list));
|
|
|
86
86
|
|
|
87
87
|
## 反模式 / 注意
|
|
88
88
|
|
|
89
|
+
- **根盒恒占整行**(`width:100%` + `min-width:0`):它落在 EP 的 `.el-form-item__content` flex 容器里,不显式给宽会收缩到内容宽 —— 行内只有窄内容(如 148px 方卡)时右边会留一大片空白。[MUST NOT] 在消费方用 `wrapProps` 补 `width:100%`(已由组件自带)
|
|
90
|
+
|
|
91
|
+
|
|
89
92
|
- **手写行子表单配置**:绕过注册机制则多行级联校验断裂——[MUST] 用 `nestFormItemList` / 本组件
|
|
90
93
|
- **只靠 min / max 交互约束**:禁删 / 禁新增是交互层防线,长度硬校验走父 item `rules`——需要提交时兜底校验(如绕过 UI 直接改数据)必须配 `rules`
|
|
91
94
|
- **headerRender / footerRender 是 render-fn 不是 slot**:在配置内以函数形式提供,不能当插槽模板使用
|
|
@@ -169,6 +169,9 @@ EP 以 `uid` 作 key ⇒ **重复 key**、列表渲染紊乱(实测 DOM 闪出
|
|
|
169
169
|
|
|
170
170
|
## 反模式 / 注意
|
|
171
171
|
|
|
172
|
+
- **根盒恒占整行**(`width:100%` + `min-width:0`):作 `.el-form-item__content` 的 flex item,不显式给宽会收缩到内容宽 —— picture-card 形态下卡片网格的可用宽会随卡片数变化、拖拽区也跟着缩
|
|
173
|
+
|
|
174
|
+
|
|
172
175
|
- **值 = URL 不是 File**:`modelValue` 语义是「已上传文件的 URL」——外部塞 File 对象进值是错误用法(表单 stringify 提交的是 URL);上传动作经 `uploadFn` 发生
|
|
173
176
|
- **`loadingKey` 不传则上传中不拦提交**:裸用可接受(无 FormMain 环境);FormMain 配置化场景忘传 = 上传中能提交(半成品 URL 进库)——配置时 [MUST] 传
|
|
174
177
|
- **进度不自造**:ElUpload 内置进度条(on-progress → percent → 自动显示)——「把 onprogress 数据传给 getProgress 方法」是自造轮子;只有自定义进度呈现才需要自己拿 `event.percent`
|
|
@@ -83,6 +83,30 @@ AppLayout 在调用内置 app 级 ModalShelf 的**子元素最前**位置调用
|
|
|
83
83
|
|
|
84
84
|
架子把 `{show, closeFn, removeFn, payload: P}` 注入弹窗组件(见 ModalShelf 文档)。
|
|
85
85
|
|
|
86
|
+
### 放进 `AppPage` 方位槽 ⇒ 挂载时一条指名道姓的 `console.error`
|
|
87
|
+
|
|
88
|
+
`AppPage` 的 `#top` / `#bottom` / `#left` / `#right` 是**方位槽**(fixed 悬浮区),
|
|
89
|
+
它们是页面内置 `ModalShelf` 的**兄弟节点**而不是后代 ⇒ `provide` 够不到,
|
|
90
|
+
`evoke()` 恒返回 `null`(表现为「点按钮毫无反应」,不报错也不崩)。
|
|
91
|
+
|
|
92
|
+
Porter 挂载时读直系父的 `data-dc-slot`,命中方位槽即报:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
[ModalPorter] 它被放在 AppPage 的 #top 槽里 —— 方位槽是 ModalShelf 的兄弟节点,
|
|
96
|
+
provide 够不到,evoke() 恒为 null(表现为「点按钮毫无反应」,不报错不崩)。
|
|
97
|
+
[MUST] 移到**默认槽**(page 架子就住在默认槽里;与 AppPage 并列同样够不到)。
|
|
98
|
+
Porter 自身零 DOM 输出,放默认槽里不占任何版面。
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- **方位槽里放 Porter 没有任何正当用法** ⇒ 这条恒报(不限 dev),且零误报
|
|
102
|
+
- 默认槽 / 脱离 `AppPage` 单用 ⇒ **零输出**
|
|
103
|
+
- 🔴 出口是**默认槽**,[MUST NOT] 顺手改成「与 `AppPage` 并列」—— page 架子
|
|
104
|
+
(`ModalShelf level="page"`)就住在 AppPage 的默认槽里,并列**同样够不到**,
|
|
105
|
+
`evoke()` 一样恒为 null。「与 AppPage 并列」只对**不需要 page 架子**的东西成立
|
|
106
|
+
(声明式挂载、自己 teleport 走的弹窗组件)
|
|
107
|
+
- 「弹窗经弹层挂载、不占版面 ⇒ 放哪都行」是**只对了一半**的推理:弹窗自身 teleport 走了,
|
|
108
|
+
但**槽本身**照样渲染(fixed + padding + 背景),白占一条高度 —— 见 `AppPage` 文档的空壳槽检测
|
|
109
|
+
|
|
86
110
|
## 反模式 / 注意
|
|
87
111
|
|
|
88
112
|
- **未注册 key 是静默失败面**:`evoke` 返回 null + console.error——消费侧 [MUST] 判空(`evoke(...)?.close()` 或先判 null),别假设一定成功
|
|
@@ -90,6 +114,8 @@ AppLayout 在调用内置 app 级 ModalShelf 的**子元素最前**位置调用
|
|
|
90
114
|
- **key 类型必须声明合并**:evoke 的 key 泛型约束 `keyof ModalShelfMap`——业务 [MUST] 在 `.d.ts` 里 declare module 扩展 `ModalShelfMap`(见 ModalShelf 文档),否则 key 不在 map 上 TS 直接拦截
|
|
91
115
|
- **自动清理别补偿**:Porter 失活/卸载已自动 remove 全部 nsKey,手写 `onUnmounted` 清理是重复劳动(也可能提前清掉活跃弹窗)
|
|
92
116
|
- **页面级 Porter [MUST NOT] 配 `shelf:"app"` 条目**:页面卸载后弹窗仍挂在常驻 app 架子上 → 孤儿弹窗(唤起句柄已死、只剩弹窗自身 closeFn);app 级弹窗唯一正道 = AppLayout `globalModalConfig` + 内部专用 Porter + `globalModalRemote` 遥控
|
|
117
|
+
- **[MUST NOT] 把 Porter 放进 `AppPage` 的 `#top`/`#bottom`/`#left`/`#right`**:方位槽够不到 page 架子(`evoke()` 恒 null),且槽本身白占版面;挂载时会有一条点名报错。出口是**默认槽**(Porter 零 DOM 输出,放那里不占任何版面)——
|
|
118
|
+
[MUST NOT] 改成「与 `AppPage` 并列」,page 架子就在默认槽里、并列同样够不到
|
|
93
119
|
- **弹窗 UI 不在 Porter 插槽里写**:Porter 只是注册表宿主,弹窗经 evoke 落在架子上渲染——插槽只放页面内容
|
|
94
120
|
|
|
95
121
|
## 关联
|
|
@@ -99,7 +99,7 @@ watch(isOutOfSync, (v) => v && refreshList());
|
|
|
99
99
|
| ----------------- | ------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
100
|
| `mode` | `"auto" \| "split" \| "sheet"` | `"auto"` | 形态;`auto` 按视口宽 `splitAt` 定档 |
|
|
101
101
|
| `splitAt` | `number` | `1200` | `auto` 档切档阈值(px,**视口宽**)。iPad 竖屏 768 / Air 横屏 1180 落 sheet;Pro 12.9″ 横屏 1366 落 split |
|
|
102
|
-
| `listWidth` | `string` | `clamp(280px, 28%, 420px)` |
|
|
102
|
+
| `listWidth` | `string` | `clamp(280px, 28%, 420px)` | 分栏态列表列宽。**落在 `AppPage` 的 `#left` 槽上**(不是列表盒)⇒ 百分比按**视口**解析、基数稳定不循环。定值 / 百分比 / clamp 三种写法都不产生死白(实测死白恒 = shim padding)。[MUST NOT] 把它搬回列表盒 —— 槽是 shrink-to-fit,百分比在那一层没有基数(vr8kd2) |
|
|
103
103
|
| `autoActiveFirst` | `boolean` | 跟随形态 | 列表出数后自动激活第一条。**split 默认 `true`**(宽屏右半屏不该空着)、**sheet 默认 `false`**(窄屏自动弹抽屉是打扰)。⚠️ 详情若有副作用(如「打开即标记已读」)[MUST] 显式关掉 |
|
|
104
104
|
| `queryKey` | `string` | 无 | 传了才把 `activeId` 同步进 URL query(**`replace` 不 `push`**)。复原仍走不变式校验 |
|
|
105
105
|
| `detailApi` | `(params?: { id }) => Promise<R>` | 无 | 传了则内置 `ViewLayout`(零配置拿到内置导航 + `refreshToken` 接线);不传则 `#detail` 原样渲染、门面零请求 |
|