@done-coding/admin-core 0.26.0 → 0.26.1

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.
@@ -1,7 +1,7 @@
1
1
  import _sfc_main from "./AppPageDetail.vue2.mjs";
2
2
  /* empty css */
3
3
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
4
- const AppPageDetail = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-8de03183"]]);
4
+ const AppPageDetail = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5bc3d1c4"]]);
5
5
  export {
6
6
  AppPageDetail as default
7
7
  };
@@ -1,4 +1,4 @@
1
- import { defineComponent, computed, openBlock, createBlock, unref, withCtx, createElementBlock, toDisplayString, createCommentVNode, renderSlot, normalizeProps, guardReactiveProps, mergeProps, createVNode } from "vue";
1
+ import { defineComponent, computed, openBlock, createBlock, unref, withCtx, createElementBlock, toDisplayString, createCommentVNode, mergeProps, renderSlot, normalizeProps, guardReactiveProps, createVNode } from "vue";
2
2
  import { ElEmpty } from "element-plus";
3
3
  import AppPage from "../app-layout/AppPage.vue.mjs";
4
4
  import _sfc_main$1 from "../slot-layout/SlotLayoutTemplate.vue.mjs";
@@ -21,7 +21,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
21
21
  listEmpty: { type: Boolean, default: false },
22
22
  embedded: { type: Boolean, default: false },
23
23
  detailApi: {},
24
- detailTitle: {}
24
+ detailTitle: {},
25
+ viewLayoutProps: {}
25
26
  },
26
27
  setup(__props) {
27
28
  const props = __props;
@@ -61,18 +62,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
61
62
  }, {
62
63
  default: withCtx(() => [
63
64
  ctxFinal.value.id !== void 0 && !__props.detailApi && titleOf() ? (openBlock(), createElementBlock("div", _hoisted_1, toDisplayString(titleOf()), 1)) : createCommentVNode("", true),
64
- ctxFinal.value.id !== void 0 && __props.detailApi ? (openBlock(), createBlock(unref(ViewLayout), {
65
- key: 1,
65
+ ctxFinal.value.id !== void 0 && __props.detailApi ? (openBlock(), createBlock(unref(ViewLayout), mergeProps({ key: 1 }, __props.viewLayoutProps, {
66
66
  api: __props.detailApi,
67
67
  params: detailParams.value,
68
68
  refreshToken: ctxFinal.value.id
69
- }, {
69
+ }), {
70
70
  default: withCtx(({ data, loading }) => [
71
71
  titleOf(data) ? (openBlock(), createElementBlock("div", _hoisted_2, toDisplayString(titleOf(data)), 1)) : createCommentVNode("", true),
72
72
  renderSlot(_ctx.$slots, "detail", normalizeProps(guardReactiveProps(detailScope(data, loading))), void 0, true)
73
73
  ]),
74
74
  _: 3
75
- }, 8, ["api", "params", "refreshToken"])) : ctxFinal.value.id !== void 0 ? renderSlot(_ctx.$slots, "detail", normalizeProps(mergeProps({ key: 2 }, detailScope())), void 0, true) : renderSlot(_ctx.$slots, "empty", {
75
+ }, 16, ["api", "params", "refreshToken"])) : ctxFinal.value.id !== void 0 ? renderSlot(_ctx.$slots, "detail", normalizeProps(mergeProps({ key: 2 }, detailScope())), void 0, true) : renderSlot(_ctx.$slots, "empty", {
76
76
  key: 3,
77
77
  listEmpty: __props.listEmpty
78
78
  }, () => [
@@ -14,6 +14,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
14
14
  queryKey: {},
15
15
  detailApi: {},
16
16
  detailTitle: {},
17
+ viewLayoutProps: {},
17
18
  sheetLayerType: { default: "drawer" },
18
19
  sheetSize: { default: "70%" }
19
20
  }, {
@@ -100,6 +101,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
100
101
  sheetSize: __props.sheetSize,
101
102
  detailApi: __props.detailApi,
102
103
  detailTitle: __props.detailTitle,
104
+ viewLayoutProps: __props.viewLayoutProps,
103
105
  onClose: onSheetClose
104
106
  }, createSlots({ _: 2 }, [
105
107
  _ctx.$slots.list ? {
@@ -144,7 +146,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
144
146
  ]),
145
147
  key: "5"
146
148
  } : void 0
147
- ]), 1064, ["listProps", "ctx", "listEmpty", "listWidth", "sheetLayerType", "sheetSize", "detailApi", "detailTitle"]);
149
+ ]), 1064, ["listProps", "ctx", "listEmpty", "listWidth", "sheetLayerType", "sheetSize", "detailApi", "detailTitle", "viewLayoutProps"]);
148
150
  };
