@done-coding/admin-core 0.30.1-alpha.0 → 0.31.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/es/bridge/config-hook.mjs +3 -0
  2. package/es/bridge/index.mjs +6 -0
  3. package/es/bridge/theme/index.mjs +11 -3
  4. package/es/components/app-layout/AppBody.vue.mjs +1 -1
  5. package/es/components/app-layout/AppBody.vue2.mjs +2 -2
  6. package/es/components/app-layout/AppPage.vue.mjs +1 -1
  7. package/es/components/app-layout/AppPage.vue2.mjs +175 -33
  8. package/es/components/app-layout/AppPageNavBar.vue.mjs +7 -0
  9. package/es/components/app-layout/AppPageNavBar.vue2.mjs +148 -0
  10. package/es/components/app-layout/app-page-geometry.mjs +3 -2
  11. package/es/components/app-layout/app-page-slot-surface.mjs +31 -0
  12. package/es/components/app-layout/use-app-page-title-field.mjs +41 -0
  13. package/es/components/data-view/DataGridView.vue.mjs +1 -1
  14. package/es/components/data-view/DataGridView.vue2.mjs +2 -1
  15. package/es/components/data-view/DataListView.vue.mjs +1 -1
  16. package/es/components/data-view/DataListView.vue2.mjs +14 -9
  17. package/es/components/data-view/DataListViewItem.vue.mjs +1 -1
  18. package/es/components/data-view/DataListViewItem.vue2.mjs +3 -1
  19. package/es/components/data-view/InfiniteListView.vue.mjs +1 -1
  20. package/es/components/data-view/InfiniteListView.vue2.mjs +25 -18
  21. package/es/components/data-view/use-grid-layout.mjs +13 -15
  22. package/es/components/display/TabsMain.vue.mjs +3 -1
  23. package/es/components/display/use-badge.mjs +10 -3
  24. package/es/components/form/FormItem.vue.mjs +1 -1
  25. package/es/components/form/FormMain.vue.mjs +1 -1
  26. package/es/components/form/FormMain.vue2.mjs +11 -4
  27. package/es/components/form/FormSearch.vue.mjs +1 -1
  28. package/es/components/form/FormSearch.vue2.mjs +12 -4
  29. package/es/components/form/FormSubmitPanel.vue.mjs +1 -1
  30. package/es/components/form/FormSubmitPanel.vue2.mjs +2 -0
  31. package/es/components/form/use-ai-fill.mjs +7 -1
  32. package/es/components/list-layout/ListLayout.vue.mjs +1 -1
  33. package/es/components/list-layout/ListLayout.vue2.mjs +19 -10
  34. package/es/components/modal/ModalConfirm.vue.mjs +1 -1
  35. package/es/components/modal/ModalConfirm.vue2.mjs +28 -5
  36. package/es/components/table/TableToolbar.vue.mjs +1 -1
  37. package/es/components/table/TableToolbar.vue2.mjs +15 -1
  38. package/es/components/view-layout/ViewLayout.vue.mjs +1 -1
  39. package/es/components/view-layout/ViewLayout.vue2.mjs +22 -29
  40. package/es/components/view-layout/ViewLayoutToolbar.vue.mjs +1 -1
  41. package/es/components/view-layout/ViewLayoutToolbar.vue2.mjs +16 -129
  42. package/es/config/slot-region.mjs +1 -0
  43. package/es/hooks/use-surface.mjs +5 -0
  44. package/es/hooks/use-theme-apply.mjs +18 -0
  45. package/es/index.mjs +4 -1
  46. package/es/store/app.mjs +26 -11
  47. package/es/style.css +230 -172
  48. package/package.json +2 -2
  49. package/src/bridge/docs/README.md +19 -3
  50. package/src/components/app-layout/docs/README-AppBody.md +25 -1
  51. package/src/components/app-layout/docs/README-AppHeader.md +1 -1
  52. package/src/components/app-layout/docs/README-AppPage.md +172 -3
  53. package/src/components/data-view/docs/README-DataListView.md +40 -1
  54. package/src/components/data-view/docs/README-InfiniteListView.md +68 -4
  55. package/src/components/display/docs/README-BadgeMark.md +18 -10
  56. package/src/components/form/docs/README-FormMain.md +26 -0
  57. package/src/components/list-layout/docs/README-ListLayout.md +12 -1
  58. package/src/components/menu/README.md +1 -1
  59. package/src/components/modal/docs/README-ModalConfirm.md +57 -0
  60. package/src/components/view-layout/docs/README-ViewLayout.md +46 -129
  61. package/types/bridge/config-hook.d.ts +5 -2
  62. package/types/bridge/index.d.ts +22 -0
  63. package/types/components/app-layout/AppPage.vue.d.ts +43 -1
  64. package/types/components/app-layout/AppPageNavBar.vue.d.ts +48 -0
  65. package/types/components/app-layout/app-page-geometry.d.ts +27 -2
  66. package/types/components/app-layout/app-page-slot-surface.d.ts +18 -0
  67. package/types/components/app-layout/index.d.ts +1 -0
  68. package/types/components/app-layout/types.d.ts +37 -1
  69. package/types/components/app-layout/use-app-page-title-field.d.ts +49 -0
  70. package/types/components/data-view/DataListViewItem.vue.d.ts +7 -0
  71. package/types/components/data-view/types.d.ts +26 -3
  72. package/types/components/data-view/use-grid-layout.d.ts +31 -6
  73. package/types/components/form/types.d.ts +8 -2
  74. package/types/components/list-layout/ListLayout.vue.d.ts +23 -2
  75. package/types/components/list-layout/types.d.ts +7 -0
  76. package/types/components/view-layout/ViewLayout.vue.d.ts +4 -16
  77. package/types/components/view-layout/ViewLayoutToolbar.vue.d.ts +3 -9
  78. package/types/components/view-layout/types.d.ts +11 -50
  79. package/types/config/slot-region.d.ts +17 -1
  80. package/types/injectInfo.json.d.ts +1 -1
