@done-coding/admin-core 0.25.0 → 0.26.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/components/app-layout/AppPage.vue.mjs +1 -1
- package/es/components/app-layout/AppPage.vue2.mjs +42 -23
- package/es/components/app-layout/use-content-box.mjs +17 -0
- package/es/components/data-view/DataGridView.vue.mjs +1 -1
- package/es/components/data-view/DataGridView.vue2.mjs +34 -14
- package/es/components/data-view/DataListView.vue.mjs +1 -1
- package/es/components/data-view/DataListView.vue2.mjs +27 -7
- package/es/components/data-view/DataListViewItem.vue.mjs +1 -1
- package/es/components/data-view/DataListViewItem.vue2.mjs +3 -2
- package/es/components/list-layout/ListLayout.vue.mjs +1 -1
- package/es/components/list-layout/ListLayout.vue2.mjs +34 -11
- package/es/components/modal/ModalConfirm.vue.mjs +1 -1
- package/es/components/modal/ModalConfirm.vue2.mjs +67 -49
- package/es/components/page-layout/AppPageDetail.vue.mjs +7 -0
- package/es/components/page-layout/AppPageDetail.vue2.mjs +89 -0
- package/es/components/page-layout/AppPageListDetailLayout.vue.mjs +153 -0
- package/es/components/page-layout/AppPageListDetailLayout.vue2.mjs +4 -0
- package/es/components/page-layout/AppPageListDetailSheet.vue.mjs +96 -0
- package/es/components/page-layout/AppPageListDetailSheet.vue2.mjs +4 -0
- package/es/components/page-layout/AppPageListDetailSplit.vue.mjs +7 -0
- package/es/components/page-layout/AppPageListDetailSplit.vue2.mjs +83 -0
- package/es/components/page-layout/use-list-detail.mjs +110 -0
- package/es/components/panel/PanelEditSwitch.vue.mjs +1 -1
- package/es/components/table/TableMain.vue.mjs +1 -1
- package/es/components/table/TableMain.vue2.mjs +55 -5
- package/es/components/view-layout/ViewLayout.vue.mjs +1 -1
- package/es/components/view-layout/ViewLayout.vue2.mjs +34 -4
- package/es/components/view-layout/ViewLayoutNavigation.vue.mjs +7 -0
- package/es/components/view-layout/ViewLayoutNavigation.vue2.mjs +34 -0
- package/es/hooks/use-active-record.mjs +100 -0
- package/es/index.mjs +146 -141
- package/es/inject/key.mjs +4 -0
- package/es/style.css +129 -96
- package/package.json +2 -2
- package/src/components/app-layout/README.md +8 -1
- package/src/components/app-layout/docs/README-AppPage.md +48 -2
- package/src/components/data-view/docs/README-DataGridView.md +2 -1
- package/src/components/data-view/docs/README-DataListView.md +13 -4
- package/src/components/list-layout/docs/README-ListLayout.md +1 -1
- package/src/components/modal/docs/README-ModalConfirm.md +2 -0
- package/src/components/page-layout/README.md +47 -0
- package/src/components/page-layout/docs/README-AppPageDetail.md +57 -0
- package/src/components/page-layout/docs/README-AppPageListDetailLayout.md +142 -0
- package/src/components/table/docs/README-TableMain.md +1 -1
- package/src/components/view-layout/docs/README-ViewLayout.md +14 -0
- package/types/components/app-layout/AppPage.vue.d.ts +14 -0
- package/types/components/app-layout/use-content-box.d.ts +31 -0
- package/types/components/data-view/DataGridView.vue.d.ts +9 -2
- package/types/components/data-view/DataListView.vue.d.ts +10 -2
- package/types/components/data-view/DataListViewItem.vue.d.ts +4 -2
- package/types/components/data-view/types.d.ts +5 -1
- package/types/components/list-layout/ListLayout.vue.d.ts +7 -2
- package/types/components/modal/ModalConfirm.vue.d.ts +18 -0
- package/types/components/page-layout/AppPageDetail.vue.d.ts +40 -0
- package/types/components/page-layout/AppPageListDetailLayout.vue.d.ts +39 -0
- package/types/components/page-layout/AppPageListDetailSheet.vue.d.ts +44 -0
- package/types/components/page-layout/AppPageListDetailSplit.vue.d.ts +47 -0
- package/types/components/page-layout/index.d.ts +4 -0
- package/types/components/page-layout/types.d.ts +102 -0
- package/types/components/page-layout/use-list-detail.d.ts +58 -0
- package/types/components/panel/PanelEditSwitch.vue.d.ts +30 -0
- package/types/components/table/TableMain.vue.d.ts +9 -2
- package/types/components/view-layout/ViewLayoutNavigation.vue.d.ts +13 -0
- package/types/components/view-layout/types.d.ts +46 -0
- package/types/hooks/use-active-record.d.ts +64 -0
- package/types/index.d.ts +1 -0
- package/types/inject/key.d.ts +29 -0
- package/types/injectInfo.json.d.ts +1 -1
- package/types/types/utility-types.d.ts +17 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# AppPageDetail(详情区 · 可独立成页)
|
|
2
|
+
|
|
3
|
+
> 详情区组件:**默认自带 `AppPage`,可直接当一个详情路由页用**;`embedded` 开启后走透传态,供 `AppPageListDetailLayout` 内嵌。
|
|
4
|
+
> 范式页:`apps/reference/src/pages/app-layout/list-detail/`
|
|
5
|
+
|
|
6
|
+
## 两种用法
|
|
7
|
+
|
|
8
|
+
**① 独立成页(默认 `embedded=false`)** —— 详情走独立路由时用:
|
|
9
|
+
|
|
10
|
+
```vue
|
|
11
|
+
<AppPageDetail
|
|
12
|
+
:id="route.params.id"
|
|
13
|
+
:detail-api="detailApi"
|
|
14
|
+
:detail-title="(d) => d?.name ?? ''"
|
|
15
|
+
>
|
|
16
|
+
<template #detail="{ data }"><MyDetail :data="data" /></template>
|
|
17
|
+
</AppPageDetail>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
此时 `ctx` 不必传,**无序列导航**(`total=0`、上/下一条两端皆 false)——独立页本就没有「列表上下文」,这是正确语义而非缺失。
|
|
21
|
+
|
|
22
|
+
**② 被门面内嵌(`embedded=true`)** —— 由 `AppPageListDetailLayout` 内部使用,宿主已有一层 `AppPage`,故透传不再套;门面下发完整 `ctx`(含序列导航句柄)。业务一般不直接这么用。
|
|
23
|
+
|
|
24
|
+
## API
|
|
25
|
+
|
|
26
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
27
|
+
|
|
28
|
+
### Props
|
|
29
|
+
|
|
30
|
+
| name | type | 默认 | 语义 |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| `embedded` | `boolean` | `false` | `true` = 不套 `AppPage`(宿主已有一层)。⚠️ 内嵌态下本组件无宿主元素,`dc-app-page-detail` 根类与 `$attrs` 随之无落点 |
|
|
33
|
+
| `ctx` | `Omit<AppPageListDetailDetailScope, "data" \| "loading">` | 无 | 完整详情上下文(含序列导航)。**门面内嵌时下发**;独立使用不传 |
|
|
34
|
+
| `id` | `string \| number` | 无 | 独立使用时的当前项键(`ctx` 未传时生效;与 `ctx` 都无 → 落空态) |
|
|
35
|
+
| `record` | `T` | 无 | 独立使用时的当前项数据(`ctx` 未传时生效),供 `detailTitle` / `#detail` 消费 |
|
|
36
|
+
| `detailApi` | `(params?: { id }) => Promise<R>` | 无 | 传了则内置 `ViewLayout`(零配置拿到 `refreshToken` 接线 + 内置上/下一条导航);不传则 `#detail` 原样渲染、本组件零请求 |
|
|
37
|
+
| `detailTitle` | `string \| ((detailData, record) => string)` | 无 | 详情区标题,渲染在详情区顶部 |
|
|
38
|
+
| `listEmpty` | `boolean` | `false` | 分流两种空态文案;独立使用时通常不关心 |
|
|
39
|
+
|
|
40
|
+
### Slots
|
|
41
|
+
|
|
42
|
+
| 槽 | scope | 语义 |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情内容。`data` / `loading` 仅在传了 `detailApi` 时有值;独立使用时序列字段为零值 |
|
|
45
|
+
| `#empty` | `{ listEmpty }` | 无当前项时的占位 |
|
|
46
|
+
|
|
47
|
+
## 反模式 / 注意
|
|
48
|
+
|
|
49
|
+
- **独立使用时期待上/下一条** —— 没有列表就没有序列,导航句柄是空操作、两端恒 false。要导航就用门面 `AppPageListDetailLayout`
|
|
50
|
+
- **业务外层再套 `AppPage`** —— 默认态它自己就是 `AppPage`,套了成两层
|
|
51
|
+
- **`embedded=true` 还指望 `dc-app-page-detail` 根类 / `$attrs` 落地** —— 透传态无宿主元素
|
|
52
|
+
- **照抄 `SlotLayoutTemplate :disabled` 的写法去做需要具名槽的组件** —— 透传态只渲染默认槽,具名槽会被整个丢掉
|
|
53
|
+
|
|
54
|
+
## 关联
|
|
55
|
+
|
|
56
|
+
- 族系标准与清单:[../README.md](../README.md)
|
|
57
|
+
- 门面:[README-AppPageListDetailLayout.md](./README-AppPageListDetailLayout.md)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# AppPageListDetailLayout(列表 / 详情同页范式 · master-detail)
|
|
2
|
+
|
|
3
|
+
> 把「列表 → 弹窗改 → 关闭 → 刷新」换成「列表选中 → 详情常驻」:一根 `activeId` / `activeData` 状态线打通列表与详情,宽屏并排 / 窄屏抽屉两形态按视口宽定档。
|
|
4
|
+
> 范式页:`apps/reference/src/pages/app-layout/list-detail/`
|
|
5
|
+
|
|
6
|
+
## 定位
|
|
7
|
+
|
|
8
|
+
- **页面级组件**:内含 `AppPage`,**页面根即 `.app-page`**。业务页面根**二选一**——普通页用 `AppPage`,列表详情页用本组件;[MUST NOT] 再套一层 `AppPage`,也 [MUST NOT] 塞进弹窗 / Tab。
|
|
9
|
+
- **何时用**:单条记录是主角的场景(工单、会话、任务、审核队列)——用户要在列表与单条之间反复横跳。
|
|
10
|
+
- **何时不用**:纯批量操作(导入导出、批量改状态)——那里单条不是主角,常规 `AppPage` + `ListLayout` 更合适。
|
|
11
|
+
|
|
12
|
+
## 层级
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
AppPageListDetailLayout 【L1 门面】无 DOM 输出:持状态 + 形态定档 + provide 上下文
|
|
16
|
+
├─ split → AppPageListDetailSplit 【L2】AppPage:#left = 列表 · 默认槽 = 详情区
|
|
17
|
+
└─ sheet → AppPageListDetailSheet 【L2】AppPage:默认槽 = 列表 · 抽屉 = 详情区
|
|
18
|
+
└─ AppPageListDetailPane 【L3】有当前项 → 详情;无 → 空态
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
三个实现件(Split / Sheet / Pane)**不对外导出**,是门面的实现细节。
|
|
22
|
+
|
|
23
|
+
## 快速上手
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<AppPageListDetailLayout
|
|
27
|
+
v-model:active-id="activeId"
|
|
28
|
+
v-model:active-data="activeData"
|
|
29
|
+
:detail-api="detailApi"
|
|
30
|
+
>
|
|
31
|
+
<template #list="{ listProps }">
|
|
32
|
+
<!-- ⚠️ listProps [MUST] 整体 v-bind,少接一条对应能力即静默失效 -->
|
|
33
|
+
<ListLayout v-bind="listProps" :api="listApi" :columns="columns" row-key="id" />
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<template #detail="{ data, record, refreshList }">
|
|
37
|
+
<MyDetail :detail="data" :record="record" @changed="refreshList" />
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<template #empty="{ listEmpty }">
|
|
41
|
+
<ElEmpty :description="listEmpty ? '没有符合条件的数据' : '请从列表选择一条'" />
|
|
42
|
+
</template>
|
|
43
|
+
</AppPageListDetailLayout>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 核心不变式:先有列表,才有详情
|
|
47
|
+
|
|
48
|
+
> **`activeId` 只能从列表内选择产生,[MUST NOT] 直接从链接取。**
|
|
49
|
+
> `activeData` 是**列表数据 item**(不是详情数据),两者**同生同灭**。
|
|
50
|
+
|
|
51
|
+
由此推出的行为(由列表族的 `useActiveRecord` 统一守,门面不重复接管):
|
|
52
|
+
|
|
53
|
+
| 触发 | 行为 |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| 用户点某项 | 两个 model 同时写 |
|
|
56
|
+
| 列表刷新 | 按 id 在新数据内重查刷新 `activeData`;**查不到 → 两者一并清空**(详情自动关闭) |
|
|
57
|
+
| 外部写不在列表内的 id | 忽略 + `console.warn`,**不清空既有选中** |
|
|
58
|
+
| 写 `activeData`(导航切上/下一条走此路) | 按行反查键同步 `activeId`;行不在列表内同样忽略 + warn |
|
|
59
|
+
|
|
60
|
+
**深链形态随之改变([MUST] 照做)**:链接 [MUST NOT] 携带「详情 id」直达详情,而是把 id 带进**搜索条件** → 列表搜出结果 → `autoActiveFirst` 激活第一条。两个代价需知情:ⓐ 搜索表单 [MUST] 有按 id 精确筛的字段(可隐藏);ⓑ 深链进入时列表通常只剩一条 → 上下条导航两端皆 false,清空搜索条件即恢复。
|
|
61
|
+
|
|
62
|
+
## 详情 ↔ 列表:单向 `refreshList`,无回写
|
|
63
|
+
|
|
64
|
+
**唯一机制**:详情侧判断「与列表 item 是否同步」,不同步则触发**列表刷新**,由列表接口出新数据。[MUST NOT] 由详情去写 `activeData`。
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// 推荐用法(业务侧详情组件内)
|
|
68
|
+
const isOutOfSync = computed(() => {
|
|
69
|
+
const d = props.detail, r = props.record;
|
|
70
|
+
if (!d || !r) return false;
|
|
71
|
+
// ⚠️ [MUST] 先确认「详情与当前 item 是同一条」再比字段 —— 切换当前项时详情尚未重拉回来,
|
|
72
|
+
// 旧详情 × 新 item 会被判成不同步,每切一次就误刷一次列表(实测踩坑)。
|
|
73
|
+
if (String(d.id) !== String(r.id)) return false;
|
|
74
|
+
return d.status !== r.status;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
watch(isOutOfSync, (v) => v && refreshList());
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- **判定 [MUST] 由业务自己写**(比业务关键字段):组件不可能知道哪些字段算关键;全字段比对会被 `updatedAt` 一类字段搞成每次都触发。
|
|
81
|
+
- **[MUST] 用 `watch` 的值变化语义**,[MUST NOT] 用 `watchEffect` 或在渲染期直接调 `refreshList()` —— 值变化语义正是止损点:刷新后对上了 `true→false` 收敛;仍对不上 `true→true` **值未变 → 不再触发**(「刷新了还是不同步就是接口的问题」,组件不重试)。
|
|
82
|
+
- 新建 / 删除等结构性变更同样走 `refreshList`,机制统一。
|
|
83
|
+
- ⚠️ 刷新后该记录可能因状态变化换了排序、跑出当前页 → 按不变式两值清空 → **详情自动关闭**。语义正确,但使用方需知情。
|
|
84
|
+
|
|
85
|
+
## API
|
|
86
|
+
|
|
87
|
+
> ⚠️ API 以 types.ts 与组件源码为真相源;与文档冲突时以源码为准。
|
|
88
|
+
|
|
89
|
+
### v-model
|
|
90
|
+
|
|
91
|
+
| 名 | type | 语义 |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| `activeId` | `string \| number \| undefined` | 当前项唯一键。`string` / `number` 两种都是一等公民,出口值保业务数据原始类型 |
|
|
94
|
+
| `activeData` | `T \| undefined` | 当前项对应的**列表 item**(恒为列表接口原样) |
|
|
95
|
+
|
|
96
|
+
### Props
|
|
97
|
+
|
|
98
|
+
| name | type | 默认 | 语义 |
|
|
99
|
+
| --- | --- | --- | --- |
|
|
100
|
+
| `mode` | `"auto" \| "split" \| "sheet"` | `"auto"` | 形态;`auto` 按视口宽 `splitAt` 定档 |
|
|
101
|
+
| `splitAt` | `number` | `1200` | `auto` 档切档阈值(px,**视口宽**)。iPad 竖屏 768 / Air 横屏 1180 落 sheet;Pro 12.9″ 横屏 1366 落 split |
|
|
102
|
+
| `listWidth` | `string` | `clamp(280px, 28%, 420px)` | 分栏态列表列宽。[MUST NOT] 写死 px —— 侧边栏折叠会改内容区可用宽 |
|
|
103
|
+
| `autoActiveFirst` | `boolean` | 跟随形态 | 列表出数后自动激活第一条。**split 默认 `true`**(宽屏右半屏不该空着)、**sheet 默认 `false`**(窄屏自动弹抽屉是打扰)。⚠️ 详情若有副作用(如「打开即标记已读」)[MUST] 显式关掉 |
|
|
104
|
+
| `queryKey` | `string` | 无 | 传了才把 `activeId` 同步进 URL query(**`replace` 不 `push`**)。复原仍走不变式校验 |
|
|
105
|
+
| `detailApi` | `(params?: { id }) => Promise<R>` | 无 | 传了则内置 `ViewLayout`(零配置拿到内置导航 + `refreshToken` 接线);不传则 `#detail` 原样渲染、门面零请求 |
|
|
106
|
+
| `detailTitle` | `string \| ((detailData, record) => string)` | 无 | 详情区标题。字符串固定 / 函数按数据算:`detailData` = 详情接口数据(**未传 `detailApi` 时恒 `undefined`**)、`record` = 当前项的列表 item(「详情即列表数据」形态靠它出标题)。⚠️ 渲染在**详情区顶部**(两形态一致),**不是** sheet 弹层的 header —— 详情数据只在详情区内部可得,弹层那层拿不到。不传则不渲染,业务照旧可在 `#detail` 自绘 |
|
|
107
|
+
| `sheetLayerType` | `"drawer" \| "modal"` | `"drawer"` | sheet 形态的弹层承载:抽屉 / 居中弹窗。透传 `ModalConfirm.layerType` |
|
|
108
|
+
| `sheetSize` | `string \| number` | `"70%"` | sheet 弹层尺寸(透传 `ModalConfirm.width`):drawer 下是抽屉尺寸、modal 下是弹窗宽度 |
|
|
109
|
+
|
|
110
|
+
**AppPage 的 props 靠 `$attrs` 自动穿透**(`gap` / 各 `*Bg` / `fullViewport` / `nextViewportMaxHeight` 等原样可用),无需白名单。
|
|
111
|
+
|
|
112
|
+
### Slots
|
|
113
|
+
|
|
114
|
+
| 槽 | scope | 语义 |
|
|
115
|
+
| --- | --- | --- |
|
|
116
|
+
| `#list` | `{ listProps }` | 列表。`listProps` [MUST] 整体 `v-bind` 到列表组件 |
|
|
117
|
+
| `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情。`data` / `loading` 仅在传了 `detailApi` 时有值 |
|
|
118
|
+
| `#empty` | `{ listEmpty }` | 未选中占位。`listEmpty=true` 列表本身无数据 / `false` 有数据但未选中,**两种文案 [MUST] 分流** |
|
|
119
|
+
| `#top` / `#bottom` / `#right` | 无 | 直通 `AppPage` 同名悬浮槽(**两形态都支持,`auto` 自动挡切档后依旧在**) |
|
|
120
|
+
|
|
121
|
+
> **为什么 `#right` 可用、`#left` 不可用**:门面对 `AppPage` 槽的占用是——Split 用 `#left` 放列表、**默认槽**放详情;Sheet 用**默认槽**放列表、**抽屉**放详情。两形态都**没占 `#right`**,故它原样开放给业务;`#left` 则是 Split 自用,不开放。
|
|
122
|
+
>
|
|
123
|
+
> ⚠️ **Sheet 形态下详情打开时抽屉会盖住右栏**:抽屉是 EP 弹层(z-index 2000+ 档)且默认 70% 宽,而 AppPage 悬浮槽是 `APP_PAGE_SLOT_Z_INDEX`=1。这是形态固有结果、**不视为缺陷**——侧栏承载的本就是次要信息(说明 / 辅助入口一类),被临时遮住无妨;[MUST NOT] 为此加避让或降级逻辑。
|
|
124
|
+
|
|
125
|
+
## 反模式 / 注意
|
|
126
|
+
|
|
127
|
+
- **业务外层再套 `AppPage`** —— 页面根二选一,套了就成两层
|
|
128
|
+
- **`#list` 里只 `v-bind` 一部分 `listProps`** —— 少接 `onDataChange` 则序列 / 自动激活失效、少接 `ref` 则 `refreshList` 失效,且**都不报错**
|
|
129
|
+
- **自己给列表加 `:ref` 覆盖掉 `listProps.ref`** —— 需要自己也拿实例时 [MUST] 链式调用:`:ref="(el) => { myRef = el; listProps.ref(el) }"`
|
|
130
|
+
- **运行时增删 `#top` / `#bottom` / `#right` / `#list`** —— `AppPage` 的槽集判定不响应运行时增删(既有限制),插槽集 [MUST] 当静态结构
|
|
131
|
+
- **不传 `detailApi` 时期待内置上/下一条** —— 导航住在 `ViewLayout` 内;不内置 `ViewLayout` 就没有内置导航,用 `#detail` scope 的 `goPrev` / `goNext` 自绘
|
|
132
|
+
- **删除 / 筛掉当前项后期待自动跳到下一条** —— 按不变式两值清空落空态(不自动重激活,避免自动触发详情副作用)
|
|
133
|
+
- **sheet 态用 scoped `:deep` 调抽屉内样式** —— 抽屉 teleport 到 body,继承 class 但**不继承 scopeId**,`:deep` 恒失效;验收 [MUST] 量 computed style
|
|
134
|
+
- **`activeId` 只从链接取** —— 违反不变式,见上「深链形态」
|
|
135
|
+
|
|
136
|
+
## 关联
|
|
137
|
+
|
|
138
|
+
- 内部件(不导出):`AppPageListDetailSplit` / `AppPageListDetailSheet` / `AppPageListDetailPane` / `use-list-detail.ts`
|
|
139
|
+
- 不变式内核:`hooks/use-active-record.ts`(`useActiveRecord`,列表族三家共用)
|
|
140
|
+
- 上下文 key:`APP_PAGE_LIST_DETAIL_CONTEXT`(导航句柄 + `refreshList`,`ViewLayout` 可选 inject 探测)
|
|
141
|
+
- 详情容器:`ViewLayout`(`refreshToken` 外部重拉口 + 内置导航 `showNavigation`)
|
|
142
|
+
- spec:`docs/specs/2026-08-31-025041-moderate-列表详情同页范式AppPageListDetailLayout/spec.md`
|
|
@@ -86,7 +86,7 @@ const toolbarConfig: TableToolbarConfig<Row> = {
|
|
|
86
86
|
| `viewportHeight` | `number` | — | 显式指定视口可用高(优先级最高);缺省走 SCOPE_VIEWPORT_MAX_HEIGHT scope 链 + fallback 0(500 floor 兜底) |
|
|
87
87
|
| `parentChannel` | `string` | — | 定向消费的祖先 scope 标识;setup 期快照,沿 scope 链找 scope === parentChannel 取高 |
|
|
88
88
|
|
|
89
|
-
**v-model**:`isAutoRefresh`(boolean)、`refreshInterval`(number,秒)、`customView`(boolean,默认 false,显式切 DataListView 替代 ElTable,需配 `#custom-view-item`
|
|
89
|
+
**v-model**:`isAutoRefresh`(boolean)、`refreshInterval`(number,秒)、`customView`(boolean,默认 false,显式切 DataListView 替代 ElTable,需配 `#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()` 归一,且**保留调用方写入的原值**不回写覆盖。ElTable 态由本组件接管(命令式 `setCurrentRow` 同步高亮——EP 的 `current-row-key` 置空不清高亮,故不用它);`customView` 态让位内层 `DataListView`
|
|
90
90
|
|
|
91
91
|
### Emits
|
|
92
92
|
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
| --- | --- | --- | --- |
|
|
64
64
|
| `api` | `(params?: P) => Promise<R>`(必填) | — | 数据拉取函数;每次拉取(激活 / 手动刷新 / 自动刷新)传入 props.params(未传收 undefined) |
|
|
65
65
|
| `params` | `P?` | — | 拉取参数;响应式变化不自动重拉(调 `scope.refresh()`) |
|
|
66
|
+
| `refreshToken` | `string \| number?` | — | **外部重拉触发口**:值变化即重拉一次,且不进 `params`(不发给后端)。变为 `undefined` 不触发;挂载时已有值也不额外拉(首拉由激活态承担)。默认不传 = 老行为逐字节不变 |
|
|
66
67
|
| `refine` | `boolean?` | 上下文默认 | 精细流:包 HeightProvider 精确扣 toolbar reserve(WatchSize 量高),内容区可用高 + scope viewportHeight;false=自然流。默认由 `useContextDefaults` 按直系父解析:AppPage `#default` / TabsMain 页签内容内 `true`,否则 `false`;显式传值优先 |
|
|
67
68
|
| `viewportHeight` | `number?` | — | 显式视口高,优先级最高;未传降级 inject SCOPE_VIEWPORT_MAX_HEIGHT scope 链 |
|
|
68
69
|
| `channel` | `string?` | — | 作为父时的 scope 标识。⚠️ 与 parentChannel 均 [MUST NOT] 响应式变更(setup 期快照);且仅 refine=true 路径生效 |
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
| `minHeight` | `number?` | `100` | 内容区可用高兜底下限(对齐 ListLayout/TabsMain 惯例) |
|
|
71
72
|
| `showAutoRefresh` | `boolean?` | `true` | 是否显示自动刷新开关(透传 AutoRefreshGroup) |
|
|
72
73
|
| `showRefresh` | `boolean?` | `true` | 是否显示手动刷新按钮(透传 AutoRefreshGroup) |
|
|
74
|
+
| `showNavigation` | `boolean?` | `true` | 是否显示内置「上一条 / 下一条」导航。⚠️ **真正的显隐判据是可选 inject 探测**:只有置于 `AppPageListDetailLayout` 内才渲染,单独使用 ViewLayout 时无论本值为何都不渲染(老用法零变化);本 prop 用于在门面内显式关掉 |
|
|
73
75
|
| `leftExtraButtons` | `ViewLayoutButtonConfig[]` | `[]` | 左侧额外按钮(`ActionBtnGroup` 配置,ctx = `{ loading, refresh }`,`default-size="small"` 对齐 table-toolbar) |
|
|
74
76
|
| `rightExtraButtons` | `ViewLayoutButtonConfig[]` | `[]` | 右侧额外按钮(置于 AutoRefreshGroup 之前) |
|
|
75
77
|
|
|
@@ -79,6 +81,15 @@
|
|
|
79
81
|
| --- | --- | --- |
|
|
80
82
|
| 默认 | `{ data, loading, refresh, viewportHeight }`(`ViewLayoutScope<R>`) | 展示内容;PanelMain 或自由编排皆可;viewportHeight refine 时给可用高、自然流 undefined |
|
|
81
83
|
|
|
84
|
+
### 内置上/下一条导航(仅列表详情范式内出现)
|
|
85
|
+
|
|
86
|
+
置于 `AppPageListDetailLayout` 内时,toolbar 右侧自动出现「上一条 / 下一条」+「第 N / M 条」读数。
|
|
87
|
+
|
|
88
|
+
- 位置 = **`rightExtraButtons` 之前(最靠左)**,`AutoRefreshGroup` 恒最右 —— 导航是**位置指示**不是业务动作,与业务按钮分档。
|
|
89
|
+
- 显隐 = **可选 inject 探测**(注入到才渲染);单独使用 ViewLayout 时完全不渲染,`hasToolbarContent` 判定也不受影响。
|
|
90
|
+
- 已纳入 `hasToolbarContent`:「只有上下条、其余全关」的页面 toolbar 仍可见。
|
|
91
|
+
- `ViewLayoutButtonContext` 同步扩展导航句柄(`hasPrev` / `hasNext` / `index` / `total` / `goPrev` / `goNext` / `refreshList`,**门面外恒为 `undefined`**),业务自定义按钮可据此做「保存并下一条」等组合动作。
|
|
92
|
+
|
|
82
93
|
### Expose
|
|
83
94
|
|
|
84
95
|
无
|
|
@@ -88,6 +99,9 @@
|
|
|
88
99
|
- **纯展示套 PanelMain 作页级根**:PanelMain 已废弃页级展示场景(见 panel 族文档)——纯展示在插槽内自由编排
|
|
89
100
|
- **channel / parentChannel 响应式变更**:[MUST NOT]——setup 期快照,后续变更无效;缺 refine 的 channel 链静默失效
|
|
90
101
|
- **api 抛错不吞**:api 内部自行处理错误(ViewLayout 只保证 loading 复位,不拦截异常)
|
|
102
|
+
- **要重拉不要改 `params` 指望它自动生效**:`params` 响应式变化**刻意**不重拉(防请求风暴的既有设计)。「换了参数要重拉」= 改 `params` **并**动 `refreshToken`(或调 `scope.refresh()`)
|
|
103
|
+
- **[MUST NOT] 把触发值塞进 `params` 当刷新开关**:那会把纯前端的令牌发给后端、污染 API 契约——这正是 `refreshToken` 独立成 prop 的原因
|
|
104
|
+
- **[MUST NOT] 用 `:key="token"` 重建来实现重拉**:会连 toolbar / 按钮一起重建,连续切换时闪烁,且语义错位(chrome 不该随内容重建)——用 `refreshToken`
|
|
91
105
|
- **toolbar 不含表格能力全集**:对齐的是 table-toolbar 的**结构与视觉惯例**(左右分栏 + extra 按钮 + AutoRefreshGroup),[MUST NOT] 期待导出 / 批量下载 / 视图切换等表格特有能力出现在 ViewLayout——需要表格能力用 `ListLayout` / `TableMain`
|
|
92
106
|
|
|
93
107
|
## 关联
|
|
@@ -30,6 +30,18 @@ type __VLS_Props = {
|
|
|
30
30
|
leftObserveResize?: boolean;
|
|
31
31
|
/** right 插槽 RO 观测开关 */
|
|
32
32
|
rightObserveResize?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* 超宽屏适配:默认 slot 内容限宽居中(默认关)。开启后内容盒宽收敛到 `contentMaxWidth`
|
|
35
|
+
* 并居中于「两侧槽之间的可用区」,同时同步收窄对后代公布的内容几何(inset / viewport-width)。
|
|
36
|
+
* 窄屏(可用宽 ≤ contentMaxWidth)留白为 0、与关闭态一致,无需断点判断。
|
|
37
|
+
* 适用表单 / 详情 / 阅读型页;列表 / 表格页 [MUST NOT] 开(列本就挤)。
|
|
38
|
+
*/
|
|
39
|
+
contentCentered?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* 限宽值(px,仅 `contentCentered` 开启时生效)。
|
|
42
|
+
* [MUST] 为 number——几何链三处出口需其参与数值运算,CSS 串会让样式与公布几何脱钩。
|
|
43
|
+
*/
|
|
44
|
+
contentMaxWidth?: number;
|
|
33
45
|
};
|
|
34
46
|
declare function __VLS_template(): {
|
|
35
47
|
attrs: Partial<{}>;
|
|
@@ -59,6 +71,8 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}
|
|
|
59
71
|
bottomObserveResize: boolean;
|
|
60
72
|
leftObserveResize: boolean;
|
|
61
73
|
rightObserveResize: boolean;
|
|
74
|
+
contentCentered: boolean;
|
|
75
|
+
contentMaxWidth: number;
|
|
62
76
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
63
77
|
appPageRef: HTMLDivElement;
|
|
64
78
|
}, HTMLDivElement>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ComputedRef } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* 超宽屏内容限宽留白(单侧 px,纯函数,钳非负)。
|
|
4
|
+
*
|
|
5
|
+
* `availableWidth`(内容可用宽 = AppPage 盒宽扣四向槽让位)超出 `maxWidth` 时,超出部分两侧均分;
|
|
6
|
+
* 未超出(窄屏 / 常规屏)恒 0 —— 关闭态与开启态在窄屏下逐字节一致,无需断点判断。
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeAppPageContentGutter(availableWidth: number, maxWidth: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* AppPage 超宽屏内容限宽居中(非主干功能,默认关;规则 22 同级 hook 收纳,**[MUST NOT] 出桶导出**)。
|
|
11
|
+
*
|
|
12
|
+
* 只出一个数 `contentGutter`(单侧留白 px,关闭态恒 0),主干三处消费即闭环:
|
|
13
|
+
* ⓐ `.app-page-shim` 左右 padding 各 `+ gutter`(内容盒宽收敛到 `maxWidth` 且精确居中);
|
|
14
|
+
* ⓑ `APP_PAGE_CONTENT_INSET` 的 `left`/`right` 各 `+ gutter`(后代 fixed / 悬浮贴真实内容列边);
|
|
15
|
+
* ⓒ `--dc-page-content-viewport-width` 减 `2 × gutter`(对后代公布的可用宽与实际同步)。
|
|
16
|
+
*
|
|
17
|
+
* **刻意不套限宽容器**:`.app-page-shim` 挂着 `data-dc-slot`(默认槽区域标记),`useContextDefaults`
|
|
18
|
+
* 读的是**直系父 DOM**——中间插一层容器会让 ListLayout 窄栏默认、ViewLayout/TabsMain `refine` 上下文
|
|
19
|
+
* 默认静默失效。padding 方案与套容器行为等价(居中基准同为「两侧槽之间的可用区」),且零 DOM 变更。
|
|
20
|
+
*
|
|
21
|
+
* @param options.enabled 开关(`contentCentered`),关闭恒出 0
|
|
22
|
+
* @param options.maxWidth 限宽值 px(`contentMaxWidth`)
|
|
23
|
+
* @param options.availableWidth 内容可用宽 px(AppPage 盒宽扣四向槽让位 = `computeAppPageReducedViewportWidth`)
|
|
24
|
+
*/
|
|
25
|
+
export declare function useAppPageContentBox(options: {
|
|
26
|
+
enabled: () => boolean;
|
|
27
|
+
maxWidth: () => number;
|
|
28
|
+
availableWidth: () => number;
|
|
29
|
+
}): {
|
|
30
|
+
contentGutter: ComputedRef<number>;
|
|
31
|
+
};
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { TableMainInstance, TablePageInfo } from '../table';
|
|
2
2
|
import { DataGridViewProps } from './types';
|
|
3
|
+
import { ActiveRecordKey } from '../../hooks/use-active-record';
|
|
3
4
|
declare const _default: <T extends Record<string, any>, SQ extends Record<string, any>, F extends Record<string, any>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
5
|
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
5
6
|
readonly "onUpdate:isAutoRefresh"?: ((value: boolean) => any) | undefined;
|
|
6
7
|
readonly onLoadingChange?: ((value: boolean) => any) | undefined;
|
|
7
8
|
readonly "onUpdate:refreshInterval"?: ((value: number) => any) | undefined;
|
|
9
|
+
readonly "onUpdate:activeId"?: ((value: ActiveRecordKey | undefined) => any) | undefined;
|
|
10
|
+
readonly "onUpdate:activeData"?: ((value: T | undefined) => any) | undefined;
|
|
8
11
|
readonly onPageChange?: ((page: number) => any) | undefined;
|
|
9
12
|
readonly onPageSizeChange?: ((page: number) => any) | undefined;
|
|
10
13
|
readonly onDataChange?: ((payload: T[]) => any) | undefined;
|
|
11
14
|
readonly onPageInfoChange?: ((info: TablePageInfo) => any) | undefined;
|
|
12
15
|
readonly onSelectionChange?: ((payload: T[]) => any) | undefined;
|
|
13
|
-
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:isAutoRefresh" | "onLoadingChange" | "onUpdate:refreshInterval" | "onPageChange" | "onPageSizeChange" | "onDataChange" | "onPageInfoChange" | "onSelectionChange"> & ({
|
|
16
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:isAutoRefresh" | "onLoadingChange" | "onUpdate:refreshInterval" | "onUpdate:activeId" | "onUpdate:activeData" | "onPageChange" | "onPageSizeChange" | "onDataChange" | "onPageInfoChange" | "onSelectionChange"> & ({
|
|
14
17
|
isAutoRefresh?: boolean;
|
|
15
18
|
refreshInterval?: number;
|
|
19
|
+
activeId?: ActiveRecordKey | undefined;
|
|
20
|
+
activeData?: T | undefined;
|
|
16
21
|
} & DataGridViewProps<T, SQ, F>) & Partial<{}>> & import('vue').PublicProps;
|
|
17
22
|
expose(exposed: import('vue').ShallowUnwrapRef<TableMainInstance>): void;
|
|
18
23
|
attrs: any;
|
|
@@ -23,6 +28,8 @@ declare const _default: <T extends Record<string, any>, SQ extends Record<string
|
|
|
23
28
|
index: number;
|
|
24
29
|
selected: boolean;
|
|
25
30
|
toggleSelect: () => void;
|
|
31
|
+
active: boolean;
|
|
32
|
+
setActive: () => void;
|
|
26
33
|
}): any;
|
|
27
34
|
};
|
|
28
35
|
emit: {
|
|
@@ -30,7 +37,7 @@ declare const _default: <T extends Record<string, any>, SQ extends Record<string
|
|
|
30
37
|
(e: "dataChange" | "selectionChange", payload: T[]): void;
|
|
31
38
|
(e: "loadingChange", value: boolean): void;
|
|
32
39
|
(e: "pageInfoChange", info: TablePageInfo): void;
|
|
33
|
-
} & (((evt: "update:isAutoRefresh", value: boolean) => void) & ((evt: "update:refreshInterval", value: number) => void));
|
|
40
|
+
} & (((evt: "update:isAutoRefresh", value: boolean) => void) & ((evt: "update:refreshInterval", value: number) => void) & ((evt: "update:activeId", value: ActiveRecordKey | undefined) => void) & ((evt: "update:activeData", value: T | undefined) => void));
|
|
34
41
|
}>) => import('vue').VNode & {
|
|
35
42
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
36
43
|
};
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { DataListViewProps } from './types';
|
|
2
|
+
import { ActiveRecordKey } from '../../hooks/use-active-record';
|
|
2
3
|
declare const _default: <T extends Record<string, any>, F extends Record<string, any>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
4
|
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
5
|
+
readonly "onUpdate:activeId"?: ((value: ActiveRecordKey | undefined) => any) | undefined;
|
|
6
|
+
readonly "onUpdate:activeData"?: ((value: T | undefined) => any) | undefined;
|
|
4
7
|
readonly onSelectionChange?: ((rows: T[]) => any) | undefined;
|
|
5
|
-
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "
|
|
8
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:activeId" | "onUpdate:activeData" | "onSelectionChange"> & ({
|
|
9
|
+
activeId?: ActiveRecordKey | undefined;
|
|
10
|
+
activeData?: T | undefined;
|
|
11
|
+
} & DataListViewProps<T, F>) & Partial<{}>> & import('vue').PublicProps;
|
|
6
12
|
expose(exposed: import('vue').ShallowUnwrapRef<import('../table').TableInstance>): void;
|
|
7
13
|
attrs: any;
|
|
8
14
|
slots: {
|
|
@@ -12,10 +18,12 @@ declare const _default: <T extends Record<string, any>, F extends Record<string,
|
|
|
12
18
|
index: number;
|
|
13
19
|
selected: boolean;
|
|
14
20
|
toggleSelect: () => void;
|
|
21
|
+
active: boolean;
|
|
22
|
+
setActive: () => void;
|
|
15
23
|
}): any;
|
|
16
24
|
empty?(_: {}): any;
|
|
17
25
|
};
|
|
18
|
-
emit: (e: "selectionChange", rows: T[]) => void;
|
|
26
|
+
emit: ((e: "selectionChange", rows: T[]) => void) & (((evt: "update:activeId", value: ActiveRecordKey | undefined) => void) & ((evt: "update:activeData", value: T | undefined) => void));
|
|
19
27
|
}>) => import('vue').VNode & {
|
|
20
28
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
21
29
|
};
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DataListView 单项容器
|
|
3
3
|
* ---
|
|
4
|
-
*
|
|
4
|
+
* 仅承载选中(勾选)/ 当前项(active)两个 class 钩子 + 项内容插槽,由 DataListView 内部 v-for 使用。
|
|
5
5
|
* [MUST NOT] 写死视觉样式(卡片阴影/圆角等归消费方);
|
|
6
6
|
* [MUST NOT] 内置选中状态机(状态机在 DataListView)。
|
|
7
7
|
*/
|
|
8
8
|
type __VLS_Props = {
|
|
9
|
-
/**
|
|
9
|
+
/** 当前项是否选中(勾选语义,由 DataListView 传入) */
|
|
10
10
|
selected: boolean;
|
|
11
|
+
/** 当前项是否为「当前项」(active = 用户点击/激活的那一条,与勾选无关) */
|
|
12
|
+
active?: boolean;
|
|
11
13
|
};
|
|
12
14
|
declare function __VLS_template(): {
|
|
13
15
|
attrs: Partial<{}>;
|
|
@@ -68,10 +68,14 @@ export interface DataListViewItemScope<T extends Record<string, any> = Record<st
|
|
|
68
68
|
row: T;
|
|
69
69
|
/** 当前行索引 */
|
|
70
70
|
index: number;
|
|
71
|
-
/**
|
|
71
|
+
/** 当前行是否选中(勾选语义,与 active 无关) */
|
|
72
72
|
selected: boolean;
|
|
73
73
|
/** 翻转当前行选中态 */
|
|
74
74
|
toggleSelect: () => void;
|
|
75
|
+
/** 当前行是否为「当前项」(active,= 用户点击/激活的那一条;与勾选 selected 是两回事) */
|
|
76
|
+
active: boolean;
|
|
77
|
+
/** 把当前行设为「当前项」(等价于点击该项) */
|
|
78
|
+
setActive: () => void;
|
|
75
79
|
}
|
|
76
80
|
/** InfiniteListView 配置(经 DataGridView.infiniteListProps 透传) */
|
|
77
81
|
export interface InfiniteListViewConfig {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FormItemConfigList, FormScope, FormSearchSearchType } from '../form';
|
|
2
2
|
import { TableColumnDefaultScope, TableColumnHeaderScope, TablePageInfo } from '../table';
|
|
3
|
+
import { ActiveRecordKey } from '../../hooks/use-active-record';
|
|
3
4
|
import { ListLayoutInnerInjectInfo, ListLayoutInstance } from './types';
|
|
4
5
|
import { VNode } from 'vue';
|
|
5
6
|
import { DataListViewItemScope } from '../data-view';
|
|
@@ -8,12 +9,16 @@ declare const _default: <T extends Record<string, any>, PO extends Record<string
|
|
|
8
9
|
readonly "onUpdate:isAutoRefresh"?: ((value: boolean) => any) | undefined;
|
|
9
10
|
readonly "onUpdate:refreshInterval"?: ((value: number) => any) | undefined;
|
|
10
11
|
readonly "onUpdate:customView"?: ((value: boolean) => any) | undefined;
|
|
12
|
+
readonly "onUpdate:activeId"?: ((value: ActiveRecordKey | undefined) => any) | undefined;
|
|
13
|
+
readonly "onUpdate:activeData"?: ((value: T | undefined) => any) | undefined;
|
|
11
14
|
readonly onDataChange?: ((data: T[]) => any) | undefined;
|
|
12
15
|
readonly onPageInfoChange?: ((info: TablePageInfo) => any) | undefined;
|
|
13
|
-
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:isAutoRefresh" | "onUpdate:refreshInterval" | "onUpdate:customView" | "onDataChange" | "onPageInfoChange"> & ({
|
|
16
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:isAutoRefresh" | "onUpdate:refreshInterval" | "onUpdate:customView" | "onUpdate:activeId" | "onUpdate:activeData" | "onDataChange" | "onPageInfoChange"> & ({
|
|
14
17
|
isAutoRefresh?: boolean;
|
|
15
18
|
refreshInterval?: number;
|
|
16
19
|
customView?: boolean;
|
|
20
|
+
activeId?: ActiveRecordKey | undefined;
|
|
21
|
+
activeData?: T | undefined;
|
|
17
22
|
} & Partial<Pick<import('../form').FormSearchProps<PO, SO, {}>, "staticQuery">> & {
|
|
18
23
|
formSearchProps?: Partial<Omit<import('../form').FormSearchProps<PO, SO, {}>, "staticQuery" | "compact" | "list">> | undefined;
|
|
19
24
|
} & Pick<import('../table').TableMainProps<T, import('../form').ExcludeNeverProperties<import('../form').ExtractFormStringifyRawFromObject<PO, SO>> & SQ, ListLayoutInnerInjectInfo<PO, F>>, import('./types').TableMainRequiredKey> & Partial<Pick<import('../table').TableMainProps<T, import('../form').ExcludeNeverProperties<import('../form').ExtractFormStringifyRawFromObject<PO, SO>> & SQ, ListLayoutInnerInjectInfo<PO, F>>, import('./types').TableMainOptionsKey>> & {
|
|
@@ -66,7 +71,7 @@ declare const _default: <T extends Record<string, any>, PO extends Record<string
|
|
|
66
71
|
emit: {
|
|
67
72
|
(e: "dataChange", data: T[]): void;
|
|
68
73
|
(e: "pageInfoChange", info: TablePageInfo): void;
|
|
69
|
-
} & (((evt: "update:isAutoRefresh", value: boolean) => void) & ((evt: "update:refreshInterval", value: number) => void) & ((evt: "update:customView", value: boolean) => void));
|
|
74
|
+
} & (((evt: "update:isAutoRefresh", value: boolean) => void) & ((evt: "update:refreshInterval", value: number) => void) & ((evt: "update:customView", value: boolean) => void) & ((evt: "update:activeId", value: ActiveRecordKey | undefined) => void) & ((evt: "update:activeData", value: T | undefined) => void));
|
|
70
75
|
}>) => import('vue').VNode & {
|
|
71
76
|
__ctx?: Awaited<typeof __VLS_setup>;
|
|
72
77
|
};
|
|
@@ -12,6 +12,22 @@ type __VLS_Props = {
|
|
|
12
12
|
confirmText?: string | (() => VNode);
|
|
13
13
|
/** 是否隐藏取消按钮 */
|
|
14
14
|
hiddenCancel?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* 整块隐藏 header(标题区 + 关闭按钮),默认 false。
|
|
17
|
+
* 两形态的实现不同(EP 使然,非本组件选择):
|
|
18
|
+
* - drawer:走 EP 的 `with-header=false` → **节点真不渲染**;
|
|
19
|
+
* - modal:EP 的 `el-dialog__header` **无条件渲染**(`dialog-content` 里 `createElementVNode("header")`
|
|
20
|
+
* 不带任何条件),故只能由 `dc-confirm-modal_hidden-header` 类 `display:none` 兜。
|
|
21
|
+
* ⚠️ 隐藏 header 同时失去关闭按钮 —— [MUST] 自行提供关闭入口(遮罩 / ESC / 自绘按钮)。
|
|
22
|
+
*/
|
|
23
|
+
hiddenHeader?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 整块隐藏 footer(操作区),默认 false。用于「只要弹层容器、不要确认-取消」的场景
|
|
26
|
+
* (如列表详情范式的详情抽屉)。
|
|
27
|
+
* dialog / drawer 都是按 footer 插槽存在与否渲染容器,故此处 **v-if 省掉插槽 = 真不渲染**,
|
|
28
|
+
* [MUST NOT] 改用「传空 #footer 插槽」绕 —— 那会留一条带 padding 的空条(drawer 实测 56px)。
|
|
29
|
+
*/
|
|
30
|
+
hiddenFooter?: boolean;
|
|
15
31
|
width?: number | string;
|
|
16
32
|
/** 反转按钮位置 */
|
|
17
33
|
reverse?: boolean;
|
|
@@ -84,6 +100,8 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {}, {}
|
|
|
84
100
|
cancelText: string | (() => VNode);
|
|
85
101
|
confirmText: string | (() => VNode);
|
|
86
102
|
hiddenCancel: boolean;
|
|
103
|
+
hiddenHeader: boolean;
|
|
104
|
+
hiddenFooter: boolean;
|
|
87
105
|
closeOnPressEscape: boolean;
|
|
88
106
|
closeOnClickModal: boolean;
|
|
89
107
|
layerType: "modal" | "drawer";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ActiveRecordKey } from '../../hooks/use-active-record';
|
|
2
|
+
import { AppPageListDetailDetailScope, AppPageListDetailEmptyScope, AppPageListDetailProps } from './types';
|
|
3
|
+
declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, never> & {
|
|
5
|
+
/**
|
|
6
|
+
* 完整详情上下文(含序列导航句柄)。**门面内嵌时下发**;
|
|
7
|
+
* 独立使用时不传,改用下方 `id` / `record`。
|
|
8
|
+
*/
|
|
9
|
+
ctx?: Omit<AppPageListDetailDetailScope<T, R>, "data" | "loading">;
|
|
10
|
+
/** 独立使用时的当前项键(`ctx` 未传时生效;与 `ctx` 都无 → 落空态) */
|
|
11
|
+
id?: ActiveRecordKey;
|
|
12
|
+
/** 独立使用时的当前项数据(`ctx` 未传时生效),供 `detailTitle` / `#detail` 消费 */
|
|
13
|
+
record?: T;
|
|
14
|
+
/** 列表是否为空(分流两种空态文案);独立使用时通常不关心 */
|
|
15
|
+
listEmpty?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 内嵌态:`true` 则不套 `AppPage`(宿主已有一层)。默认 `false` = 自带 `AppPage`、可独立成页。
|
|
18
|
+
* ⚠️ 内嵌态下本组件无宿主元素,`dc-app-page-detail` 根类与 `$attrs` 随之无落点。
|
|
19
|
+
*/
|
|
20
|
+
embedded?: boolean;
|
|
21
|
+
detailApi?: AppPageListDetailProps<R>["detailApi"];
|
|
22
|
+
detailTitle?: AppPageListDetailProps<R>["detailTitle"];
|
|
23
|
+
} & Partial<{}>> & import('vue').PublicProps;
|
|
24
|
+
expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
|
|
25
|
+
attrs: any;
|
|
26
|
+
slots: Readonly<{
|
|
27
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
28
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
29
|
+
}> & {
|
|
30
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
31
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
32
|
+
};
|
|
33
|
+
emit: {};
|
|
34
|
+
}>) => import('vue').VNode & {
|
|
35
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
36
|
+
};
|
|
37
|
+
export default _default;
|
|
38
|
+
type __VLS_PrettifyLocal<T> = {
|
|
39
|
+
[K in keyof T]: T[K];
|
|
40
|
+
} & {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ActiveRecordKey } from '../../hooks/use-active-record';
|
|
2
|
+
import { AppPageListDetailDetailScope, AppPageListDetailEmptyScope, AppPageListDetailListProps, AppPageListDetailProps } from './types';
|
|
3
|
+
declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
4
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
5
|
+
readonly "onUpdate:activeId"?: ((value: ActiveRecordKey | undefined) => any) | undefined;
|
|
6
|
+
readonly "onUpdate:activeData"?: ((value: T | undefined) => any) | undefined;
|
|
7
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onUpdate:activeId" | "onUpdate:activeData"> & ({
|
|
8
|
+
activeId?: ActiveRecordKey | undefined;
|
|
9
|
+
activeData?: T | undefined;
|
|
10
|
+
} & AppPageListDetailProps<R>) & Partial<{}>> & import('vue').PublicProps;
|
|
11
|
+
expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
|
|
12
|
+
attrs: any;
|
|
13
|
+
slots: Readonly<{
|
|
14
|
+
list?: (scope: {
|
|
15
|
+
listProps: AppPageListDetailListProps<T>;
|
|
16
|
+
}) => unknown;
|
|
17
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
18
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
19
|
+
top?: () => unknown;
|
|
20
|
+
bottom?: () => unknown;
|
|
21
|
+
right?: () => unknown;
|
|
22
|
+
}> & {
|
|
23
|
+
list?: (scope: {
|
|
24
|
+
listProps: AppPageListDetailListProps<T>;
|
|
25
|
+
}) => unknown;
|
|
26
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
27
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
28
|
+
top?: () => unknown;
|
|
29
|
+
bottom?: () => unknown;
|
|
30
|
+
right?: () => unknown;
|
|
31
|
+
};
|
|
32
|
+
emit: ((evt: "update:activeId", value: ActiveRecordKey | undefined) => void) & ((evt: "update:activeData", value: T | undefined) => void);
|
|
33
|
+
}>) => import('vue').VNode & {
|
|
34
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
35
|
+
};
|
|
36
|
+
export default _default;
|
|
37
|
+
type __VLS_PrettifyLocal<T> = {
|
|
38
|
+
[K in keyof T]: T[K];
|
|
39
|
+
} & {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AppPageListDetailDetailScope, AppPageListDetailEmptyScope, AppPageListDetailListProps, AppPageListDetailProps } from './types';
|
|
2
|
+
declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
+
props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
|
|
4
|
+
readonly onClose?: (() => any) | undefined;
|
|
5
|
+
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onClose"> & {
|
|
6
|
+
listProps: AppPageListDetailListProps<T>;
|
|
7
|
+
ctx: Omit<AppPageListDetailDetailScope<T, R>, "data" | "loading">;
|
|
8
|
+
listEmpty: boolean;
|
|
9
|
+
sheetLayerType: "drawer" | "modal";
|
|
10
|
+
sheetSize: string | number;
|
|
11
|
+
/** 本形态不消费,但 [MUST] 声明(理由同 Split 的 `sheetSize`:防 attr 泄漏成 DOM 属性) */
|
|
12
|
+
listWidth?: string;
|
|
13
|
+
detailApi?: AppPageListDetailProps<R>["detailApi"];
|
|
14
|
+
detailTitle?: AppPageListDetailProps<R>["detailTitle"];
|
|
15
|
+
} & Partial<{}>> & import('vue').PublicProps;
|
|
16
|
+
expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
|
|
17
|
+
attrs: any;
|
|
18
|
+
slots: Readonly<{
|
|
19
|
+
list?: (scope: {
|
|
20
|
+
listProps: AppPageListDetailListProps<T>;
|
|
21
|
+
}) => unknown;
|
|
22
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
23
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
24
|
+
top?: () => unknown;
|
|
25
|
+
bottom?: () => unknown;
|
|
26
|
+
right?: () => unknown;
|
|
27
|
+
}> & {
|
|
28
|
+
list?: (scope: {
|
|
29
|
+
listProps: AppPageListDetailListProps<T>;
|
|
30
|
+
}) => unknown;
|
|
31
|
+
detail?: (scope: AppPageListDetailDetailScope<T, R>) => unknown;
|
|
32
|
+
empty?: (scope: AppPageListDetailEmptyScope) => unknown;
|
|
33
|
+
top?: () => unknown;
|
|
34
|
+
bottom?: () => unknown;
|
|
35
|
+
right?: () => unknown;
|
|
36
|
+
};
|
|
37
|
+
emit: (e: "close") => void;
|
|
38
|
+
}>) => import('vue').VNode & {
|
|
39
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
40
|
+
};
|
|
41
|
+
export default _default;
|
|
42
|
+
type __VLS_PrettifyLocal<T> = {
|
|
43
|
+
[K in keyof T]: T[K];
|
|
44
|
+
} & {};
|