149
151
  }
150
152
  });
@@ -13,7 +13,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
13
13
  sheetSize: {},
14
14
  listWidth: {},
15
15
  detailApi: { type: Function },
16
- detailTitle: { type: [String, Function] }
16
+ detailTitle: { type: [String, Function] },
17
+ viewLayoutProps: {}
17
18
  },
18
19
  emits: ["close"],
19
20
  setup(__props, { emit: __emit }) {
@@ -43,7 +44,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
43
44
  ctx: __props.ctx,
44
45
  listEmpty: __props.listEmpty,
45
46
  detailApi: __props.detailApi,
46
- detailTitle: __props.detailTitle
47
+ detailTitle: __props.detailTitle,
48
+ viewLayoutProps: __props.viewLayoutProps
47
49
  }, createSlots({ _: 2 }, [
48
50
  _ctx.$slots.detail ? {
49
51
  name: "detail",
@@ -59,7 +61,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
59
61
  ]),
60
62
  key: "1"
61
63
  } : void 0
62
- ]), 1032, ["ctx", "listEmpty", "detailApi", "detailTitle"])
64
+ ]), 1032, ["ctx", "listEmpty", "detailApi", "detailTitle", "viewLayoutProps"])
63
65
  ]),
64
66
  _: 3
65
67
  }, 8, ["layerType", "show", "width"])
@@ -1,7 +1,7 @@
1
1
  import _sfc_main from "./AppPageListDetailSplit.vue2.mjs";
2
2
  /* empty css */
3
3
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
4
- const AppPageListDetailSplit = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-19f0f56d"]]);
4
+ const AppPageListDetailSplit = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7a3617de"]]);
5
5
  export {
6
6
  AppPageListDetailSplit as default
7
7
  };
@@ -12,7 +12,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
12
12
  sheetSize: {},
13
13
  sheetLayerType: {},
14
14
  detailApi: { type: Function },
15
- detailTitle: { type: [String, Function] }
15
+ detailTitle: { type: [String, Function] },
16
+ viewLayoutProps: {}
16
17
  },
17
18
  setup(__props) {
18
19
  const props = __props;
@@ -33,7 +34,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
33
34
  ctx: __props.ctx,
34
35
  listEmpty: __props.listEmpty,
35
36
  detailApi: __props.detailApi,
36
- detailTitle: __props.detailTitle
37
+ detailTitle: __props.detailTitle,
38
+ viewLayoutProps: __props.viewLayoutProps
37
39
  }, createSlots({ _: 2 }, [
38
40
  _ctx.$slots.detail ? {
39
41
  name: "detail",
@@ -49,7 +51,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
49
51
  ]),
50
52
  key: "1"
51
53
  } : void 0
52
- ]), 1032, ["ctx", "listEmpty", "detailApi", "detailTitle"])
54
+ ]), 1032, ["ctx", "listEmpty", "detailApi", "detailTitle", "viewLayoutProps"])
53
55
  ]),
54
56
  _: 2
