@done-coding/admin-core 0.24.0 → 0.25.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 +27 -0
- package/es/bridge/index.mjs +4 -0
- package/es/components/app-layout/AppHeader.vue.mjs +1 -1
- package/es/components/app-layout/AppHeader.vue2.mjs +13 -3
- package/es/components/app-layout/AppLayout.vue.mjs +1 -1
- package/es/components/app-layout/AppLayout.vue2.mjs +6 -2
- package/es/components/display/ActionBtn.vue.mjs +21 -3
- package/es/components/display/BadgeMark.vue.mjs +7 -0
- package/es/components/display/BadgeMark.vue2.mjs +31 -0
- package/es/components/display/TabsHeader.vue.mjs +1 -1
- package/es/components/display/TabsHeader.vue2.mjs +16 -7
- package/es/components/display/TabsTile.vue.mjs +1 -1
- package/es/components/display/TabsTile.vue2.mjs +27 -7
- package/es/components/display/use-badge.mjs +62 -0
- package/es/components/menu/MenuItemSub.vue.mjs +22 -3
- package/es/components/menu/MenuTree.vue.mjs +34 -8
- package/es/index.mjs +62 -60
- package/es/store/app.mjs +16 -4
- package/es/style.css +61 -46
- package/package.json +2 -2
- package/src/bridge/README.md +1 -1
- package/src/bridge/docs/README.md +98 -2
- package/src/components/app-layout/README.md +1 -1
- package/src/components/app-layout/docs/README-AppHeader.md +8 -0
- package/src/components/app-layout/docs/README-AppLayout.md +24 -0
- package/src/components/display/README.md +7 -4
- package/src/components/display/docs/README-ActionBtn.md +1 -0
- package/src/components/display/docs/README-BadgeMark.md +125 -0
- package/src/components/display/docs/README-TabsMain.md +20 -1
- package/src/components/menu/README.md +8 -0
- package/src/components/menu/docs/README-MenuItemSub.md +4 -0
- package/src/components/menu/docs/README-MenuTree.md +10 -0
- package/types/bridge/badge.d.ts +112 -0
- package/types/bridge/index.d.ts +18 -0
- package/types/components/display/BadgeMark.vue.d.ts +57 -0
- package/types/components/display/index.d.ts +7 -0
- package/types/components/display/types.d.ts +18 -0
- package/types/components/display/use-badge.d.ts +19 -0
- package/types/injectInfo.json.d.ts +1 -1
- package/types/store/app.d.ts +22 -1
- package/types/types/route.d.ts +20 -0
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
- 中间区 = 路由大模块菜单(menus 渲染,点击高亮 activeModulePath)
|
|
11
11
|
- 整体显隐由 AppLayout 间接控制(store headerStyle.height=0 视觉收起 + dom 保留)
|
|
12
12
|
|
|
13
|
+
## 模块条徽标(badge)
|
|
14
|
+
|
|
15
|
+
模块条的每一项以其路由 **`path` 为 key** 查 `bridge.badge` 注册表,命中则渲染绝对定位角标(数量 / 新上线 / 更新 / 内测 / 开发 / 自定义),**不压字、不改变宿主尺寸**;未命中零影响、无需在菜单数据里加字段。
|
|
16
|
+
|
|
17
|
+
与 AppSidebar 菜单树同一份注册表、同一套 key 规则——同一个 `path` 在两处显示同一个标。
|
|
18
|
+
|
|
19
|
+
> AppHeader 的模块条是**自建实现**(扁平一级 + 横向滚动 + `router.push` 切模块),不走 `MenuTree`;徽标是两处各自接入的,行为对齐。契约见 [bridge 文档 · 徽标注册表](../../../bridge/docs/README.md)。
|
|
20
|
+
|
|
13
21
|
## 快速上手(最小可用)
|
|
14
22
|
|
|
15
23
|
经 AppLayout 内容锚填充后由 shell 落位(App.vue 范式——#brand/#userEntry 即 Header #left/#right 的内容来源):
|
|
@@ -32,8 +32,32 @@ App.vue 总装配——三个必填注入 + 两个必填内容锚即完整可跑
|
|
|
32
32
|
- `watermarkContent`:默认不展示水印;仅需要页面水印时填内容(空数组/空串/undefined 即关闭;显隐走 content 切换,无独立开关 prop)。
|
|
33
33
|
- `sidebarAboveHeader`:默认 `false` 即够用;仅当 sidebar 需要 z-index 盖住 header 悬浮时开。
|
|
34
34
|
- **布局形态深度定制**:内置默认经典布局 shell 已可用(行为基线 [MUST NOT] 借重构改);仅当落位/折叠策略不满足时才走布局插件自供 shell(AppShell 契约)——AppLayout 本身不动。
|
|
35
|
+
- **单页全屏 / 关动画**(路由级,非 prop):走 `meta.layout` 两个可选项——见下「路由级布局开关」。
|
|
35
36
|
- **完整能力演示**(五锚全填 + 水印 + 自供 shell):`apps/reference/src/App.vue`、`apps/reference/src/pages/app-layout/shell/`——能力展示,非最小起点。
|
|
36
37
|
|
|
38
|
+
## 路由级布局开关(`meta.layout`)
|
|
39
|
+
|
|
40
|
+
除既有的 `header` / `sidebar` / `aside` / `footer` / `breadcrumb` / `bodyShimPadding`,还有两个整页形态开关:
|
|
41
|
+
|
|
42
|
+
| 选项 | 默认 | 语义 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `noViewportPadding` | `false` | 全屏展示:viewport 四边套框留白归零 |
|
|
45
|
+
| `noAnimation` | `false` | 进入本路由不做布局过渡动画(登录页 / 全屏页) |
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
{ path: "fullscreen", meta: { title: "全屏页", layout: { noViewportPadding: true, noAnimation: true } } }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**`noViewportPadding` 归零的是两条链,且同源**:① core 内部几何(header/sidebar/aside/footer/body 的 fixed 锚点与 minHeight);② 对外 `--dc-core-viewport-*` CSS var(浮层 / 主题 decor / 业务页 CSS 消费)。二者都读 store 的 `viewportConfigEffective`,[MUST NOT] 让任一条回退成直读 bridge——只归零一边就是半吊子全屏(内容贴边了,读 CSS var 的浮层仍按套框内缘定位)。
|
|
52
|
+
|
|
53
|
+
配套:主题的套框装饰(`useFrameGeometry` → `hasFrame`)随之整层不渲染。
|
|
54
|
+
|
|
55
|
+
**只管最外圈套框留白**,不影响模块间距 `gap` —— 该页若仍开着 header/sidebar,模块之间的内缝照旧。
|
|
56
|
+
|
|
57
|
+
**`noAnimation`** 挂 `app-layout_noAnimation` 根类,与既有的「未登录关动画」`app-layout_noLogin` **并列**(互不替代,共用同一份 `transition: none` 规则)。
|
|
58
|
+
|
|
59
|
+
> 范式页:`apps/reference/src/pages/app-layout/viewport-fullscreen/`——同一个组件挂两条路由、只差 meta,来回切即对照。
|
|
60
|
+
|
|
37
61
|
## API
|
|
38
62
|
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
39
63
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# display 展示族
|
|
2
2
|
|
|
3
|
-
展示与交互原语:TabsMain(Tab 容器)/ ActionBtn 系列(确认闸门 + 自动 loading
|
|
3
|
+
展示与交互原语:TabsMain(Tab 容器)/ ActionBtn 系列(确认闸门 + 自动 loading + 徽标)/ 尺寸与高度工具(WatchSize / HeightProvider)/ BooleanTag / ShadowClone(影分身双渲染位)。
|
|
4
4
|
|
|
5
5
|
## 组件文档(docs/)
|
|
6
6
|
|
|
7
|
-
- [TabsMain](./docs/README-TabsMain.md) — Tab 容器(channel 嵌套 / tile 平铺 / query
|
|
8
|
-
- [ActionBtn](./docs/README-ActionBtn.md) — ElButton 超集(confirm 闸门 + beforeOpen + 自动 loading
|
|
7
|
+
- [TabsMain](./docs/README-TabsMain.md) — Tab 容器(channel 嵌套 / tile 平铺 / query 同步 / item badgeKey 徽标)
|
|
8
|
+
- [ActionBtn](./docs/README-ActionBtn.md) — ElButton 超集(confirm 闸门 + beforeOpen + 自动 loading + badgeKey 徽标)
|
|
9
|
+
- [BadgeMark](./docs/README-BadgeMark.md) — 徽标标记(按 key 查 bridge.badge 注册表挂绝对定位角标)
|
|
9
10
|
- [ActionBtnGroup](./docs/README-ActionBtnGroup.md) — 配置式按钮组(渲染三态)
|
|
10
11
|
- [ActionConfirm](./docs/README-ActionConfirm.md) — 确认原语(扁平,submitFn 返 Promise 才关)
|
|
11
12
|
- [WatchSize](./docs/README-WatchSize.md) — 尺寸监听容器(mode / observeResize / debounce)
|
|
@@ -16,7 +17,9 @@
|
|
|
16
17
|
## 关键类型
|
|
17
18
|
|
|
18
19
|
- `TabsMainProps` / `TabsMainItem` / `ActionBtnProps` / `ActionBtnConfirmConfig` / `ActionBtnConfigItem` / `ActionBtnGroupProps` / `ActionConfirmProps` / `BooleanTagProps` / `ShadowCloneProps`(经 `./types` 导出)
|
|
19
|
-
- 内部件不导出:`TabsHeader` / `TabsTile` / `TabsRefineFlow` / `TabsNaturalFlow` / `use-tabs-query-sync` / `use-shadow-target`
|
|
20
|
+
- 内部件不导出:`TabsHeader` / `TabsTile` / `TabsRefineFlow` / `TabsNaturalFlow` / `use-tabs-query-sync` / `use-shadow-target` / `use-badge`
|
|
21
|
+
|
|
22
|
+
> **徽标(BadgeMark)**:按 key 查 `bridge.badge` 注册表渲染绝对定位角标,供 ActionBtn 与菜单族共用;**跨桶消费故公开导出**(同 WatchSize 被 app-layout 消费的先例),自供 shell 自渲染导航时也可直接用。完整契约(标记类型 / 失效路径 / key 约定 / 已知限制)见 [bridge 文档 · 徽标注册表](../../bridge/docs/README.md)。`use-badge` 收纳查表 + `expireAt` 判定 + 共享 tick,**不出桶**(规则 21/22,组件同级)。
|
|
20
23
|
|
|
21
24
|
## 跨族依赖(被依赖)
|
|
22
25
|
|
|
@@ -48,6 +48,7 @@ const handleDelete = async () => {
|
|
|
48
48
|
| `loading` | ❌ 被 Omit | — | 对外不可设置,内部独占 innerLoading(对接异步 confirm / beforeOpen / click handler) |
|
|
49
49
|
| `confirmMode` | `ActionConfirmMode?` | `"popconfirm"` | "popconfirm"\|"messageConfirm"(后者内嵌 ConfirmModal 居中模态) |
|
|
50
50
|
| `confirmConfig` | `ActionBtnConfirmConfig \| ((data: T) => ActionBtnConfirmConfig)?` | 无=普通按钮 | 确认闸门;函数形态入参 data 来自 beforeOpen 返回值 |
|
|
51
|
+
| `badgeKey` | `string?` | 无=不显徽标 | 徽标键;命中 `bridge.badge` 注册表则在按钮内容右侧渲染绝对定位角标(不压字,且**不改变按钮尺寸**)。按钮无 path,[MUST] 显式给键。契约见 [bridge 文档 · 徽标注册表](../../../bridge/docs/README.md) |
|
|
51
52
|
| `beforeOpen` | `() => Promise<T>?` | — | 点击后、弹层前异步预取;reject 静默退出(不弹层不触发 click,调用方 [MUST] 内部 catch 反馈);期间按钮 loading。⚠️ confirmConfig/beforeOpen 均会消耗瞬时用户激活窗口,之后 handler [MUST NOT] 调 window.open/navigator.clipboard 等 |
|
|
52
53
|
|
|
53
54
|
`ActionBtnConfirmConfig`:`title?` / `content?`(`ActionConfirmTextResolver` = string | (() => string | VNode[]))、`confirmText?`(默认「确定」)、`cancelText?`(默认「取消」)、`type?: ButtonProps["type"]`
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# BadgeMark(徽标标记)
|
|
2
|
+
|
|
3
|
+
> 按 `badgeKey` 查 `bridge.badge` 注册表,命中且未过期则给默认插槽内容挂**绝对定位角标**(包 ElBadge,不挤压主体);未命中渲染裸插槽、DOM 逐字节不变。
|
|
4
|
+
> 范式页:`apps/reference/src/pages/integration/badge/`(7 场景);生产驱动点见 MenuTree / MenuItemSub / AppHeader / ActionBtn 内部接入。
|
|
5
|
+
|
|
6
|
+
## 定位
|
|
7
|
+
|
|
8
|
+
- **何时用**:自供 shell 自渲染导航、或业务自建的入口元素需要接徽标体系时
|
|
9
|
+
- **何时不用**:core 的菜单族与 `ActionBtn` **已内置接入**,用它们时 [MUST NOT] 再自行包一层
|
|
10
|
+
- **opt-in 且零侵入**:未 `app.use(bridge)` / key 未命中 / 标已过期 → 静默渲染裸插槽,[MUST NOT] 抛错
|
|
11
|
+
- 完整契约(标记类型 / `expireAt` 失效 / key 约定 / 已知限制 / 为何不做冒泡聚合)见 [bridge 文档 · 徽标注册表](../../../bridge/docs/README.md)
|
|
12
|
+
|
|
13
|
+
## 快速上手(最小可用)
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<BadgeMark badgeKey="/order/list">
|
|
17
|
+
<span>订单列表</span>
|
|
18
|
+
</BadgeMark>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
app 侧往注册表写一条即出标:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
bridge.badge.set("/order/list", { kind: "new" });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 能力边界 / 按需使用
|
|
28
|
+
|
|
29
|
+
- **`align` 怎么选:看被包内容盒是否紧贴文字**(不是看宿主类型)。
|
|
30
|
+
|
|
31
|
+
| 宿主 | `.el-badge` 盒高 | 宿主高 | `corner` 落点 | core 用 |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `ActionBtn` | 14px(紧贴文字行) | 32px | 角标顶齐按钮顶,上溢 **0** | **`corner`** 右上角 |
|
|
34
|
+
| `TabsHeader` tab | 22px(紧贴) | 40px | 上溢 **−1**,不越界 | **`corner`** 右上角 |
|
|
35
|
+
| 菜单项 / AppHeader 模块条 / tile 卡片头 | **50px(撑满整行)** | 50px | 钉在**行顶**、上溢 **9px** 侵入上一行 | **`middle`** 垂直居中 |
|
|
36
|
+
|
|
37
|
+
差别根源:菜单项的 `span` 继承整行 `line-height`,`.el-badge` 盒跟着撑满一行,于是「内容顶边」≠「文字顶边」;按钮 / tab 的盒紧贴文字行,顶边即文字顶边。
|
|
38
|
+
|
|
39
|
+
- **[MUST NOT] 用 `top: N%` 或 ElBadge `offset` 魔数去凑**:与字号 / 行高 / 主题 density 强耦合,一变就错位。盒紧贴文字的宿主用 `corner`,盒撑满行的用 `middle`。
|
|
40
|
+
|
|
41
|
+
### 硬约束:角标的出现 [MUST NOT] 改变宿主尺寸
|
|
42
|
+
|
|
43
|
+
两档均为**绝对定位**,不占布局位。实测证据:同一批按钮打标前后逐项宽度差 **全 0**。
|
|
44
|
+
|
|
45
|
+
代价是绝对定位不占位 → 角标必然向右**逃逸出宿主边界**,逃逸量 = `角标宽 − 13px`(EP 的 10px 内嵌已归零,另留 2px 让位;文字距按钮右缘 15px padding):
|
|
46
|
+
|
|
47
|
+
| 内置标 | 宽 | 逃逸 | 后果 |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| 新 | 26px | 13px | 落在间距里 |
|
|
50
|
+
| 更新 / 内测 | 38px | 25px | 压进邻居 padding,**不压邻居文字** |
|
|
51
|
+
| ~~开发中(3 字)~~ | ~~50px~~ | ~~37px~~ | ~~右缘顶到下一个按钮~~ → 已改「开发」 |
|
|
52
|
+
|
|
53
|
+
**故内置标文案 [MUST] ≤ 2 个汉字。** 逃逸量最终由消费方的布局间距决定,core [MUST NOT] 声称能保证零重叠;`custom` 传长文本的后果由消费方自负。
|
|
54
|
+
|
|
55
|
+
### 尺寸:固定 12px / 18px,不随 density 变
|
|
56
|
+
|
|
57
|
+
| 项 | 值 | 来源 |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| 字号 | **12px** | `--el-badge-font-size`(EP **组件级**变量,定义在 `.el-badge` 上,非 `:root`) |
|
|
60
|
+
| pill 高 | **18px** | `--el-badge-size` |
|
|
61
|
+
| 内 padding | 0 6px | `--el-badge-padding` |
|
|
62
|
+
|
|
63
|
+
实测按钮 / tab / 菜单三处**完全一致**,文字在 pill 内垂直居中偏移均为 0(三处 `line-height` 虽不同——按钮 12px、tab 16.8px、菜单 56px 继承整行行高——但 pill 是 `inline-flex + align-items:center + 固定高`,不产生偏移)。
|
|
64
|
+
|
|
65
|
+
⚠️ **不随主题 density 缩放**:core 的 `THEME_FIELD_SPECS` 只往 `:root` 挂变量且未映射任何 badge 变量,故 density 档(compact / cozy / comfortable)不改角标尺寸。这是刻意的——角标应恒为全局最小号,跟着 density 缩放可能小到不可读。消费方要改自行覆盖 `--el-badge-font-size` / `--el-badge-size`。
|
|
66
|
+
|
|
67
|
+
### ⚠️ 换英文**不能**减宽(已实测,[MUST NOT] 再试)
|
|
68
|
+
|
|
69
|
+
12px 字号 + pill 内 padding 实测:
|
|
70
|
+
|
|
71
|
+
| 中文 | 宽 | 英文 | 宽 | 差 |
|
|
72
|
+
| --- | --- | --- | --- | --- |
|
|
73
|
+
| 新 | 26 | NEW | 41 | **+15** |
|
|
74
|
+
| 更新 | 38 | UPDATED | 70 | **+32** |
|
|
75
|
+
| 内测 | 38 | BETA | 45 | **+7** |
|
|
76
|
+
| 开发中 | 50 | WIP | 36 | −14 |
|
|
77
|
+
|
|
78
|
+
汉字在小字号下表意密度远高于大写字母——除「开发中→WIP」外全部变宽。压宽度的正确手段是**减字数**,不是换语种。
|
|
79
|
+
|
|
80
|
+
## API
|
|
81
|
+
|
|
82
|
+
> ⚠️ API 以源码为真相源;与文档冲突时以源码为准。
|
|
83
|
+
|
|
84
|
+
### Props
|
|
85
|
+
|
|
86
|
+
| name | type | 默认 | 语义 |
|
|
87
|
+
| --- | --- | --- | --- |
|
|
88
|
+
| `badgeKey` | `string?` | 无=不显 | 徽标键。菜单侧传路由 `path`,按钮侧传显式键 |
|
|
89
|
+
| `align` | `"corner" \| "middle"` | `"corner"` | 角标垂直锚点:`corner`=骑内容顶边(按钮/图标);`middle`=与内容垂直居中(纯文字标签) |
|
|
90
|
+
|
|
91
|
+
### Emits / Expose
|
|
92
|
+
|
|
93
|
+
无。
|
|
94
|
+
|
|
95
|
+
### Slots
|
|
96
|
+
|
|
97
|
+
| 槽 | scope | 语义 |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| `#default` | 无 | 被挂标的内容;未命中时原样渲染,不加任何包裹层 |
|
|
100
|
+
|
|
101
|
+
## 内置标预设
|
|
102
|
+
|
|
103
|
+
文案与语义色由 core 统一定(跨 app 一致);语义档解析到 `--el-color-*`,**主题风格 / 亮暗切换自动跟随**(规则 14,[MUST NOT] 写死色值)。
|
|
104
|
+
|
|
105
|
+
| kind | 文案 | 语义色 | 为什么 |
|
|
106
|
+
| --- | --- | --- | --- |
|
|
107
|
+
| `count` | 数字 | `danger` 红 | 待办数量**要人行动**,未读红点是全行业最强共识 |
|
|
108
|
+
| `new` | 新 | `success` 绿 | 新功能上线是**正面资讯**(Atlassian success 档即覆盖 "added")。[MUST NOT] 复用红——会与 `count` 撞色,扫视时分不清「有新东西」和「有待办」 |
|
|
109
|
+
| `updated` | 更新 | `primary` 主色 | 中性被动通知,不喧宾夺主 |
|
|
110
|
+
| `beta` | 内测 | `warning` 橙 | 能用但**不稳定需谨慎**,正是 warning 语义 |
|
|
111
|
+
| `wip` | 开发 | `info` 灰 | 不可用,中性、不引导点击 |
|
|
112
|
+
| `custom` | 自定 | 缺省 `primary` | 内置档不够用时才用;`type` 只收语义档 |
|
|
113
|
+
|
|
114
|
+
## 反模式 / 注意
|
|
115
|
+
|
|
116
|
+
- **`custom` 传色值字面量**:`type` [MUST] 取语义档(`primary`/`success`/`warning`/`danger`/`info`),写死色跨主题不响应(规则 14)
|
|
117
|
+
- **包纯文字却用默认 `corner`**:角标会悬在文字上方并溢出相邻行
|
|
118
|
+
- **指望它做聚合**:本组件只按 key 查表渲染,core [MUST NOT] 把子级的标上卷到父级;聚合由 app 自算后往父级 key 写 entry
|
|
119
|
+
- **角标文字色恒为白**(EP 的 `--el-color-white`,主题不覆盖):若某主题的语义色很浅,白字对比度会不足——这是 EP 层面的约束
|
|
120
|
+
|
|
121
|
+
## 关联
|
|
122
|
+
|
|
123
|
+
- 数据真源:`bridge.badge`(`bridge/badge.ts`)
|
|
124
|
+
- 内部 hook:`use-badge`(同级、不出桶;查表 + `expireAt` 判定 + 全 app 共享 tick)
|
|
125
|
+
- 已接入的生产驱动点:`MenuTree` / `MenuItemSub` / `AppHeader` 模块条 / `ActionBtn`
|
|
@@ -62,7 +62,7 @@ const tabs = [
|
|
|
62
62
|
|
|
63
63
|
| name | type | 默认 | 语义 |
|
|
64
64
|
| --- | --- | --- | --- |
|
|
65
|
-
| `tabs` | `TabsMainItem[]`(必填) | — | tab
|
|
65
|
+
| `tabs` | `TabsMainItem[]`(必填) | — | tab 配置列表(`{ key, title, component?, badgeKey? }`;`badgeKey` 见下「tab 标题徽标」) |
|
|
66
66
|
| `viewportHeight` | `number?` | — | 显式视口高度,优先级最高;未传降级 inject SCOPE_VIEWPORT_MAX_HEIGHT scope 链 |
|
|
67
67
|
| `minHeight` | `number?` | `100` | 内容区高度兜底下限 |
|
|
68
68
|
| `channel` | `string?` | — | 作为父时的 scope 标识。⚠️ 与 parentChannel 均 [MUST NOT] 响应式变更(setup 期快照);且仅 refine=true 路径生效 |
|
|
@@ -108,3 +108,22 @@ const tabs = [
|
|
|
108
108
|
- 内部实现:`TabsNaturalFlow` / `TabsRefineFlow` / `TabsHeader` / `TabsTile` / `use-tabs-query-sync.ts`(均不导出)
|
|
109
109
|
- 高度链:`SCOPE_VIEWPORT_MAX_HEIGHT` scope 链(viewportHeight / parentChannel 消费侧)
|
|
110
110
|
- 范式页:`apps/reference/src/pages/display/tabs/showcase/`、`pages/display/tabs/nested-channel/`、`pages/display/tabs/tile/`
|
|
111
|
+
|
|
112
|
+
## tab 标题徽标(`badgeKey`)
|
|
113
|
+
|
|
114
|
+
给 `TabsMainItem` 配 `badgeKey`,命中 `bridge.badge` 注册表且未过期时,在 tab 标题右侧挂绝对定位角标(不改变 tab 尺寸)——**三处同步**:tab-bar 标题、平铺(tile)卡片标题、平铺角落 nav。未配 / 未命中 → DOM 与不接入时逐字节一致。
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const tabs = [
|
|
118
|
+
{ key: "detail", title: "详情" },
|
|
119
|
+
{ key: "export", title: "导出", badgeKey: "order.export" },
|
|
120
|
+
];
|
|
121
|
+
// app 侧
|
|
122
|
+
bridge.badge.set("order.export", { kind: "beta" });
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**[MUST NOT] 拿 `item.key` 当徽标键。** `key` 是**局部唯一**的(v-model 值 + 槽名),两个页面都可能有 `key: "detail"`,复用它会跨页串标。徽标键需全局唯一,故显式另给(同 `ActionBtn.badgeKey` 范式)。
|
|
126
|
+
|
|
127
|
+
**徽标包在标题 slot 外**——消费方自定义 `#{key}` / `#header-{key}` 标题槽时,角标依然在(徽标标的是"这个 tab 有东西",与标题怎么渲染无关)。
|
|
128
|
+
|
|
129
|
+
完整契约(六种标记类型 / `expireAt` 失效 / 已知限制)见 [bridge 文档 · 徽标注册表](../../../bridge/docs/README.md)。
|
|
@@ -20,6 +20,14 @@
|
|
|
20
20
|
- [MenuTree](./docs/README-MenuTree.md) — 菜单主体(menus 递归 + `groupMode` 树形 / 分组双形态 + routerMode / autoClose / collapse + open/close expose)
|
|
21
21
|
- [MenuItemSub](./docs/README-MenuItemSub.md) — 递归子菜单分组件(ElSubMenu 递归节点,一般由 MenuTree 自用;分组形态下承接 L2 及更深层级)
|
|
22
22
|
|
|
23
|
+
## 菜单项徽标
|
|
24
|
+
|
|
25
|
+
两种形态下,凡渲染出路由 `path` 的位置(叶子项 / ElSubMenu 标题 / 分组标题)都会按 **`path` 作 key** 查 `bridge.badge` 注册表,命中则渲染绝对定位角标(数量 / 新上线 / 更新 / 内测 / 开发 / 自定义),**不压字、不改变宿主尺寸**;未命中零影响。
|
|
26
|
+
|
|
27
|
+
无需在菜单数据里另配 key。完整契约见 [bridge 文档 · 徽标注册表](../../bridge/docs/README.md)。
|
|
28
|
+
|
|
29
|
+
> core **不做**子级到父级的冒泡聚合——需要「模块上有引导点」由 app 自己算完往父级 path 写 entry,理由见上述文档。
|
|
30
|
+
|
|
23
31
|
## 关键类型
|
|
24
32
|
|
|
25
33
|
- `RouteMetaResolveRaw`(@/types,menus / menuFlatList 数据契约)
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
- MenuTree `groupMode=false`(树形)→ 承接 **L1 及以下**全部有子项的节点
|
|
13
13
|
- MenuTree `groupMode=true`(分组)→ L1 已被 `ElMenuItemGroup` 分组标题接管,本组件**只承接 L2 及更深**层级(即「分组标题内的手风琴」)。分组标题只吃掉一层,深层级仍归本组件——这是分组形态下三级菜单混合渲染的实现落点
|
|
14
14
|
|
|
15
|
+
## 徽标(badge)
|
|
16
|
+
|
|
17
|
+
本组件渲染的 ElSubMenu 标题与其下叶子项,同样以各自的 `path` 为 key 查 `bridge.badge` 渲染角标,规则与 MenuTree 一致(未命中零影响、不挤压文案、不聚合)。见 [README-MenuTree.md](./README-MenuTree.md#徽标badge)。
|
|
18
|
+
|
|
15
19
|
## 快速上手(最小可用)
|
|
16
20
|
|
|
17
21
|
MenuTree 内部驱动(menu/MenuTree.vue 片段):
|
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
|
|
27
27
|
**视觉不自造**:分组标题吃 EP 原生皮肤(`el-menu-item-group__title` = `font-size:12px` + `--el-text-color-secondary`);需微调只走主题 var(规则 14),[MUST NOT] 写死颜色。
|
|
28
28
|
|
|
29
|
+
## 徽标(badge)
|
|
30
|
+
|
|
31
|
+
每个渲染出 `menu.path` 的位置——L1 叶子、L1 分组标题(分组形态)、递归下去的 ElSubMenu 标题与叶子(经 MenuItemSub)——都以 **`path` 为 key** 查 `bridge.badge`,命中即渲染绝对定位角标(不改变宿主尺寸)。
|
|
32
|
+
|
|
33
|
+
- **零配置**:菜单数据不用加任何字段;未命中不渲染,对既有菜单零影响。
|
|
34
|
+
- **不压字、不改尺寸**:角标绝对定位(内部包 ElBadge),跟在标题右侧、不覆盖任何字,且**角标的出现不改变菜单项尺寸**(实测有标/无标逐项宽度差 0)。
|
|
35
|
+
- **不聚合**:MenuTree [MUST NOT] 把子项的标上卷到父级,父级只显示注册表里挂在它自己 path 上的 entry。
|
|
36
|
+
|
|
37
|
+
契约(标记类型 / `expireAt` 失效 / 已知限制)见 [bridge 文档 · 徽标注册表](../../../bridge/docs/README.md)。
|
|
38
|
+
|
|
29
39
|
## 快速上手(最小可用)
|
|
30
40
|
|
|
31
41
|
必填只有 `menus` + `menuFlatList`——默认 ElMenu 主题、无路由联动、无选中收合:
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ShallowRef } from 'vue';
|
|
2
|
+
import { BadgeProps } from 'element-plus';
|
|
3
|
+
/**
|
|
4
|
+
* bridge 徽标注册表 hook(按耦合度从 index.ts 拆出,规则 12)。
|
|
5
|
+
*
|
|
6
|
+
* 一张扁平的 `key → 标记` 表;菜单项(AppSidebar 菜单树 / AppHeader 模块条)与
|
|
7
|
+
* `ActionBtn` 按 key 查表渲染**绝对定位角标**(跟在文案右侧、不压字;[MUST] 不改变宿主尺寸)。
|
|
8
|
+
*
|
|
9
|
+
* **分工:core 只负责展示。** 聚合、归属、清标、数据来源全部在 app——
|
|
10
|
+
* core [MUST NOT] 把子级的标上卷到父级菜单(归属在真实系统里多对多且有例外:
|
|
11
|
+
* 弹窗内按钮 / 全局工具栏按钮 / 一个入口挂两个模块;core 定义归属模型即越权)。
|
|
12
|
+
* 需要「一级模块上有引导点」由 app 自己算完往父级 path 写一条 entry。
|
|
13
|
+
*
|
|
14
|
+
* 每次 `createCoreBridge` 各自闭包内构造(零模块级单例,守实例隔离,规则 8)。
|
|
15
|
+
*/
|
|
16
|
+
/** 徽标 key → 标记 的注册表载荷 */
|
|
17
|
+
export type BadgeMarkMap = Record<string, BadgeEntry>;
|
|
18
|
+
/**
|
|
19
|
+
* 徽标标记。
|
|
20
|
+
*
|
|
21
|
+
* `expireAt` 提在交叉类型外——**所有 kind 一律支持过期**,缺省或 `0` = 永久展示
|
|
22
|
+
* (两个哨兵指同一件事,`!expireAt` 一个表达式同时吃掉,零分支;后端 JSON 常发
|
|
23
|
+
* `0` 而非省略字段)。
|
|
24
|
+
*
|
|
25
|
+
* 两条失效路径正交可共存:
|
|
26
|
+
* - 有 `expireAt`(如随版本写死到某日的 NEW)→ core 内部 tick 到点自动落,app 不用管;
|
|
27
|
+
* - 无 `expireAt`(如服务端来的待办数 / Beta 标)→ app 下一轮 `setAll` 刷掉。
|
|
28
|
+
*
|
|
29
|
+
* 故 `expireAt` **不是必填**——它是给「没有轮询通道」的静态标准备的兜底。
|
|
30
|
+
*/
|
|
31
|
+
export type BadgeEntry = {
|
|
32
|
+
/** 展示截止的绝对时间(epoch ms);缺省或 0 = 永久展示 */
|
|
33
|
+
expireAt?: number;
|
|
34
|
+
/**
|
|
35
|
+
* 面向 agent / AI 对话的语义描述——**core 不消费,纯透传**。
|
|
36
|
+
*
|
|
37
|
+
* 供应用层做「本期上了什么功能」这类对话时,让模型知道这个 key 指向什么、
|
|
38
|
+
* 怎么用、什么时候还不该用。与 `kind`(= 变更类别,同 Keep a Changelog 的
|
|
39
|
+
* Added/Changed 词汇)+ `expireAt`(时效)合起来,这张表即一份机器可读的功能公告。
|
|
40
|
+
*
|
|
41
|
+
* **自由文本,[MUST NOT] 拆成正反两个字段**——对齐 MCP / OpenAI function calling /
|
|
42
|
+
* JSON Schema 的 tool `description` 范式(均为单 string,约定把「何时用 / 何时不该用」
|
|
43
|
+
* 写在同一段里)。区别于主题 preset 的 {@link CoreConfigDescription}:那是**闭集选型**
|
|
44
|
+
* 场景(35 套主题里挑一套),正 / 反 facets 能做无 LLM 预筛;徽标是**讲解**场景,
|
|
45
|
+
* 不做排除,拆正反会让「新功能的不宜选场景是什么」这种问题无从填起。
|
|
46
|
+
*
|
|
47
|
+
* 重内容(图文步骤 / 深链 / 视频)[SHOULD] 走 app 自己的知识库、用同一 key 关联——
|
|
48
|
+
* [MUST NOT] 往本字段堆,否则 `BadgeEntry` 会从「一个标」膨胀成 CMS 条目。
|
|
49
|
+
*
|
|
50
|
+
* @example "批量导出订单为 Excel。适合按月对账;单次超 10 万行会超时,内测期不建议用于生产对账。"
|
|
51
|
+
*/
|
|
52
|
+
description?: string;
|
|
53
|
+
} & ({
|
|
54
|
+
/** 数字角标 */
|
|
55
|
+
kind: "count";
|
|
56
|
+
/** 计数值 */
|
|
57
|
+
value: number;
|
|
58
|
+
/** 上限,超出显 `{max}+`(透传 ElBadge) */
|
|
59
|
+
max?: number;
|
|
60
|
+
} | {
|
|
61
|
+
kind: "new";
|
|
62
|
+
} | {
|
|
63
|
+
kind: "updated";
|
|
64
|
+
} | {
|
|
65
|
+
kind: "beta";
|
|
66
|
+
} | {
|
|
67
|
+
kind: "wip";
|
|
68
|
+
} | {
|
|
69
|
+
/** 自定义标:内置五档不够用时才用 */
|
|
70
|
+
kind: "custom";
|
|
71
|
+
/** 标签文案 */
|
|
72
|
+
text: string;
|
|
73
|
+
/**
|
|
74
|
+
* 语义色档(透传 ElBadge `type`),随主题亮暗自适配。
|
|
75
|
+
* [MUST NOT] 传色值字面量——规则 14,写死色切主题不响应。
|
|
76
|
+
*/
|
|
77
|
+
type?: BadgeProps["type"];
|
|
78
|
+
});
|
|
79
|
+
/** 内置标(非 count / custom)的 kind 联合——文案与语义色由 core 统一定,保证跨 app 一致 */
|
|
80
|
+
export type BadgeBuiltinKind = "new" | "updated" | "beta" | "wip";
|
|
81
|
+
/** bridge 徽标 hook 工厂入参 */
|
|
82
|
+
export interface BridgeBadgeInit {
|
|
83
|
+
/** 初始标记表(如随版本发布的静态 NEW 标);缺省空表 */
|
|
84
|
+
marks?: BadgeMarkMap;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 徽标注册表模块(bridge 公开面 `bridge.badge`)。
|
|
88
|
+
*
|
|
89
|
+
* ⚠️ [MUST NOT] 用 `bridge.update()` 维护徽标——那是 `Object.assign` **浅合并**,
|
|
90
|
+
* 轮询场景下服务端本轮已撤下的标会永远留在表里。徽标独立走本模块。
|
|
91
|
+
*/
|
|
92
|
+
export interface CoreBadgeModule {
|
|
93
|
+
/** 全部标记(响应式只读;内部 shallowRef,每次操作产新对象) */
|
|
94
|
+
readonly marks: Readonly<ShallowRef<BadgeMarkMap>>;
|
|
95
|
+
/** **整体替换** —— 轮询主路径,服务端本轮撤下的标随之消失 */
|
|
96
|
+
setAll: (marks: BadgeMarkMap) => void;
|
|
97
|
+
/** 单点写入 / 覆盖 */
|
|
98
|
+
set: (key: string, entry: BadgeEntry) => void;
|
|
99
|
+
/** 单点移除 */
|
|
100
|
+
remove: (key: string) => void;
|
|
101
|
+
}
|
|
102
|
+
/** bridge 徽标 hook 工厂返回(聚合到 `bridge.badge`) */
|
|
103
|
+
export interface BridgeBadgeHook {
|
|
104
|
+
badge: CoreBadgeModule;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* bridge 徽标注册表 hook 工厂。
|
|
108
|
+
*
|
|
109
|
+
* 内部 `shallowRef` + 每次操作产新对象:整体替换语义天然正确、无深代理开销
|
|
110
|
+
* (标记是值对象,不需要逐字段深响应)。
|
|
111
|
+
*/
|
|
112
|
+
export declare function createBridgeBadgeHook(init?: BridgeBadgeInit): BridgeBadgeHook;
|
package/types/bridge/index.d.ts
CHANGED
|
@@ -6,11 +6,13 @@ import { UpdatableConfigKey, UpdatableConfigMap } from './config-hook';
|
|
|
6
6
|
import { CoreBridgePluginLike } from './plugin';
|
|
7
7
|
import { CoreThemeModule, CoreThemeMode, CoreThemeDensity, CoreThemeMotion, BridgeThemeLayoutState } from './theme';
|
|
8
8
|
import { CoreLayoutModule } from './layout';
|
|
9
|
+
import { CoreBadgeModule, BadgeMarkMap } from './badge';
|
|
9
10
|
export { createUseState, type UseStateOptions } from './state';
|
|
10
11
|
export { createGenerateRouteMetaRawTree, type RouteMetaDefaultConfig, } from './route';
|
|
11
12
|
export { createStorageWithNamespace } from './storage';
|
|
12
13
|
export type { CoreThemeEntry, CoreThemeLifecycleContext, CoreThemeLifecycleHooks, CoreConfigDescription, CoreThemeModule, CoreThemeMode, CoreThemeDensity, CoreThemeMotion, ElSize, CoreThemeComponentDefaults, CoreThemePresetLayout, BridgeThemeLayoutState, BridgeThemeLayoutBaseline, BridgeThemeHook, } from './theme';
|
|
13
14
|
export type { CoreLayoutModule, BridgeLayoutHook, BridgeLayoutInit, LayoutCollapsePersistShape, } from './layout';
|
|
15
|
+
export type { BadgeEntry, BadgeMarkMap, BadgeBuiltinKind, CoreBadgeModule, BridgeBadgeHook, BridgeBadgeInit, } from './badge';
|
|
14
16
|
/**
|
|
15
17
|
* 认证桥接无状态纯工厂。
|
|
16
18
|
*
|
|
@@ -480,6 +482,13 @@ export interface CoreBridgeInitOptions<UserInfo = unknown> {
|
|
|
480
482
|
* 经 `bridge.showThemeStyleSwitcher` readonly 暴露,AppTheme 直读 `v-if` 自控。
|
|
481
483
|
*/
|
|
482
484
|
showThemeStyleSwitcher?: boolean;
|
|
485
|
+
/**
|
|
486
|
+
* 徽标注册表初值(`key → 标记`);缺省空表。
|
|
487
|
+
*
|
|
488
|
+
* 适合放「随版本发布的静态标」(如写死 `expireAt` 的 NEW);服务端来的动态标
|
|
489
|
+
* 由 app 在拿到数据后经 `bridge.badge.setAll(map)` 整体替换。
|
|
490
|
+
*/
|
|
491
|
+
badgeMarks?: BadgeMarkMap;
|
|
483
492
|
/** app 嵌套路由树(必传,#4e additive;createUserStore 权限树依赖 children 嵌套,[MUST NOT] 传扁平化数据) */
|
|
484
493
|
routes: RouteRecordRaw[];
|
|
485
494
|
/** 用户信息空保底工厂(v3 终锁 Δ0b;恒返非空业务字段默认值对象,createUserStore useState 经此取初值,core 不锁业务 shape) */
|
|
@@ -608,6 +617,15 @@ export interface CoreBridge<UserInfo = unknown, LoginParams = unknown> {
|
|
|
608
617
|
* [MUST NOT] 解构/快照折叠态(⚠️ 切换后定格)。
|
|
609
618
|
*/
|
|
610
619
|
readonly layout: CoreLayoutModule;
|
|
620
|
+
/**
|
|
621
|
+
* 徽标注册表模块(`key → 标记` 扁平表)。
|
|
622
|
+
*
|
|
623
|
+
* 菜单项以路由 `path` 为 key、`ActionBtn` 以显式 `badgeKey` 查表渲染绝对定位角标。
|
|
624
|
+
* `marks` 响应式只读 + `setAll`(整体替换,轮询主路径)/ `set` / `remove`。
|
|
625
|
+
* ⚠️ [MUST NOT] 改用 `update()` 维护——浅合并会让服务端已撤下的标永远留存。
|
|
626
|
+
* core 只负责展示:聚合 / 归属 / 清标 / 数据来源全在 app。
|
|
627
|
+
*/
|
|
628
|
+
readonly badge: CoreBadgeModule;
|
|
611
629
|
/** 用户信息字段访问器集(业务注入;createUserStore 经 bridge.userInfoAccess.<字段>.get/set 读写,第一宪法 core 零字段耦合) */
|
|
612
630
|
readonly userInfoAccess: UserInfoAccess<UserInfo>;
|
|
613
631
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
/**
|
|
3
|
+
* 徽标键;命中 `bridge.badge` 注册表且未过期才渲染角标。
|
|
4
|
+
* 菜单侧传路由 `path`,`ActionBtn` 侧传显式 `badgeKey`。
|
|
5
|
+
*/
|
|
6
|
+
badgeKey?: string;
|
|
7
|
+
/**
|
|
8
|
+
* 角标垂直锚点(两档均为**绝对定位**——[MUST] 保证角标的出现不改变宿主尺寸):
|
|
9
|
+
* - `"corner"` = 右上角(EP 原生骑角,角标中心钉在**被包内容盒的顶边**)。
|
|
10
|
+
* - `"middle"`(**缺省**)= 与被包内容盒垂直居中。
|
|
11
|
+
*
|
|
12
|
+
* **怎么选:看被包内容盒是否紧贴文字**(实测三类宿主,同一段 CSS 落点完全不同):
|
|
13
|
+
*
|
|
14
|
+
* | 宿主 | `.el-badge` 盒高 | 宿主高 | `corner` 落点 | 选 |
|
|
15
|
+
* | --- | --- | --- | --- | --- |
|
|
16
|
+
* | ActionBtn | 14px(紧贴文字行) | 32px | 角标顶齐按钮顶,上溢 0 | `corner` |
|
|
17
|
+
* | TabsHeader tab | 22px(紧贴) | 40px | 上溢 −1(不越界) | `corner` |
|
|
18
|
+
* | 菜单项 / AppHeader 模块条 / tile 卡片头 | **50px(撑满整行)** | 50px | 钉在**行顶**、上溢 9px 侵入上一行 | `middle` |
|
|
19
|
+
*
|
|
20
|
+
* 差别根源:菜单项的 span 继承整行 `line-height`,`.el-badge` 盒跟着撑满一行,
|
|
21
|
+
* 于是「内容顶边」≠「文字顶边」。[MUST NOT] 靠 `top: N%` 魔数去凑——换字号 /
|
|
22
|
+
* density 就错位;盒紧贴文字的宿主用 `corner`,盒撑满行的用 `middle`。
|
|
23
|
+
*
|
|
24
|
+
* **横向几何(两档共用,见 style 块)**:EP `.is-fixed` 默认 `right: calc(1px+size/2)`
|
|
25
|
+
* 让角标**向内嵌 10px**——那是为「包裹整个按钮」设计的,而本组件包的是**内容本身**
|
|
26
|
+
* (标题 / label 文字),10px 内嵌直接压住最后一个字(14px 字号下≈3/4 个汉字)。故归零 + 2px 让位。
|
|
27
|
+
*
|
|
28
|
+
* **代价([MUST] 知悉)**:绝对定位不占位,角标必然向右**逃逸出宿主边界**,逃逸量
|
|
29
|
+
* = 角标宽 − 13px。实测中文内置标逃逸 12–24px,只压进邻居 padding、不压邻居文字;
|
|
30
|
+
* 但这依赖消费方的布局间距,core [MUST NOT] 声称能保证不重叠。故内置标文案
|
|
31
|
+
* [MUST] 控制在 **2 个汉字以内**(3 字的「开发中」实测逃逸 36px、右缘顶到下一个按钮)。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 换英文**不能**减宽:实测 12px 字号下 NEW=41 > 新=26、UPDATED=70 > 更新=38、
|
|
34
|
+
* BETA=45 > 内测=38——汉字表意密度远高于大写字母。[MUST NOT] 为「更短」而改英文。
|
|
35
|
+
*
|
|
36
|
+
* [MUST NOT] 用 ElBadge `offset` 魔数调位——与字号 / 行高强耦合,主题 density 一变就错位。
|
|
37
|
+
*/
|
|
38
|
+
align?: "middle" | "corner";
|
|
39
|
+
};
|
|
40
|
+
declare function __VLS_template(): {
|
|
41
|
+
attrs: Partial<{}>;
|
|
42
|
+
slots: {
|
|
43
|
+
default?(_: {}): any;
|
|
44
|
+
default?(_: {}): any;
|
|
45
|
+
};
|
|
46
|
+
refs: {};
|
|
47
|
+
rootEl: any;
|
|
48
|
+
};
|
|
49
|
+
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
50
|
+
declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
51
|
+
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
52
|
+
export default _default;
|
|
53
|
+
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
54
|
+
new (): {
|
|
55
|
+
$slots: S;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
@@ -6,6 +6,7 @@ import { default as ActionBtn } from './ActionBtn.vue';
|
|
|
6
6
|
import { default as ActionConfirm } from './ActionConfirm.vue';
|
|
7
7
|
import { default as ActionBtnGroup } from './ActionBtnGroup.vue';
|
|
8
8
|
import { default as BooleanTag } from './BooleanTag.vue';
|
|
9
|
+
import { default as BadgeMark } from './BadgeMark.vue';
|
|
9
10
|
export {
|
|
10
11
|
/** 尺寸监听容器:debounce 测默认槽内容宽/高,派发 `@height-change` / `@size-change` */
|
|
11
12
|
WatchSize,
|
|
@@ -25,6 +26,12 @@ HeightProvider,
|
|
|
25
26
|
* 确认闸门 + `beforeOpen` 异步预取 + onClick 返 Promise 自动 loading(内部接管)。
|
|
26
27
|
*/
|
|
27
28
|
ActionBtn,
|
|
29
|
+
/**
|
|
30
|
+
* 徽标标记:按 `badgeKey` 查 `bridge.badge` 注册表,命中且未过期则给默认插槽内容
|
|
31
|
+
* 挂绝对定位角标(包 ElBadge,不挤压主体);未命中渲染裸插槽、DOM 零变化。
|
|
32
|
+
* 菜单族 / ActionBtn 内部已接入;自供 shell 自渲染导航时可直接复用。
|
|
33
|
+
*/
|
|
34
|
+
BadgeMark,
|
|
28
35
|
/**
|
|
29
36
|
* confirm 原语(扁平,可独立于 ActionBtn 消费):触发元素由默认 slot 提供,
|
|
30
37
|
* 弹层可见性 `v-model:show` 声明式驱动;`submitFn` 返 Promise 时仅成功才关弹层。
|
|
@@ -61,6 +61,15 @@ export type ActionBtnProps<T = void> = Omit<ButtonProps, "loading"> & {
|
|
|
61
61
|
* 瞬时用户激活(详见 {@link ActionBtnConfirmConfig} 顶部注释)。
|
|
62
62
|
*/
|
|
63
63
|
beforeOpen?: () => Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* 徽标键——命中 `bridge.badge` 注册表且未过期时,在按钮内容右侧挂**绝对定位角标**
|
|
66
|
+
* (包 ElBadge;不压字,且 [MUST] 不改变按钮尺寸;未命中时 DOM 与不传时逐字节一致)。
|
|
67
|
+
*
|
|
68
|
+
* 按钮没有路由 path,[MUST] 显式给键(菜单侧则默认取 path,无需另配)。
|
|
69
|
+
* 配置驱动(`ActionBtnGroup`)经 `actionBtnProps` 工厂返回 `{ badgeKey }` 即可。
|
|
70
|
+
* 契约详见 bridge 文档「徽标注册表」章节。
|
|
71
|
+
*/
|
|
72
|
+
badgeKey?: string;
|
|
64
73
|
};
|
|
65
74
|
/**
|
|
66
75
|
* ActionConfirm 组件 props(扁平,confirm 原语;可独立于 ActionBtn 消费)。
|
|
@@ -142,6 +151,15 @@ export interface TabsMainItem {
|
|
|
142
151
|
title: string;
|
|
143
152
|
/** 内容组件,#{key} 槽缺省时回退渲染 */
|
|
144
153
|
component?: Component;
|
|
154
|
+
/**
|
|
155
|
+
* 徽标键——命中 `bridge.badge` 注册表且未过期时,在 tab 标题右侧挂绝对定位角标
|
|
156
|
+
* (tab-bar / 平铺卡片标题 / 平铺角落 nav 三处同步);[MUST] 不改变 tab 尺寸。
|
|
157
|
+
*
|
|
158
|
+
* [MUST NOT] 复用本项的 `key`:那是**局部唯一**(v-model 值 + 槽名),两个页面
|
|
159
|
+
* 都可能有 `key: "detail"`,当徽标键会跨页串标。徽标键需全局唯一,故显式另给
|
|
160
|
+
* (同 `ActionBtn.badgeKey` 范式)。契约见 bridge 文档「徽标注册表」章节。
|
|
161
|
+
*/
|
|
162
|
+
badgeKey?: string;
|
|
145
163
|
}
|
|
146
164
|
/** [top, right, bottom, left] px——TabsMain 默认插槽容器内置 padding(top+bottom 同步从 channel viewportHeight 扣减) */
|
|
147
165
|
export type TabsMainPadding = [number, number, number, number];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ComputedRef } from 'vue';
|
|
2
|
+
import { BadgeProps } from 'element-plus';
|
|
3
|
+
/** 徽标解析结果——已过滤过期项,直接可渲染 */
|
|
4
|
+
export interface BadgeResolved {
|
|
5
|
+
/** ElBadge value(数量标为数字,其余为文案) */
|
|
6
|
+
value: string | number;
|
|
7
|
+
/** ElBadge max(仅 count 有意义) */
|
|
8
|
+
max?: number;
|
|
9
|
+
/** ElBadge 语义色档 */
|
|
10
|
+
type: BadgeProps["type"];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 按 key 解析出可渲染的徽标;无 key / 未 `app.use(bridge)` / 未命中 / 已过期 → `undefined`。
|
|
14
|
+
*
|
|
15
|
+
* bridge 经 `CORE_BRIDGE_KEY`(app 级整实例)而非 `APP_LAYOUT_BRIDGE_KEY`(layout 窄切片)——
|
|
16
|
+
* `ActionBtn` 可用在 AppLayout 之外的任何地方。**opt-in**:未 `app.use(bridge)` 时静默不显,
|
|
17
|
+
* [MUST NOT] 抛错。
|
|
18
|
+
*/
|
|
19
|
+
export declare function useBadgeResolved(getKey: () => string | undefined): ComputedRef<BadgeResolved | undefined>;
|
package/types/store/app.d.ts
CHANGED
|
@@ -77,6 +77,13 @@ export declare function createAppStore<UserInfo = unknown, LoginParams = unknown
|
|
|
77
77
|
bottom: string;
|
|
78
78
|
left: string;
|
|
79
79
|
}>;
|
|
80
|
+
viewportConfigEffective: import('vue').ComputedRef<{
|
|
81
|
+
readonly paddingTop: number;
|
|
82
|
+
readonly paddingRight: number;
|
|
83
|
+
readonly paddingBottom: number;
|
|
84
|
+
readonly paddingLeft: number;
|
|
85
|
+
}>;
|
|
86
|
+
noAnimation: import('vue').ComputedRef<boolean>;
|
|
80
87
|
sidebarIsCollapse: import('vue').ComputedRef<boolean>;
|
|
81
88
|
asideIsCollapse: import('vue').ComputedRef<boolean>;
|
|
82
89
|
changeSidebarCollapseStatus: (status: boolean) => void;
|
|
@@ -130,13 +137,20 @@ export declare function createAppStore<UserInfo = unknown, LoginParams = unknown
|
|
|
130
137
|
bottom: string;
|
|
131
138
|
left: string;
|
|
132
139
|
}>;
|
|
140
|
+
viewportConfigEffective: import('vue').ComputedRef<{
|
|
141
|
+
readonly paddingTop: number;
|
|
142
|
+
readonly paddingRight: number;
|
|
143
|
+
readonly paddingBottom: number;
|
|
144
|
+
readonly paddingLeft: number;
|
|
145
|
+
}>;
|
|
146
|
+
noAnimation: import('vue').ComputedRef<boolean>;
|
|
133
147
|
sidebarIsCollapse: import('vue').ComputedRef<boolean>;
|
|
134
148
|
asideIsCollapse: import('vue').ComputedRef<boolean>;
|
|
135
149
|
changeSidebarCollapseStatus: (status: boolean) => void;
|
|
136
150
|
changeAsideCollapseStatus: (status: boolean) => void;
|
|
137
151
|
changeSidebarHasMenusStatus: (status: boolean) => void;
|
|
138
152
|
changeIsDarkTheme: (status: boolean) => void;
|
|
139
|
-
}, "name" | "theme" | "showHeader" | "keepAlive" | "bodyShimPadding" | "isDarkTheme" | "showFooter" | "showSidebar" | "showAside" | "showBreadcrumb" | "headerStyle" | "footerStyle" | "sidebarStyle" | "asideStyle" | "bodyStyle" | "contentViewportInset" | "bodyShimStyle" | "viewportPadding" | "sidebarIsCollapse" | "asideIsCollapse">, Pick<{
|
|
153
|
+
}, "name" | "theme" | "showHeader" | "keepAlive" | "bodyShimPadding" | "noAnimation" | "isDarkTheme" | "showFooter" | "showSidebar" | "showAside" | "showBreadcrumb" | "headerStyle" | "footerStyle" | "sidebarStyle" | "asideStyle" | "bodyStyle" | "contentViewportInset" | "bodyShimStyle" | "viewportPadding" | "viewportConfigEffective" | "sidebarIsCollapse" | "asideIsCollapse">, Pick<{
|
|
140
154
|
name: import('vue').ComputedRef<string>;
|
|
141
155
|
isDarkTheme: import('vue').ComputedRef<boolean>;
|
|
142
156
|
theme: import('vue').ComputedRef<{
|
|
@@ -183,6 +197,13 @@ export declare function createAppStore<UserInfo = unknown, LoginParams = unknown
|
|
|
183
197
|
bottom: string;
|
|
184
198
|
left: string;
|
|
185
199
|
}>;
|
|
200
|
+
viewportConfigEffective: import('vue').ComputedRef<{
|
|
201
|
+
readonly paddingTop: number;
|
|
202
|
+
readonly paddingRight: number;
|
|
203
|
+
readonly paddingBottom: number;
|
|
204
|
+
readonly paddingLeft: number;
|
|
205
|
+
}>;
|
|
206
|
+
noAnimation: import('vue').ComputedRef<boolean>;
|
|
186
207
|
sidebarIsCollapse: import('vue').ComputedRef<boolean>;
|
|
187
208
|
asideIsCollapse: import('vue').ComputedRef<boolean>;
|
|
188
209
|
changeSidebarCollapseStatus: (status: boolean) => void;
|