@@ -9,6 +9,7 @@ function createBridgeConfigHook(init) {
9
9
  const reactiveBody = reactive(init.APP_LAYOUT_BODY_CONFIG);
10
10
  const reactiveViewport = reactive(init.APP_LAYOUT_VIEWPORT_CONFIG);
11
11
  const reactiveGap = reactive(init.APP_LAYOUT_GAP_CONFIG);
12
+ const reactiveForm = reactive(init.APP_FORM_CONFIG);
12
13
  const reactiveTheme = reactive(init.APP_THEME_CONFIG);
13
14
  const reactiveMap = {
14
15
  APP_BASE_INFO: reactiveBaseInfo,
@@ -20,6 +21,7 @@ function createBridgeConfigHook(init) {
20
21
  APP_LAYOUT_BODY_CONFIG: reactiveBody,
21
22
  APP_LAYOUT_VIEWPORT_CONFIG: reactiveViewport,
22
23
  APP_LAYOUT_GAP_CONFIG: reactiveGap,
24
+ APP_FORM_CONFIG: reactiveForm,
23
25
  APP_THEME_CONFIG: reactiveTheme
24
26
  };
25
27
  const layoutAggregate = reactive({
@@ -46,6 +48,7 @@ function createBridgeConfigHook(init) {
46
48
  roBody: readonly(reactiveBody),
47
49
  roViewport: readonly(reactiveViewport),
48
50
  roGap: readonly(reactiveGap),
51
+ roForm: readonly(reactiveForm),
49
52
  roTheme: readonly(reactiveTheme),
50
53
  roLayout: readonly(layoutAggregate)
51
54
  };
@@ -38,8 +38,13 @@ function createCoreBridge(options) {
38
38
  size: 8,
39
39
  ...options.APP_LAYOUT_GAP_CONFIG
40
40
  };
41
+ const formConfig = {
42
+ layoutByContainer: true,
43
+ ...options.APP_FORM_CONFIG
44
+ };
41
45
  const configHook = createBridgeConfigHook({
42
46
  APP_BASE_INFO: options.APP_BASE_INFO,
47
+ APP_FORM_CONFIG: formConfig,
43
48
  APP_THEME_CONFIG: options.APP_THEME_CONFIG,
44
49
  APP_LAYOUT_HEADER_CONFIG: options.APP_LAYOUT_HEADER_CONFIG,
45
50
  APP_LAYOUT_FOOTER_CONFIG: options.APP_LAYOUT_FOOTER_CONFIG,
@@ -218,6 +223,7 @@ function createCoreBridge(options) {
218
223
  * [MUST NOT] 解构/快照(⚠️ 定格)。
219
224
  */
220
225
  APP_LAYOUT_GAP_CONFIG: configHook.roGap,
226
+ APP_FORM_CONFIG: configHook.roForm,
221
227
  /**
222
228
  * 应用布局聚合配置(**响应式只读视图**,插件系统一期改)。
223
229
  *
@@ -63,7 +63,7 @@ function createBridgeThemeHook(init) {
63
63
  const applyConfig = (config) => {
64
64
  init.update("APP_THEME_CONFIG", { light: config.light, dark: config.dark });
65
65
  };
66
- const applyLayout = (layout) => {
66
+ const applyLayout = (layout, presetId = "") => {
67
67
  const baseline = init.layoutBaseline;
68
68
  if (baseline) {
69
69
  init.update("APP_LAYOUT_HEADER_CONFIG", { ...baseline.header });
@@ -83,7 +83,15 @@ function createBridgeThemeHook(init) {
83
83
  if (layout.aside) init.update("APP_LAYOUT_ASIDE_CONFIG", layout.aside);
84
84
  if (layout.breadcrumb)
85
85
  init.update("APP_LAYOUT_BREADCRUMB_CONFIG", layout.breadcrumb);
86
- if (layout.body) init.update("APP_LAYOUT_BODY_CONFIG", layout.body);
86
+ if (layout.body) {
87
+ const { shimPadding, ...bodyRest } = layout.body;
88
+ if (shimPadding) {
89
+ console.warn(
90
+ `[dc-layout] 主题预设「${presetId}」的 layout.body.shimPadding(${shimPadding})已废弃且**不再影响任何渲染**,已忽略。内容四周留白改由间距阶梯的外圈档(gap × 2)给出 —— 预设要调留白请改自己的 layout.gap.size。本字段将在下一个版本移除。`
91
+ );
92
+ }
93
+ init.update("APP_LAYOUT_BODY_CONFIG", bodyRest);
94
+ }
87
95
  if (layout.viewport)
88
96
  init.update("APP_LAYOUT_VIEWPORT_CONFIG", layout.viewport);
89
97
  if (layout.gap) init.update("APP_LAYOUT_GAP_CONFIG", layout.gap);
@@ -111,7 +119,7 @@ function createBridgeThemeHook(init) {
111
119
  if (prev === target) return;
112
120
  fireHook((_a2 = hookMap.get(prev)) == null ? void 0 : _a2.onDeactivate, prev);
113
121
  applyConfig(targetConfig);
114
- applyLayout(layoutMap.get(target));
122
+ applyLayout(layoutMap.get(target), target);
115
123
  const rhythm = rhythmMap.get(target);
116
124
  themeDensity.value = (rhythm == null ? void 0 : rhythm.density) ?? DEFAULT_DENSITY;
117
125
  themeMotion.value = (rhythm == null ? void 0 : rhythm.motion) ?? DEFAULT_MOTION;
@@ -1,7 +1,7 @@
1
1
  import _sfc_main from "./AppBody.vue2.mjs";
2
2
  /* empty css */
3
3
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
4
- const AppBody = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-ca48e9a0"]]);
4
+ const AppBody = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-84bfe27d"]]);
5
5
  export {
6
6
  AppBody as default
7
7
  };
@@ -12,7 +12,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
12
12
  },
13
13
  setup(__props) {
14
14
  useCssVars((_ctx) => ({
15
- "ece8e1ac": contentViewHeight.value
15
+ "v645a9d15": contentViewHeight.value
16
16
  }));
17
17
  const appStore = inject(APP_LAYOUT_APP_STORE_KEY);
18
18
  const bridge = inject(APP_LAYOUT_BRIDGE_KEY);
@@ -32,7 +32,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
32
32
  onMounted(() => {
33
33
  if (bodyShimPadding.value) {
34
34
  console.warn(
35
- `[dc-layout] APP_LAYOUT_BODY_CONFIG.shimPadding(当前 ${bodyShimPadding.value})已废弃且**不再影响任何渲染**。内容四周留白改由间距阶梯的外圈档(gap × 2)给出 —— 要调留白请改 APP_LAYOUT_GAP_CONFIG.size,一个旋钮按比例缩放「外圈 / 呼吸 / 模块 / 分段」四档。本字段将在下一个版本移除。`
35
+ `[dc-layout] APP_LAYOUT_BODY_CONFIG.shimPadding(当前 ${bodyShimPadding.value})已废弃且**不再影响任何渲染**。来源只可能是这两处之一:① createCoreBridge 的 APP_LAYOUT_BODY_CONFIG.shimPadding;② 当前路由的 meta.layout.bodyShimPadding(优先级更高)。内置主题预设自 v0.32.0 起已全部清空该字段、且第三方预设写了也会被忽略并单独告警 —— 不必去翻预设。内容四周留白改由间距阶梯的外圈档(gap × 2)给出 —— 要调留白请改 APP_LAYOUT_GAP_CONFIG.size,一个旋钮按比例缩放「外圈 / 呼吸 / 模块 / 分段」四档。本字段将在下一个版本移除。`
36
36
  );
37
37
  }
38
38
  });
@@ -1,7 +1,7 @@
1
1
  import _sfc_main from "./AppPage.vue2.mjs";
2
2
  /* empty css */
3
3
  import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
4
- const AppPage = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-73e3d4a9"]]);
4
+ const AppPage = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-f05fad7f"]]);
5
5
  export {
6
6
  AppPage as default
7
7
  };
@@ -1,10 +1,14 @@
1
- import { defineComponent, useCssVars, computed, useSlots, ref, shallowRef, inject, provide, readonly, openBlock, createElementBlock, normalizeStyle, createElementVNode, unref, createVNode, withCtx, renderSlot, createBlock, createCommentVNode } from "vue";
1
+ import { defineComponent, useCssVars, useModel, computed, useSlots, ref, shallowRef, inject, watch, provide, readonly, openBlock, createElementBlock, normalizeStyle, createElementVNode, unref, createVNode, withCtx, renderSlot, createBlock, createSlots, normalizeProps, guardReactiveProps, createCommentVNode, mergeModels } from "vue";
2
2
  import _debounce from "lodash/debounce";
3
3
  import _sfc_main$1 from "../modal/ModalShelf.vue.mjs";
4
4
  import _sfc_main$2 from "../display/WatchSize.vue.mjs";
5
- import { computeAppPageReducedViewportHeight, computeAppPageReducedViewportWidth, computeAppPageContentInset, computeAppPageSlotStyles, computeAppPageSlotShimStyles } from "./app-page-geometry.mjs";
5
+ import AppPageNavBar from "./AppPageNavBar.vue.mjs";
6
+ import { APP_PAGE_TITLE_FIELD_CLAIM } from "./use-app-page-title-field.mjs";
7
+ import { appPageSlotInset, APP_PAGE_SLOT_Z_INDEX, computeAppPageReducedViewportHeight, computeAppPageReducedViewportWidth, computeAppPageContentInset, computeAppPageSlotStyles, computeAppPageSlotShimStyles } from "./app-page-geometry.mjs";
6
8
  import { useAppPageContentBox } from "./use-content-box.mjs";
7
9
  import { useAppPageSlotMisuseWarn } from "./use-slot-misuse-warn.mjs";
10
+ import { countSlotSurfaceHosts } from "./app-page-slot-surface.mjs";
11
+ import { useIsDev } from "../../hooks/use-is-dev.mjs";
8
12
  import { APP_PAGE_LIST_LAYOUT_RECOMMENDED } from "./app-page-recommended.mjs";
9
13
  import { BODY_CONTENT_VIEWPORT_RECT, APP_LAYOUT_BRIDGE_KEY, SCOPE_VIEWPORT_MAX_HEIGHT, BODY_CONTENT_VIEWPORT_HEIGHT, BODY_CONTENT_VIEWPORT_INSET, APP_PAGE_CONTENT_INSET } from "../../inject/key.mjs";
10
14
  import { useActivated } from "../../hooks/activated.mjs";
@@ -15,13 +19,14 @@ const _hoisted_2 = ["data-dc-slot"];
15
19
  const _hoisted_3 = ["data-dc-slot"];
16
20
  const _hoisted_4 = ["data-dc-slot"];
17
21
  const _hoisted_5 = ["data-dc-slot"];
22
+ const DEFAULT_SLOT_BG = "var(--el-bg-color)";
18
23
  const cssVar = "--app-viewport-max-height";
19
24
  const _sfc_main = /* @__PURE__ */ defineComponent({
20
25
  ...{
21
26
  name: "AppPage"
22
27
  },
23
28
  __name: "AppPage",
24
- props: {
29
+ props: /* @__PURE__ */ mergeModels({
25
30
  fullViewport: { type: Boolean, default: false },
26
31
  fullMode: { default: "min-height" },
27
32
  nextViewportMaxHeight: { default: `var(--app-viewport-max-height)` },
@@ -38,19 +43,32 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
38
43
  leftWidth: {},
39
44
  rightWidth: {},
40
45
  contentCentered: { type: Boolean, default: false },
41
- contentMaxWidth: { default: 1200 }
42
- },
46
+ contentMaxWidth: { default: 1200 },
47
+ showNavBar: { type: Boolean, default: false },
48
+ back: {},
49
+ moreOptions: {},
50
+ onMoreSelect: {},
51
+ onMore: {},
52
+ navBarObserveResize: { type: Boolean, default: false },
53
+ titleField: {}
54
+ }, {
55
+ "title": {},
56
+ "titleModifiers": {}
57
+ }),
58
+ emits: ["update:title"],
43
59
  setup(__props) {
44
60
  useCssVars((_ctx) => ({
45
- "v62d8191f": pageCssVars.value.top,
46
- "v7adfb621": pageCssVars.value.bottom,
47
- "v0fb17d06": pageCssVars.value.left,
48
- "v0d174f66": pageCssVars.value.right,
49
- "v0d5dbb10": pageCssVars.value.width,
50
- "f6eaa5c6": pageCssVars.value.height,
51
- "v4ad30b28": props.nextViewportMaxHeight
61
+ "v14128d84": pageCssVars.value.top,
62
+ "v8762a8c8": pageCssVars.value.bottom,
63
+ "v6e3b59b8": pageCssVars.value.left,
64
+ "v59863e8b": pageCssVars.value.right,
65
+ "v59ccaa35": pageCssVars.value.width,
66
+ "v740cbad0": pageCssVars.value.height,
67
+ "v0517015e": props.nextViewportMaxHeight
52
68
  }));
53
69
  const props = __props;
70
+ const title = useModel(__props, "title");
71
+ const isDev = useIsDev();
54
72
  const style = computed(() => {
55
73
  return {
56
74
  background: props.background,
@@ -127,6 +145,46 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
127
145
  const outerGap = computed(
128
146
  () => gapScaleValue(gapBase.value, "outer", layoutBridge == null ? void 0 : layoutBridge.themeDensity.value)
129
147
  );
148
+ const navBarH = ref(0);
149
+ const onNavBarH = (v) => {
150
+ navBarH.value = v;
151
+ };
152
+ const navBarInset = computed(
153
+ () => (
154
+ // 🔴 [MUST] 先过 `showNavBar`:`WatchSize` 卸载时**不会**补发一个 0,
155
+ // 只看 `navBarH` 会让关掉 navbar 后内容仍让着最后量到的那一截(真机实测 99px 死留白)。
156
+ // 四向槽没这个问题只是因为它们由**非响应式的插槽集**决定、运行期不切换。
157
+ props.showNavBar ? appPageSlotInset(navBarH.value, outerGap.value) : 0
158
+ )
159
+ );
160
+ const keylinePx = computed(() => `${outerGap.value}px`);
161
+ const navBarBandStyle = computed(() => ({
162
+ position: "fixed",
163
+ top: rectInset.value.top,
164
+ left: rectInset.value.left,
165
+ right: rectInset.value.right,
166
+ zIndex: APP_PAGE_SLOT_Z_INDEX
167
+ }));
168
+ const navBarTitleBox = shallowRef();
169
+ const onTitleBox = (el) => {
170
+ var _a, _b;
171
+ navBarTitleBox.value = el;
172
+ (_b = (_a = props.titleField) == null ? void 0 : _a[APP_PAGE_TITLE_FIELD_CLAIM]) == null ? void 0 : _b.claimBox(el);
173
+ };
174
+ watch(
175
+ () => props.titleField,
176
+ (field, prev) => {
177
+ var _a;
178
+ (_a = prev == null ? void 0 : prev[APP_PAGE_TITLE_FIELD_CLAIM]) == null ? void 0 : _a.claimWrite(() => void 0);
179
+ const claim = field == null ? void 0 : field[APP_PAGE_TITLE_FIELD_CLAIM];
180
+ if (!claim) return;
181
+ claim.claimWrite((t) => {
182
+ title.value = t;
183
+ });
184
+ claim.claimBox(navBarTitleBox.value);
185
+ },
186
+ { immediate: true }
187
+ );
130
188
  const slotSizes = computed(() => ({
131
189
  topH: topH.value,
132
190
  bottomH: bottomH.value,
@@ -136,7 +194,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
136
194
  const parentScope = inject(SCOPE_VIEWPORT_MAX_HEIGHT, void 0);
137
195
  const reducedViewportHeight = computed(
138
196
  () => computeAppPageReducedViewportHeight(
139
- (parentScope == null ? void 0 : parentScope.viewportMaxHeight.value) ?? 0,
197
+ // 🔴 navbar 档**预扣在输入上**(vq3mzn D3):四处几何出口全从这三个输入派生
198
+ // ⇒ 预加即四处自动一致,[MUST NOT] 在四处各写一遍加法(huhyx7 漏三处的形状)。
199
+ Math.max(
200
+ 0,
201
+ ((parentScope == null ? void 0 : parentScope.viewportMaxHeight.value) ?? 0) - navBarInset.value
202
+ ),
140
203
  slotSizes.value,
141
204
  // 🔴 [MUST] outerGap,[MUST NOT] shimPadding —— 后者 huhyx7 起恒 0
142
205
  // (AppBody 写死 `shimPadding: 0`),传它等于「让位不含 gap」,可用高多算
@@ -192,7 +255,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
192
255
  const bodyInset = inject(BODY_CONTENT_VIEWPORT_INSET, NO_APP_CHROME_INSET);
193
256
  const contentInset = computed(() => {
194
257
  const base = computeAppPageContentInset(
195
- bodyInset.value,
258
+ // navbar 档预加在 app 级 chrome 的上沿上(同上,输入侧一处加)
259
+ navBarInset.value ? { ...bodyInset.value, top: bodyInset.value.top + navBarInset.value } : bodyInset.value,
196
260
  slotSizes.value,
197
261
  props.gap
198
262
  );
@@ -214,28 +278,63 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
214
278
  height: `${reducedViewportHeight.value}px`
215
279
  };
216
280
  });
281
+ const rectInsetBelowNavBar = computed(
282
+ () => navBarInset.value ? {
283
+ ...rectInset.value,
284
+ top: `calc(${rectInset.value.top} + ${navBarInset.value}px)`
285
+ } : rectInset.value
286
+ );
217
287
  const slotStyles = computed(
218
- () => computeAppPageSlotStyles(rectInset.value, slotSizes.value, {
288
+ () => computeAppPageSlotStyles(rectInsetBelowNavBar.value, slotSizes.value, {
219
289
  outerGap: outerGap.value,
220
290
  sideWidth: { left: props.leftWidth, right: props.rightWidth }
221
291
  })
222
292
  );
223
- const shimStyles = computed(() => {
224
- const base = computeAppPageSlotShimStyles();
225
- return {
226
- top: { ...base.top, background: props.topBg },
227
- bottom: { ...base.bottom, background: props.bottomBg },
228
- left: { ...base.left, background: props.leftBg },
229
- right: { ...base.right, background: props.rightBg }
230
- };
231
- });
293
+ const moduleGap = computed(
294
+ () => gapScaleValue(gapBase.value, "module", layoutBridge == null ? void 0 : layoutBridge.themeDensity.value)
295
+ );
296
+ const slotSurfaceProjected = (name) => {
297
+ var _a;
298
+ const hits = countSlotSurfaceHosts((_a = slots[name]) == null ? void 0 : _a.call(slots, { listLayoutRecommended }));
299
+ if (hits > 1) {
300
+ if (isDev()) {
301
+ console.warn(
302
+ `[dc-page] AppPage 的 #${name} 槽里有 ${hits} 个 ListLayout ——「面归谁」无法判定,本槽按**不下放**处理(槽照旧自带面)。出口:一个槽只放一个列表;确要并排多个,请自行在槽内组织面。`
303
+ );
304
+ }
305
+ return false;
306
+ }
307
+ return hits === 1;
308
+ };
309
+ const slotShimStyle = (name) => {
310
+ const base = computeAppPageSlotShimStyles(moduleGap.value)[name];
311
+ const bg = {
312
+ top: props.topBg,
313
+ bottom: props.bottomBg,
314
+ left: props.leftBg,
315
+ right: props.rightBg
316
+ }[name];
317
+ const explicitBg = {
318
+ top: "topBg",
319
+ bottom: "bottomBg",
320
+ left: "leftBg",
321
+ right: "rightBg"
322
+ }[name];
323
+ const isExplicit = props[explicitBg] !== DEFAULT_SLOT_BG;
324
+ if (!isExplicit && slotSurfaceProjected(name)) {
325
+ return { padding: "0px", background: void 0, borderRadius: "0px" };
326
+ }
327
+ return { ...base, background: bg };
328
+ };
232
329
  const shimStyle = computed(() => {
233
330
  const o = outerGap.value;
234
331
  const g = contentGutter.value;
235
332
  const inset = (v) => `${v ? v + o : 0}px`;
236
333
  const insetX = (v) => `${(v ? v + o : 0) + g}px`;
237
334
  return {
238
- paddingTop: inset(topH.value),
335
+ // navbar 档叠在 top 槽让位之上(navbar 未开启时恒 0);
336
+ // 让位式仍走共享的 appPageSlotInset,[MUST NOT] 在此把公式再抄一遍
337
+ paddingTop: `${appPageSlotInset(topH.value, o) + navBarInset.value}px`,
239
338
  paddingBottom: inset(bottomH.value),
240
339
  paddingLeft: insetX(leftW.value),
241
340
  paddingRight: insetX(rightW.value)
@@ -260,9 +359,52 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
260
359
  _: 3
261
360
  })
262
361
  ], 12, _hoisted_1),
263
- hasTop.value ? (openBlock(), createBlock(unref(_sfc_main$2), {
362
+ __props.showNavBar ? (openBlock(), createBlock(unref(_sfc_main$2), {
264
363
  key: 0,
265
364
  mode: "height",
365
+ "observe-resize": __props.navBarObserveResize,
366
+ class: "app-page-nav-bar-band",
367
+ style: normalizeStyle(navBarBandStyle.value),
368
+ onHeightChange: onNavBarH
369
+ }, {
370
+ default: withCtx(() => [
371
+ createVNode(AppPageNavBar, {
372
+ title: title.value,
373
+ back: __props.back,
374
+ moreOptions: __props.moreOptions,
375
+ onMoreSelect: __props.onMoreSelect,
376
+ onMore: __props.onMore,
377
+ keylinePx: keylinePx.value,
378
+ onTitleBox
379
+ }, createSlots({ _: 2 }, [
380
+ _ctx.$slots.navTitle ? {
381
+ name: "navTitle",
382
+ fn: withCtx((scope) => [
383
+ renderSlot(_ctx.$slots, "navTitle", normalizeProps(guardReactiveProps(scope)), void 0, true)
384
+ ]),
385
+ key: "0"
386
+ } : void 0,
387
+ _ctx.$slots.more ? {
388
+ name: "more",
389
+ fn: withCtx(() => [
390
+ renderSlot(_ctx.$slots, "more", {}, void 0, true)
391
+ ]),
392
+ key: "1"
393
+ } : void 0,
394
+ _ctx.$slots.tabbar ? {
395
+ name: "tabbar",
396
+ fn: withCtx(() => [
397
+ renderSlot(_ctx.$slots, "tabbar", {}, void 0, true)
398
+ ]),
399
+ key: "2"
400
+ } : void 0
401
+ ]), 1032, ["title", "back", "moreOptions", "onMoreSelect", "onMore", "keylinePx"])
402
+ ]),
403
+ _: 3
404
+ }, 8, ["observe-resize", "style"])) : createCommentVNode("", true),
405
+ hasTop.value ? (openBlock(), createBlock(unref(_sfc_main$2), {
406
+ key: 1,
407
+ mode: "height",
266
408
  "observe-resize": __props.topObserveResize,
267
409
  class: "app-page-slot app-page-slot--top",
268
410
  style: normalizeStyle(slotStyles.value.top),
@@ -271,7 +413,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
271
413
  default: withCtx(() => [
272
414
  createElementVNode("div", {
273
415
  class: "app-page-slot-shim",
274
- style: normalizeStyle(shimStyles.value.top),
416
+ style: normalizeStyle(slotShimStyle("top")),
275
417
  "data-dc-slot": unref(SlotRegion).AppPage_Top
276
418
  }, [
277
419
  renderSlot(_ctx.$slots, "top", {}, void 0, true)
@@ -280,7 +422,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
280
422
  _: 3
281
423
  }, 8, ["observe-resize", "style"])) : createCommentVNode("", true),
282
424
  hasBottom.value ? (openBlock(), createBlock(unref(_sfc_main$2), {
283
- key: 1,
425
+ key: 2,
284
426
  mode: "height",
285
427
  "observe-resize": __props.bottomObserveResize,
286
428
  class: "app-page-slot app-page-slot--bottom",
@@ -290,7 +432,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
290
432
  default: withCtx(() => [
291
433
  createElementVNode("div", {
292
434
  class: "app-page-slot-shim",
293
- style: normalizeStyle(shimStyles.value.bottom),
435
+ style: normalizeStyle(slotShimStyle("bottom")),
294
436
  "data-dc-slot": unref(SlotRegion).AppPage_Bottom
295
437
  }, [
296
438
  renderSlot(_ctx.$slots, "bottom", {}, void 0, true)
@@ -299,7 +441,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
299
441
  _: 3
300
442
  }, 8, ["observe-resize", "style"])) : createCommentVNode("", true),
301
443
  hasLeft.value ? (openBlock(), createBlock(unref(_sfc_main$2), {
302
- key: 2,
444
+ key: 3,
303
445
  mode: "width",
304
446
  "observe-resize": __props.leftObserveResize,
305
447
  class: "app-page-slot app-page-slot--left",
@@ -309,7 +451,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
309
451
  default: withCtx(() => [
310
452
  createElementVNode("div", {
311
453
  class: "app-page-slot-shim",
312
- style: normalizeStyle(shimStyles.value.left),
454
+ style: normalizeStyle(slotShimStyle("left")),
313
455
  "data-dc-slot": unref(SlotRegion).AppPage_Left
314
456
  }, [
315
457
  renderSlot(_ctx.$slots, "left", { listLayoutRecommended: unref(listLayoutRecommended) }, void 0, true)
@@ -318,7 +460,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
318
460
  _: 3
319
461
  }, 8, ["observe-resize", "style"])) : createCommentVNode("", true),
320
462
  hasRight.value ? (openBlock(), createBlock(unref(_sfc_main$2), {
321
- key: 3,
463
+ key: 4,
322
464
  mode: "width",
323
465
  "observe-resize": __props.rightObserveResize,
324
466
  class: "app-page-slot app-page-slot--right",
@@ -328,7 +470,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
328
470
  default: withCtx(() => [
329
471
  createElementVNode("div", {
330
472
  class: "app-page-slot-shim",
331
- style: normalizeStyle(shimStyles.value.right),
473
+ style: normalizeStyle(slotShimStyle("right")),
332
474
  "data-dc-slot": unref(SlotRegion).AppPage_Right
333
475
  }, [
334
476
  renderSlot(_ctx.$slots, "right", { listLayoutRecommended: unref(listLayoutRecommended) }, void 0, true)
@@ -0,0 +1,7 @@
1
+ import _sfc_main from "./AppPageNavBar.vue2.mjs";
2
+ /* empty css */
3
+ import _export_sfc from "../../_virtual/_plugin-vue_export-helper.mjs";
4
+ const AppPageNavBar = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1f8c3893"]]);
5
+ export {
6
+ AppPageNavBar as default
7
+ };
@@ -0,0 +1,148 @@
1
+ import { defineComponent, useSlots, shallowRef, watch, computed, openBlock, createElementBlock, createElementVNode, normalizeStyle, withKeys, withModifiers, createVNode, unref, withCtx, createCommentVNode, renderSlot, createTextVNode, toDisplayString, createBlock, Fragment, renderList } from "vue";
2
+ import { ElIcon, ElDropdown, ElDropdownMenu, ElDropdownItem } from "element-plus";
3
+ import { ArrowLeft, MoreFilled } from "@element-plus/icons-vue";
4
+ const _hoisted_1 = { class: "dc-app-page__nav-bar app-page-nav-bar" };
5
+ const _hoisted_2 = { class: "app-page-nav-bar__more" };
6
+ const _sfc_main = /* @__PURE__ */ defineComponent({
7
+ __name: "AppPageNavBar",
8
+ props: {
9
+ title: {},
10
+ back: { type: Function },
11
+ moreOptions: {},
12
+ onMoreSelect: { type: Function },
13
+ onMore: { type: Function },
14
+ keylinePx: {}
15
+ },
16
+ emits: ["titleBox"],
17
+ setup(__props, { emit: __emit }) {
18
+ const props = __props;
19
+ const emit = __emit;
20
+ const slots = useSlots();
21
+ const titleBoxEl = shallowRef();
22
+ watch(titleBoxEl, (el) => emit("titleBox", el), { immediate: true });
23
+ const hasMoreMenu = computed(() => {
24
+ var _a;
25
+ return !!((_a = props.moreOptions) == null ? void 0 : _a.length);
26
+ });
27
+ const showMore = () => hasMoreMenu.value || !!props.onMore || !!slots.more;
28
+ const hasTabbar = () => !!slots.tabbar;
29
+ const mainStyle = computed(() => ({
30
+ padding: `0 ${props.keylinePx}`
31
+ }));
32
+ const tabbarStyle = computed(() => ({
33
+ padding: `0 ${props.keylinePx}`
34
+ }));
35
+ return (_ctx, _cache) => {
36
+ return openBlock(), createElementBlock("div", _hoisted_1, [
37
+ createElementVNode("div", {
38
+ class: "app-page-nav-bar__main",
39
+ style: normalizeStyle(mainStyle.value)
40
+ }, [
41
+ __props.back ? (openBlock(), createElementBlock("span", {
42
+ key: 0,
43
+ class: "app-page-nav-bar__back",
44
+ role: "button",
45
+ tabindex: "0",
46
+ "aria-label": "返回",
47
+ onClick: _cache[0] || (_cache[0] = ($event) => __props.back()),
48
+ onKeydown: [
49
+ _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => __props.back(), ["prevent"]), ["enter"])),
50
+ _cache[2] || (_cache[2] = withKeys(withModifiers(($event) => __props.back(), ["prevent"]), ["space"]))
51
+ ]
52
+ }, [
53
+ createVNode(unref(ElIcon), null, {
54
+ default: withCtx(() => [
55
+ createVNode(unref(ArrowLeft))
56
+ ]),
57
+ _: 1
58
+ })
59
+ ], 32)) : createCommentVNode("", true),
60
+ createElementVNode("div", {
61
+ ref_key: "titleBoxEl",
62
+ ref: titleBoxEl,
63
+ class: "app-page-nav-bar__title"
64
+ }, [
65
+ renderSlot(_ctx.$slots, "navTitle", { title: __props.title }, () => [
66
+ createTextVNode(toDisplayString(__props.title), 1)
67
+ ], true)
68
+ ], 512),
69
+ showMore() && hasMoreMenu.value ? (openBlock(), createBlock(unref(ElDropdown), {
70
+ key: 1,
71
+ trigger: "click",
72
+ onCommand: __props.onMoreSelect
73
+ }, {
74
+ dropdown: withCtx(() => [
75
+ createVNode(unref(ElDropdownMenu), null, {
76
+ default: withCtx(() => [
77
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.moreOptions, (item) => {
78
+ return openBlock(), createBlock(unref(ElDropdownItem), {
79
+ key: item.value,
80
+ command: item
81
+ }, {
82
+ default: withCtx(() => [
83
+ createTextVNode(toDisplayString(item.label), 1)
84
+ ]),
85
+ _: 2
86
+ }, 1032, ["command"]);
87
+ }), 128))
88
+ ]),
89
+ _: 1
90
+ })
91
+ ]),
92
+ default: withCtx(() => [
93
+ createElementVNode("span", _hoisted_2, [
94
+ renderSlot(_ctx.$slots, "more", {}, () => [
95
+ createVNode(unref(ElIcon), null, {
96
+ default: withCtx(() => [
97
+ createVNode(unref(MoreFilled))
98
+ ]),
99
+ _: 1
100
+ })
101
+ ], true)
102
+ ])
103
+ ]),
104
+ _: 3
105
+ }, 8, ["onCommand"])) : showMore() ? (openBlock(), createElementBlock("span", {
106
+ key: 2,
107
+ class: "app-page-nav-bar__more",
108
+ role: "button",
109
+ tabindex: "0",
110
+ onClick: _cache[3] || (_cache[3] = ($event) => {
111
+ var _a;
112
+ return (_a = __props.onMore) == null ? void 0 : _a.call(__props);
113
+ }),
114
+ onKeydown: [
115
+ _cache[4] || (_cache[4] = withKeys(withModifiers(($event) => {
116
+ var _a;
117
+ return (_a = __props.onMore) == null ? void 0 : _a.call(__props);
118
+ }, ["prevent"]), ["enter"])),
119
+ _cache[5] || (_cache[5] = withKeys(withModifiers(($event) => {
120
+ var _a;
121
+ return (_a = __props.onMore) == null ? void 0 : _a.call(__props);
122
+ }, ["prevent"]), ["space"]))
123
+ ]
124
+ }, [
125
+ renderSlot(_ctx.$slots, "more", {}, () => [
126
+ createVNode(unref(ElIcon), null, {
127
+ default: withCtx(() => [
128
+ createVNode(unref(MoreFilled))
129
+ ]),
130
+ _: 1
131
+ })
132
+ ], true)
133
+ ], 32)) : createCommentVNode("", true)
134
+ ], 4),
135
+ hasTabbar() ? (openBlock(), createElementBlock("div", {
136
+ key: 0,
137
+ class: "app-page-nav-bar__tabbar",
138
+ style: normalizeStyle(tabbarStyle.value)
139
+ }, [
140
+ renderSlot(_ctx.$slots, "tabbar", {}, void 0, true)
141
+ ], 4)) : createCommentVNode("", true)
142
+ ]);
143
+ };
144
+ }
145
+ });
146
+ export {
147
+ _sfc_main as default
148
+ };
@@ -66,12 +66,13 @@ function computeAppPageSlotStyles(rect, slotSizes, options) {
66
66
  }
67
67
  };
68
68
  }
69
- function computeAppPageSlotShimStyles() {
70
- const pad = { padding: "8px" };
69
+ function computeAppPageSlotShimStyles(moduleGap) {
70
+ const pad = { padding: `${moduleGap}px` };
71
71
  return { top: pad, bottom: pad, left: pad, right: pad };
72
72
  }
73
73
  export {
74
74
  APP_PAGE_SLOT_Z_INDEX,
75
+ appPageSlotInset,
75
76
  computeAppPageContentInset,
76
77
  computeAppPageReducedViewportHeight,
77
78
  computeAppPageReducedViewportWidth,
@@ -0,0 +1,31 @@
1
+ import { Text, Comment, Fragment } from "vue";
2
+ const SURFACE_HOST_NAME = "ListLayout";
3
+ const componentNameOf = (node) => {
4
+ const t = node.type;
5
+ if (!t || typeof t === "string") return void 0;
6
+ return t.name ?? t.__name;
7
+ };
8
+ const countSlotSurfaceHosts = (nodes) => {
9
+ if (!(nodes == null ? void 0 : nodes.length)) return 0;
10
+ let hit = 0;
11
+ const walk = (list) => {
12
+ list.forEach((raw) => {
13
+ const node = raw;
14
+ if (!node || typeof node !== "object") return;
15
+ if (node.type === Text || node.type === Comment) return;
16
+ if (componentNameOf(node) === SURFACE_HOST_NAME) {
17
+ hit += 1;
18
+ return;
19
+ }
20
+ if (Array.isArray(node.children)) walk(node.children);
21
+ else if (node.type === Fragment && node.children) {
22
+ walk([node.children]);
23
+ }
24
+ });
25
+ };
26
+ walk(nodes);
27
+ return hit;
28
+ };
29
+ export {
30
+ countSlotSurfaceHosts
31
+ };