55
57
  }, [
package/es/style.css CHANGED
@@ -145,14 +145,14 @@ to {
145
145
  .view-layout-content[data-v-8eb550ba] {
146
146
  overflow: auto;
147
147
  }
148
- .dc-app-page-detail__title[data-v-8de03183] {
148
+ .dc-app-page-detail__title[data-v-5bc3d1c4] {
149
149
  padding-bottom: 12px;
150
150
  font-size: 16px;
151
151
  font-weight: 600;
152
152
  line-height: 24px;
153
153
  color: var(--dc-core-text-color, var(--el-text-color-primary));
154
154
  }
155
- .dc-app-page-list-detail-split__list[data-v-19f0f56d] {
155
+ .dc-app-page-list-detail-split__list[data-v-7a3617de] {
156
156
  box-sizing: border-box;
157
157
  height: 100%;
158
158
  overflow: hidden;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@done-coding/admin-core",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "内部前端库",
5
5
  "private": false,
6
6
  "main": "es/index.mjs",
@@ -83,5 +83,5 @@
83
83
  "dependencies": {
84
84
  "@tanstack/vue-virtual": "^3.13.35"
85
85
  },
86
- "gitHead": "57af30147986e57cbf53b07e1470448ddfc13661"
86
+ "gitHead": "413474bbb4f32f343d3cfa990815985518729ef1"
87
87
  }
@@ -27,22 +27,23 @@
27
27
 
28
28
  ### Props
29
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` | 分流两种空态文案;独立使用时通常不关心 |
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
+ | `viewLayoutProps` | `Omit<ViewLayoutProps, "api" \| "params" \| "refreshToken">` | 无 | 透传给内置 `ViewLayout` props——**详情侧的业务操作按钮走这里**:`{ leftExtraButtons: [...] }` 落到 ViewLayout 工具条左侧、与右侧刷新按钮组同一行(`rightExtraButtons` 则置于 `AutoRefreshGroup` 之前)。🔴 `api` / `params` / `refreshToken` 三键被 `Omit`:它们分别由 `detailApi` / `{ id: activeId }` / `activeId` 占位,是「详情跟着当前项走」这条不变式的载体,放开任一个都能让主干**静默失效**(页面看着正常,详情停在上一条)。**未传 `detailApi` 时无内置 ViewLayout,本项无落点** |
39
+ | `listEmpty` | `boolean` | `false` | 分流两种空态文案;独立使用时通常不关心 |
39
40
 
40
41
  ### Slots
41
42
 
42
- | 槽 | scope | 语义 |
43
- | --- | --- | --- |
43
+ | 槽 | scope | 语义 |
44
+ | --------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
44
45
  | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情内容。`data` / `loading` 仅在传了 `detailApi` 时有值;独立使用时序列字段为零值 |
45
- | `#empty` | `{ listEmpty }` | 无当前项时的占位 |
46
+ | `#empty` | `{ listEmpty }` | 无当前项时的占位 |
46
47
 
47
48
  ## 反模式 / 注意
48
49
 
@@ -45,17 +45,16 @@ AppPageListDetailLayout 【L1 门面】无 DOM 输出:持状态 + 形
45
45
 
46
46
  ## 核心不变式:先有列表,才有详情
47
47
 
48
- > **`activeId` 只能从列表内选择产生,[MUST NOT] 直接从链接取。**
49
- > `activeData` 是**列表数据 item**(不是详情数据),两者**同生同灭**。
48
+ > **`activeId` 只能从列表内选择产生,[MUST NOT] 直接从链接取。** > `activeData` 是**列表数据 item**(不是详情数据),两者**同生同灭**。
50
49
 
51
50
  由此推出的行为(由列表族的 `useActiveRecord` 统一守,门面不重复接管):
52
51
 
53
- | 触发 | 行为 |
54
- | --- | --- |
55
- | 用户点某项 | 两个 model 同时写 |
56
- | 列表刷新 | 按 id 在新数据内重查刷新 `activeData`;**查不到 → 两者一并清空**(详情自动关闭) |
57
- | 外部写不在列表内的 id | 忽略 + `console.warn`,**不清空既有选中** |
58
- | 写 `activeData`(导航切上/下一条走此路) | 按行反查键同步 `activeId`;行不在列表内同样忽略 + warn |
52
+ | 触发 | 行为 |
53
+ | ---------------------------------------- | -------------------------------------------------------------------------------- |
54
+ | 用户点某项 | 两个 model 同时写 |
55
+ | 列表刷新 | 按 id 在新数据内重查刷新 `activeData`;**查不到 → 两者一并清空**(详情自动关闭) |
56
+ | 外部写不在列表内的 id | 忽略 + `console.warn`,**不清空既有选中** |
57
+ | 写 `activeData`(导航切上/下一条走此路) | 按行反查键同步 `activeId`;行不在列表内同样忽略 + warn |
59
58
 
60
59
  **深链形态随之改变([MUST] 照做)**:链接 [MUST NOT] 携带「详情 id」直达详情,而是把 id 带进**搜索条件** → 列表搜出结果 → `autoActiveFirst` 激活第一条。两个代价需知情:ⓐ 搜索表单 [MUST] 有按 id 精确筛的字段(可隐藏);ⓑ 深链进入时列表通常只剩一条 → 上下条导航两端皆 false,清空搜索条件即恢复。
61
60
 
@@ -66,7 +65,8 @@ AppPageListDetailLayout 【L1 门面】无 DOM 输出:持状态 + 形
66
65
  ```ts
67
66
  // 推荐用法(业务侧详情组件内)
68
67
  const isOutOfSync = computed(() => {
69
- const d = props.detail, r = props.record;
68
+ const d = props.detail,
69
+ r = props.record;
70
70
  if (!d || !r) return false;
71
71
  // ⚠️ [MUST] 先确认「详情与当前 item 是同一条」再比字段 —— 切换当前项时详情尚未重拉回来,
72
72
  // 旧详情 × 新 item 会被判成不同步,每切一次就误刷一次列表(实测踩坑)。
@@ -88,35 +88,40 @@ watch(isOutOfSync, (v) => v && refreshList());
88
88
 
89
89
  ### v-model
90
90
 
91
- | 名 | type | 语义 |
92
- | --- | --- | --- |
93
- | `activeId` | `string \| number \| undefined` | 当前项唯一键。`string` / `number` 两种都是一等公民,出口值保业务数据原始类型 |
94
- | `activeData` | `T \| undefined` | 当前项对应的**列表 item**(恒为列表接口原样) |
91
+ | 名 | type | 语义 |
92
+ | ------------ | ------------------------------- | ---------------------------------------------------------------------------- |
93
+ | `activeId` | `string \| number \| undefined` | 当前项唯一键。`string` / `number` 两种都是一等公民,出口值保业务数据原始类型 |
94
+ | `activeData` | `T \| undefined` | 当前项对应的**列表 item**(恒为列表接口原样) |
95
95
 
96
96
  ### Props
97
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 下是弹窗宽度 |
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
+ | `viewLayoutProps` | `Omit<ViewLayoutProps, "api" \| "params" \| "refreshToken">` | 无 | 透传给内置 `ViewLayout` 的 props——**详情侧的业务操作按钮走这里**:`{ leftExtraButtons: [...] }` 落到 ViewLayout 工具条左侧、与右侧刷新按钮组同一行(`rightExtraButtons` 则置于 `AutoRefreshGroup` 之前)。🔴 `api` / `params` / `refreshToken` 三键被 `Omit`:它们分别由 `detailApi` / `{ id: activeId }` / `activeId` 占位,是「详情跟着当前项走」这条不变式的载体,放开任一个都能让主干**静默失效**(页面看着正常,详情停在上一条)。**未传 `detailApi` 时无内置 ViewLayout,本项无落点** |
108
+ | `sheetLayerType` | `"drawer" \| "modal"` | `"drawer"` | sheet 形态的弹层承载:抽屉 / 居中弹窗。透传 `ModalConfirm.layerType` |
109
+ | `sheetSize` | `string \| number` | `"70%"` | sheet 弹层尺寸(透传 `ModalConfirm.width`):drawer 下是抽屉尺寸、modal 下是弹窗宽度 |
110
+
111
+ > **为什么是一个 `viewLayoutProps` 对象、而不是逐个扁平 prop**:同 `ListLayout` 的
112
+ > `tableMainProps` 惯例。内层能力面会长(按钮 / 刷新开关 / `refine` / 视口高…),
113
+ > 逐个补 prop 等于同一个洞反复开;一个对象一次封住。
109
114
 
110
115
  **AppPage 的 props 靠 `$attrs` 自动穿透**(`gap` / 各 `*Bg` / `fullViewport` / `nextViewportMaxHeight` 等原样可用),无需白名单。
111
116
 
112
117
  ### Slots
113
118
 
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` 自动挡切档后依旧在**) |
119
+ | 槽 | scope | 语义 |
120
+ | ----------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
121
+ | `#list` | `{ listProps }` | 列表。`listProps` [MUST] 整体 `v-bind` 到列表组件 |
122
+ | `#detail` | `{ id, record, index, total, hasPrev, hasNext, goPrev, goNext, refreshList, data, loading }` | 详情。`data` / `loading` 仅在传了 `detailApi` 时有值 |
123
+ | `#empty` | `{ listEmpty }` | 未选中占位。`listEmpty=true` 列表本身无数据 / `false` 有数据但未选中,**两种文案 [MUST] 分流** |
124
+ | `#top` / `#bottom` / `#right` | 无 | 直通 `AppPage` 同名悬浮槽(**两形态都支持,`auto` 自动挡切档后依旧在**) |
120
125
 
121
126
  > **为什么 `#right` 可用、`#left` 不可用**:门面对 `AppPage` 槽的占用是——Split 用 `#left` 放列表、**默认槽**放详情;Sheet 用**默认槽**放列表、**抽屉**放详情。两形态都**没占 `#right`**,故它原样开放给业务;`#left` 则是 Split 自用,不开放。
122
127
  >
@@ -20,6 +20,12 @@ declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: No
20
20
  embedded?: boolean;
21
21
  detailApi?: AppPageListDetailProps<R>["detailApi"];
22
22
  detailTitle?: AppPageListDetailProps<R>["detailTitle"];
23
+ /**
24
+ * 透传给内置 `ViewLayout` 的 props(详情侧业务按钮走 `leftExtraButtons`)。
25
+ * `api` / `params` / `refreshToken` 在类型上已被 `Omit`,模板里也**排在显式绑定之前**
26
+ * —— 双保险:TS 拦不住 JS 调用方,绑定顺序拦得住。
27
+ */
28
+ viewLayoutProps?: AppPageListDetailProps<R>["viewLayoutProps"];
23
29
  } & Partial<{}>> & import('vue').PublicProps;
24
30
  expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
25
31
  attrs: any;
@@ -12,6 +12,7 @@ declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: No
12
12
  listWidth?: string;
13
13
  detailApi?: AppPageListDetailProps<R>["detailApi"];
14
14
  detailTitle?: AppPageListDetailProps<R>["detailTitle"];
15
+ viewLayoutProps?: AppPageListDetailProps<R>["viewLayoutProps"];
15
16
  } & Partial<{}>> & import('vue').PublicProps;
16
17
  expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
17
18
  attrs: any;
@@ -15,6 +15,7 @@ declare const _default: <T extends Record<string, any>, R = any>(__VLS_props: No
15
15
  sheetLayerType?: "drawer" | "modal";
16
16
  detailApi?: AppPageListDetailProps<R>["detailApi"];
17
17
  detailTitle?: AppPageListDetailProps<R>["detailTitle"];
18
+ viewLayoutProps?: AppPageListDetailProps<R>["viewLayoutProps"];
18
19
  } & Partial<{}>> & import('vue').PublicProps;
19
20
  expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
20
21
  attrs: any;
@@ -1,3 +1,4 @@
1
+ import { ViewLayoutProps } from '../view-layout';
1
2
  import { ActiveRecordKey } from '../../hooks/use-active-record';
2
3
  import { NoInferType } from '../../types';
3
4
  /** 形态:`auto` 按视口宽定档(默认)|`split` 强制宽屏并排|`sheet` 强制窄屏抽屉 */
@@ -48,6 +49,20 @@ export interface AppPageListDetailEmptyScope {
48
49
  /** true = 列表本身无数据;false = 有数据但未选中。两种空态文案不同,由业务分流 */
49
50
  listEmpty: boolean;
50
51
  }
52
+ /**
53
+ * 透传给内置 `ViewLayout` 的 props(`detailApi` 传了才有内置 ViewLayout,不传则本项无落点)。
54
+ *
55
+ * **为什么是一个对象而不是逐个扁平 prop**:同 `ListLayout` 的 `tableMainProps` 惯例 ——
56
+ * 内层组件的能力面会长(按钮 / 刷新开关 / refine / 视口高…),逐个补 prop 等于同一个洞
57
+ * 反复开;一个对象一次封住。
58
+ *
59
+ * 🔴 **`api` / `params` / `refreshToken` 三个键被 `Omit` 掉,[MUST NOT] 加回来。**
60
+ * 它们是本范式的不变式载体:`api` = `detailApi`、`params` = `{ id: activeId }`、
61
+ * `refreshToken` = `activeId`(当前项一变即重拉详情)。放开任一个,调用方都能让
62
+ * 「详情跟着当前项走」这条主干失效,而失效时页面看起来仍是正常的 —— 详情只是停在上一条。
63
+ * 需要换详情数据源请改 `detailApi`,需要手动重拉请用 `#detail` scope 里的 `refreshList`。
64
+ */
65
+ export type AppPageListDetailViewLayoutProps<R = any> = Omit<ViewLayoutProps<any, R>, "api" | "params" | "refreshToken">;
51
66
  /** `AppPageListDetailLayout` props(不含两个 v-model,见 README) */
52
67
  export interface AppPageListDetailProps<R = any> {
53
68
  /** 形态;默认 `auto`(按视口宽 `splitAt` 定档) */
@@ -93,6 +108,12 @@ export interface AppPageListDetailProps<R = any> {
93
108
  * sheet 形态的弹层形态:`drawer` 抽屉(默认)|`modal` 居中弹窗。透传 `ModalConfirm.layerType`。
94
109
  * 详情区两种承载都支持;窄屏下抽屉更常见,故默认抽屉。
95
110
  */
111
+ /**
112
+ * 透传给内置 `ViewLayout` 的 props(**详情侧的业务操作按钮走这里**:
113
+ * `{ leftExtraButtons: [...] }` 会落到 ViewLayout 工具条左侧,与右侧刷新按钮组同一行)。
114
+ * 未传 `detailApi` 时无内置 ViewLayout,本项无落点。
115
+ */
116
+ viewLayoutProps?: AppPageListDetailViewLayoutProps<R>;
96
117
  sheetLayerType?: "drawer" | "modal";
97
118
  /**
98
119
  * sheet 形态的弹层尺寸(透传 `ModalConfirm.width`):`drawer` 下是抽屉尺寸、
@@ -1,6 +1,6 @@
1
1
  declare const _default: {
2
2
  "name": "@done-coding/admin-core",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "内部前端库"
5
5
  };
6
6