smart-admin-web 10.22.0 → 10.23.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.
@@ -1,17 +1,17 @@
1
- import { defineComponent as F, useModel as I, computed as r, ref as N, onDeactivated as q, openBlock as p, createBlock as g, unref as e, normalizeStyle as G, normalizeClass as M, createSlots as D, withCtx as l, createElementVNode as V, renderSlot as v, createVNode as i, createTextVNode as h, toDisplayString as d, createElementBlock as H, mergeModels as J } from "vue";
2
- import { NModal as K, NSpace as j, NButton as y, NDrawer as L, NDrawerContent as O } from "naive-ui";
3
- import { useI18n as P } from "vue-i18n";
4
- import { useWindowSize as Q } from "@vueuse/core";
5
- import { useCompactScreen as R } from "../../composables/useCompactScreen.js";
6
- import { useShellBreakpoint as X } from "../../composables/useShellBreakpoint.js";
7
- import { useAppStore as Y } from "../../stores/app.js";
8
- import { shake as Z } from "../../utils/shake.js";
9
- const _ = {
1
+ import { defineComponent as I, useModel as q, computed as r, ref as M, onDeactivated as G, openBlock as p, createBlock as g, unref as e, normalizeStyle as H, normalizeClass as D, createSlots as V, withCtx as l, createElementVNode as j, renderSlot as h, createVNode as i, createTextVNode as y, toDisplayString as d, createElementBlock as J, mergeModels as K } from "vue";
2
+ import { NModal as L, NSpace as E, NButton as w, NDrawer as O, NDrawerContent as P } from "naive-ui";
3
+ import { useI18n as Q } from "vue-i18n";
4
+ import { useWindowSize as R } from "@vueuse/core";
5
+ import { useCompactScreen as X } from "../../composables/useCompactScreen.js";
6
+ import { useShellBreakpoint as Y } from "../../composables/useShellBreakpoint.js";
7
+ import { useAppStore as Z } from "../../stores/app.js";
8
+ import { shake as _ } from "../../utils/shake.js";
9
+ const ee = {
10
10
  key: 0,
11
11
  class: "smart-form-foot"
12
- }, ie = /* @__PURE__ */ F({
12
+ }, ue = /* @__PURE__ */ I({
13
13
  __name: "index",
14
- props: /* @__PURE__ */ J({
14
+ props: /* @__PURE__ */ K({
15
15
  title: {},
16
16
  variant: {},
17
17
  width: {},
@@ -26,14 +26,14 @@ const _ = {
26
26
  }),
27
27
  emits: ["update:show"],
28
28
  setup(a) {
29
- const t = I(a, "show"), u = a, { t: c } = P(), E = Y(), b = r(() => u.variant ?? E.formStyle), n = N(!1), { width: S } = Q(), U = R(), k = r(
29
+ const t = q(a, "show"), u = a, { t: c } = Q(), F = Z(), b = r(() => u.variant ?? F.formStyle), n = M(!1), { width: x } = R(), U = X(), m = r(
30
30
  () => u.fullscreen === "auto" ? U.value : u.fullscreen === !0
31
- ), { bp: W } = X(), m = r(() => b.value === "drawer" && W.value === "narrow" && !k.value), $ = r(() => m.value ? "large" : "medium"), x = r(
32
- () => k.value ? S.value : Math.min(u.width ?? 560, Math.round(S.value * 0.9))
33
- ), A = r(() => k.value ? void 0 : { width: `${x.value}px` }), w = r(() => !n.value), T = r(() => (u.maskClosable ?? !1) && !n.value), z = N();
34
- function B() {
31
+ ), { bp: S } = Y(), f = r(() => b.value === "drawer" && S.value === "narrow" && !m.value), W = r(() => S.value === "narrow" && !m.value), $ = r(() => f.value ? "large" : "medium"), T = r(
32
+ () => m.value ? x.value : Math.min(u.width ?? 560, Math.round(x.value * 0.9))
33
+ ), A = r(() => m.value ? void 0 : { width: `${T.value}px` }), k = r(() => !n.value), z = r(() => (u.maskClosable ?? !1) && !n.value), B = M();
34
+ function N() {
35
35
  var o;
36
- b.value !== "modal" || k.value || Z((o = z.value) == null ? void 0 : o.closest(".n-card.n-modal"));
36
+ b.value !== "modal" || m.value || _((o = B.value) == null ? void 0 : o.closest(".n-card.n-modal"));
37
37
  }
38
38
  async function C() {
39
39
  if (!u.onConfirm) {
@@ -42,48 +42,48 @@ const _ = {
42
42
  }
43
43
  n.value = !0;
44
44
  try {
45
- await u.onConfirm() !== !1 ? t.value = !1 : B();
45
+ await u.onConfirm() !== !1 ? t.value = !1 : N();
46
46
  } catch {
47
- B();
47
+ N();
48
48
  } finally {
49
49
  n.value = !1;
50
50
  }
51
51
  }
52
- return q(() => {
52
+ return G(() => {
53
53
  t.value = !1;
54
- }), (o, s) => b.value === "modal" ? (p(), g(e(K), {
54
+ }), (o, s) => b.value === "modal" ? (p(), g(e(L), {
55
55
  key: 0,
56
56
  show: t.value,
57
- "onUpdate:show": s[1] || (s[1] = (f) => t.value = f),
57
+ "onUpdate:show": s[1] || (s[1] = (v) => t.value = v),
58
58
  preset: "card",
59
59
  title: a.title,
60
- closable: w.value,
61
- "close-on-esc": w.value,
62
- "mask-closable": T.value,
63
- class: M({ "smart-form--fullscreen": k.value }),
64
- style: G(A.value)
65
- }, D({
60
+ closable: k.value,
61
+ "close-on-esc": k.value,
62
+ "mask-closable": z.value,
63
+ class: D({ "smart-form--fullscreen": m.value, "smart-form--fixed-foot": W.value }),
64
+ style: H(A.value)
65
+ }, V({
66
66
  footer: l(() => [
67
- v(o.$slots, "footer", {}, () => [
68
- i(e(j), { justify: "end" }, {
67
+ h(o.$slots, "footer", {}, () => [
68
+ i(e(E), { justify: "end" }, {
69
69
  default: l(() => [
70
- i(e(y), {
70
+ i(e(w), {
71
71
  secondary: "",
72
72
  disabled: n.value,
73
- onClick: s[0] || (s[0] = (f) => t.value = !1)
73
+ onClick: s[0] || (s[0] = (v) => t.value = !1)
74
74
  }, {
75
75
  default: l(() => [
76
- h(d(a.cancelText ?? e(c)("common.cancel")), 1)
76
+ y(d(a.cancelText ?? e(c)("common.cancel")), 1)
77
77
  ]),
78
78
  _: 1
79
79
  }, 8, ["disabled"]),
80
- i(e(y), {
80
+ i(e(w), {
81
81
  type: "primary",
82
82
  loading: n.value,
83
83
  onClick: C
84
84
  }, {
85
85
  default: l(() => [
86
- h(d(a.confirmText ?? e(c)("common.confirm")), 1)
86
+ y(d(a.confirmText ?? e(c)("common.confirm")), 1)
87
87
  ]),
88
88
  _: 1
89
89
  }, 8, ["loading"])
@@ -93,87 +93,87 @@ const _ = {
93
93
  ])
94
94
  ]),
95
95
  default: l(() => [
96
- V("i", {
96
+ j("i", {
97
97
  ref_key: "anchor",
98
- ref: z,
98
+ ref: B,
99
99
  hidden: "",
100
100
  "aria-hidden": "true"
101
101
  }, null, 512),
102
- v(o.$slots, "default")
102
+ h(o.$slots, "default")
103
103
  ]),
104
104
  _: 2
105
105
  }, [
106
106
  o.$slots["header-extra"] ? {
107
107
  name: "header-extra",
108
108
  fn: l(() => [
109
- v(o.$slots, "header-extra")
109
+ h(o.$slots, "header-extra")
110
110
  ]),
111
111
  key: "0"
112
112
  } : void 0
113
- ]), 1032, ["show", "title", "closable", "close-on-esc", "mask-closable", "class", "style"])) : (p(), g(e(L), {
113
+ ]), 1032, ["show", "title", "closable", "close-on-esc", "mask-closable", "class", "style"])) : (p(), g(e(O), {
114
114
  key: 1,
115
115
  show: t.value,
116
- "onUpdate:show": s[4] || (s[4] = (f) => t.value = f),
117
- placement: m.value ? "bottom" : "right",
118
- width: m.value ? void 0 : x.value,
119
- height: m.value ? "92%" : void 0,
120
- class: M({ "smart-form-sheet": m.value }),
121
- "close-on-esc": w.value,
122
- "mask-closable": T.value
116
+ "onUpdate:show": s[4] || (s[4] = (v) => t.value = v),
117
+ placement: f.value ? "bottom" : "right",
118
+ width: f.value ? void 0 : T.value,
119
+ height: f.value ? "92%" : void 0,
120
+ class: D({ "smart-form-sheet": f.value }),
121
+ "close-on-esc": k.value,
122
+ "mask-closable": z.value
123
123
  }, {
124
124
  default: l(() => [
125
- i(e(O), {
125
+ i(e(P), {
126
126
  title: a.title,
127
- closable: w.value,
127
+ closable: k.value,
128
128
  "native-scrollbar": !1
129
- }, D({
129
+ }, V({
130
130
  footer: l(() => [
131
- v(o.$slots, "footer", {}, () => [
132
- m.value ? (p(), H("div", _, [
133
- i(e(y), {
131
+ h(o.$slots, "footer", {}, () => [
132
+ f.value ? (p(), J("div", ee, [
133
+ i(e(w), {
134
134
  secondary: "",
135
135
  size: $.value,
136
136
  disabled: n.value,
137
- onClick: s[2] || (s[2] = (f) => t.value = !1)
137
+ onClick: s[2] || (s[2] = (v) => t.value = !1)
138
138
  }, {
139
139
  default: l(() => [
140
- h(d(a.cancelText ?? e(c)("common.cancel")), 1)
140
+ y(d(a.cancelText ?? e(c)("common.cancel")), 1)
141
141
  ]),
142
142
  _: 1
143
143
  }, 8, ["size", "disabled"]),
144
- i(e(y), {
144
+ i(e(w), {
145
145
  type: "primary",
146
146
  size: $.value,
147
147
  loading: n.value,
148
148
  onClick: C
149
149
  }, {
150
150
  default: l(() => [
151
- h(d(a.confirmText ?? e(c)("common.confirm")), 1)
151
+ y(d(a.confirmText ?? e(c)("common.confirm")), 1)
152
152
  ]),
153
153
  _: 1
154
154
  }, 8, ["size", "loading"])
155
- ])) : (p(), g(e(j), {
155
+ ])) : (p(), g(e(E), {
156
156
  key: 1,
157
157
  justify: "end"
158
158
  }, {
159
159
  default: l(() => [
160
- i(e(y), {
160
+ i(e(w), {
161
161
  secondary: "",
162
162
  disabled: n.value,
163
- onClick: s[3] || (s[3] = (f) => t.value = !1)
163
+ onClick: s[3] || (s[3] = (v) => t.value = !1)
164
164
  }, {
165
165
  default: l(() => [
166
- h(d(a.cancelText ?? e(c)("common.cancel")), 1)
166
+ y(d(a.cancelText ?? e(c)("common.cancel")), 1)
167
167
  ]),
168
168
  _: 1
169
169
  }, 8, ["disabled"]),
170
- i(e(y), {
170
+ i(e(w), {
171
171
  type: "primary",
172
172
  loading: n.value,
173
173
  onClick: C
174
174
  }, {
175
175
  default: l(() => [
176
- h(d(a.confirmText ?? e(c)("common.confirm")), 1)
176
+ y(d(a.confirmText ?? e(c)("common.confirm")), 1)
177
177
  ]),
178
178
  _: 1
179
179
  }, 8, ["loading"])
@@ -183,15 +183,15 @@ const _ = {
183
183
  ])
184
184
  ]),
185
185
  default: l(() => [
186
- v(o.$slots, "default")
186
+ h(o.$slots, "default")
187
187
  ]),
188
188
  _: 2
189
189
  }, [
190
190
  o.$slots["header-extra"] ? {
191
191
  name: "header",
192
192
  fn: l(() => [
193
- V("span", null, d(a.title), 1),
194
- v(o.$slots, "header-extra")
193
+ j("span", null, d(a.title), 1),
194
+ h(o.$slots, "header-extra")
195
195
  ]),
196
196
  key: "0"
197
197
  } : void 0
@@ -202,6 +202,6 @@ const _ = {
202
202
  }
203
203
  });
204
204
  export {
205
- ie as default
205
+ ue as default
206
206
  };
207
207
  //# sourceMappingURL=index.vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.vue.js","sources":["../../../src/components/FormContainer/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 弹窗/抽屉二合一表单容器:消化 CRUD 页「saving ref + 手写 footer + n-modal 包装」样板。\n// 形态默认跟随全局偏好(app.formStyle,外观设置里切,默认弹窗),variant 按实例覆盖。\n// 抽屉形态按壳层档位走:宽 / 中档 = 右侧抽屉,窄档(< 600)= 底部抽屉,高 92%,\n// 底栏两个按钮等分整行。弹窗形态不分档,仍是居中卡片。\n// onConfirm 协议:返回 Promise → 确认钮自动 loading;reject 或 resolve(false) → 不关闭\n// (n-form 校验放 onConfirm 首行,失败 reject 即挡住关闭);其余情况自动关。\n// 弹窗形态下,没提交成功(校验不过 / 业务拒绝)时卡片左右晃一下,像 macOS 拒绝输入的提示框,\n// 补足内联提示之外的就地反馈;抽屉与全屏形态不晃(整块贴边,位移会露出缝)。\nimport { computed, onDeactivated, ref } from 'vue'\nimport { NModal, NDrawer, NDrawerContent, NButton, NSpace } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { useWindowSize } from '@vueuse/core'\nimport { useCompactScreen } from '#/composables/useCompactScreen'\nimport { useShellBreakpoint } from '#/composables/useShellBreakpoint'\nimport { useAppStore, type FormStyle } from '#/stores/app'\nimport { shake } from '#/utils/shake'\n\nconst show = defineModel<boolean>('show', { default: false })\n\nconst props = defineProps<{\n title: string\n /** 不传 → 跟随 app.formStyle 全局偏好 */\n variant?: FormStyle\n /** modal=卡片宽 / drawer=抽屉宽,实际生效 min(width, 90vw) */\n width?: number\n /** 见顶部协议注释;不传则确认钮直接关闭 */\n onConfirm?: () => unknown | Promise<unknown>\n confirmText?: string\n cancelText?: string\n /** 默认 false:表单防误触丢输入 */\n maskClosable?: boolean\n /**\n * 铺满视口。默认 `false` —— 不给 `'auto'` 当默认值是刻意的:没传这个 prop 的既有页面\n * 不该因为换了台平板就换一种形态。字段多的表单显式传 `true`,想跟着屏幕走再传 `'auto'`\n * (紧凑屏判定见 composables/useCompactScreen)。\n */\n fullscreen?: boolean | 'auto'\n}>()\n\nconst { t } = useI18n()\nconst app = useAppStore()\n\nconst mode = computed(() => props.variant ?? app.formStyle)\nconst loading = ref(false)\n// 窄屏不溢出:同 SettingsDrawer 惯例,宽度取 min(width, 90vw)。\nconst { width: winW } = useWindowSize()\nconst compact = useCompactScreen()\nconst fullscreen = computed(() =>\n props.fullscreen === 'auto' ? compact.value : props.fullscreen === true,\n)\n// 窄档抽屉改底部抽屉。全屏表单(fullscreen)本就铺满视口,不叠底部抽屉形态。\nconst { bp } = useShellBreakpoint()\nconst sheet = computed(() => mode.value === 'drawer' && bp.value === 'narrow' && !fullscreen.value)\nconst btnSize = computed(() => (sheet.value ? 'large' : 'medium'))\nconst w = computed(() =>\n fullscreen.value ? winW.value : Math.min(props.width ?? 560, Math.round(winW.value * 0.9)),\n)\n// 全屏时不再钉宽度,交给 .smart-form--fullscreen 那条全局样式铺满。\nconst modalStyle = computed(() => (fullscreen.value ? undefined : { width: `${w.value}px` }))\n// 提交中禁止任何途径关闭(esc/遮罩/关闭钮/取消钮),防提交中途关闭导致状态错乱。\nconst canClose = computed(() => !loading.value)\nconst maskClose = computed(() => (props.maskClosable ?? false) && !loading.value)\n\n// 卡片 teleport 到 body,拿不到它的 ref;在正文里放一个不可见的锚点,从锚点向上找到所在的卡片。\nconst anchor = ref<HTMLElement>()\nfunction shakeCard() {\n if (mode.value !== 'modal' || fullscreen.value) return\n shake(anchor.value?.closest('.n-card.n-modal'))\n}\n\nasync function handleConfirm() {\n if (!props.onConfirm) {\n show.value = false\n return\n }\n loading.value = true\n try {\n if ((await props.onConfirm()) !== false) show.value = false\n else shakeCard()\n } catch {\n // 静默:n-form 校验失败已有内联提示;API 错误 toast 由业务 onConfirm 内负责。这里只补一下晃动。\n shakeCard()\n } finally {\n loading.value = false\n }\n}\n\n// keep-alive 页签切走时,teleport 到 body 的弹层会残留可见 → 强制收起。\nonDeactivated(() => {\n show.value = false\n})\n</script>\n\n<template>\n <n-modal\n v-if=\"mode === 'modal'\"\n v-model:show=\"show\"\n preset=\"card\"\n :title=\"title\"\n :closable=\"canClose\"\n :close-on-esc=\"canClose\"\n :mask-closable=\"maskClose\"\n :class=\"{ 'smart-form--fullscreen': fullscreen }\"\n :style=\"modalStyle\"\n >\n <template v-if=\"$slots['header-extra']\" #header-extra>\n <slot name=\"header-extra\" />\n </template>\n <i ref=\"anchor\" hidden aria-hidden=\"true\" />\n <slot />\n <template #footer>\n <slot name=\"footer\">\n <n-space justify=\"end\">\n <n-button secondary :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </n-space>\n </slot>\n </template>\n </n-modal>\n\n <n-drawer\n v-else\n v-model:show=\"show\"\n :placement=\"sheet ? 'bottom' : 'right'\"\n :width=\"sheet ? undefined : w\"\n :height=\"sheet ? '92%' : undefined\"\n :class=\"{ 'smart-form-sheet': sheet }\"\n :close-on-esc=\"canClose\"\n :mask-closable=\"maskClose\"\n >\n <n-drawer-content :title=\"title\" :closable=\"canClose\" :native-scrollbar=\"false\">\n <!-- n-drawer-content 无 header-extra 原生槽:业务传了才自拼 #header(标题 + 附加区并排) -->\n <template v-if=\"$slots['header-extra']\" #header>\n <span>{{ title }}</span>\n <slot name=\"header-extra\" />\n </template>\n <slot />\n <template #footer>\n <slot name=\"footer\">\n <!-- 窄档底部抽屉:两个按钮等分整行、大号 -->\n <div v-if=\"sheet\" class=\"smart-form-foot\">\n <n-button secondary :size=\"btnSize\" :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :size=\"btnSize\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </div>\n <n-space v-else justify=\"end\">\n <n-button secondary :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </n-space>\n </slot>\n </template>\n </n-drawer-content>\n </n-drawer>\n</template>\n\n<!-- 不能加 scoped:n-modal / n-drawer 的内容 teleport 到 body,scoped 编译出的 data-v 属性到不了那棵子树,\n 写了等于没写(这正是\"全屏样式不生效\"最常见的原因)。故用带前缀的类名自行隔离。 -->\n<style>\n.smart-form--fullscreen {\n width: 100vw;\n max-width: 100vw;\n height: 100dvh;\n max-height: 100dvh;\n display: flex;\n flex-direction: column;\n border-radius: 0;\n}\n/* 头/脚按内容高,正文吃满剩余并自己滚 —— 否则长表单会把整张卡撑出视口,底部按钮点不到。 */\n.smart-form--fullscreen > .n-card__content {\n flex: 1;\n min-height: 0;\n overflow: auto;\n}\n/* 窄档底部抽屉:圆角只在上沿;按钮等分整行。\n 高度用 92% 而不是随内容:表单长短不一,固定高度让不同表单的抽屉一致,正文自己滚。 */\n.n-drawer.smart-form-sheet {\n border-radius: var(--radius-xl) var(--radius-xl) 0 0;\n}\n.smart-form-foot {\n display: flex;\n gap: 10px;\n width: 100%;\n}\n.smart-form-foot > .n-button {\n flex: 1;\n}\n</style>\n"],"names":["show","_useModel","__props","props","t","useI18n","app","useAppStore","mode","computed","loading","ref","winW","useWindowSize","compact","useCompactScreen","fullscreen","bp","useShellBreakpoint","sheet","btnSize","w","modalStyle","canClose","maskClose","anchor","shakeCard","shake","_a","handleConfirm","onDeactivated","_createBlock","_unref","NModal","$event","_renderSlot","_ctx","_createVNode","NSpace","NButton","_createTextVNode","_toDisplayString","_createElementVNode","$slots","NDrawer","NDrawerContent","_openBlock","_createElementBlock","_hoisted_1"],"mappings":"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"}
1
+ {"version":3,"file":"index.vue.js","sources":["../../../src/components/FormContainer/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 弹窗/抽屉二合一表单容器:消化 CRUD 页「saving ref + 手写 footer + n-modal 包装」样板。\n// 形态默认跟随全局偏好(app.formStyle,外观设置里切,默认弹窗),variant 按实例覆盖。\n// 抽屉形态按壳层档位走:宽 / 中档 = 右侧抽屉,窄档(< 600)= 底部抽屉,高 92%,\n// 底栏两个按钮等分整行。弹窗形态不分档,仍是居中卡片。\n// onConfirm 协议:返回 Promise → 确认钮自动 loading;reject 或 resolve(false) → 不关闭\n// (n-form 校验放 onConfirm 首行,失败 reject 即挡住关闭);其余情况自动关。\n// 弹窗形态下,没提交成功(校验不过 / 业务拒绝)时卡片左右晃一下,像 macOS 拒绝输入的提示框,\n// 补足内联提示之外的就地反馈;抽屉与全屏形态不晃(整块贴边,位移会露出缝)。\nimport { computed, onDeactivated, ref } from 'vue'\nimport { NModal, NDrawer, NDrawerContent, NButton, NSpace } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { useWindowSize } from '@vueuse/core'\nimport { useCompactScreen } from '#/composables/useCompactScreen'\nimport { useShellBreakpoint } from '#/composables/useShellBreakpoint'\nimport { useAppStore, type FormStyle } from '#/stores/app'\nimport { shake } from '#/utils/shake'\n\nconst show = defineModel<boolean>('show', { default: false })\n\nconst props = defineProps<{\n title: string\n /** 不传 → 跟随 app.formStyle 全局偏好 */\n variant?: FormStyle\n /** modal=卡片宽 / drawer=抽屉宽,实际生效 min(width, 90vw) */\n width?: number\n /** 见顶部协议注释;不传则确认钮直接关闭 */\n onConfirm?: () => unknown | Promise<unknown>\n confirmText?: string\n cancelText?: string\n /** 默认 false:表单防误触丢输入 */\n maskClosable?: boolean\n /**\n * 铺满视口。默认 `false` —— 不给 `'auto'` 当默认值是刻意的:没传这个 prop 的既有页面\n * 不该因为换了台平板就换一种形态。字段多的表单显式传 `true`,想跟着屏幕走再传 `'auto'`\n * (紧凑屏判定见 composables/useCompactScreen)。\n */\n fullscreen?: boolean | 'auto'\n}>()\n\nconst { t } = useI18n()\nconst app = useAppStore()\n\nconst mode = computed(() => props.variant ?? app.formStyle)\nconst loading = ref(false)\n// 窄屏不溢出:同 SettingsDrawer 惯例,宽度取 min(width, 90vw)。\nconst { width: winW } = useWindowSize()\nconst compact = useCompactScreen()\nconst fullscreen = computed(() =>\n props.fullscreen === 'auto' ? compact.value : props.fullscreen === true,\n)\n// 窄档抽屉改底部抽屉。全屏表单(fullscreen)本就铺满视口,不叠底部抽屉形态。\nconst { bp } = useShellBreakpoint()\nconst sheet = computed(() => mode.value === 'drawer' && bp.value === 'narrow' && !fullscreen.value)\n// 窄档的弹窗卡片限高在视口内、正文自己滚:内容比视口高时底栏(保存 / 取消)不随正文滚出屏外。\n// 全屏态自带这套(.smart-form--fullscreen),所以只给非全屏的窄档弹窗加。宽 / 中档保持 naive 默认(整张卡随遮罩层滚)。\nconst fixedFoot = computed(() => bp.value === 'narrow' && !fullscreen.value)\nconst btnSize = computed(() => (sheet.value ? 'large' : 'medium'))\nconst w = computed(() =>\n fullscreen.value ? winW.value : Math.min(props.width ?? 560, Math.round(winW.value * 0.9)),\n)\n// 全屏时不再钉宽度,交给 .smart-form--fullscreen 那条全局样式铺满。\nconst modalStyle = computed(() => (fullscreen.value ? undefined : { width: `${w.value}px` }))\n// 提交中禁止任何途径关闭(esc/遮罩/关闭钮/取消钮),防提交中途关闭导致状态错乱。\nconst canClose = computed(() => !loading.value)\nconst maskClose = computed(() => (props.maskClosable ?? false) && !loading.value)\n\n// 卡片 teleport 到 body,拿不到它的 ref;在正文里放一个不可见的锚点,从锚点向上找到所在的卡片。\nconst anchor = ref<HTMLElement>()\nfunction shakeCard() {\n if (mode.value !== 'modal' || fullscreen.value) return\n shake(anchor.value?.closest('.n-card.n-modal'))\n}\n\nasync function handleConfirm() {\n if (!props.onConfirm) {\n show.value = false\n return\n }\n loading.value = true\n try {\n if ((await props.onConfirm()) !== false) show.value = false\n else shakeCard()\n } catch {\n // 静默:n-form 校验失败已有内联提示;API 错误 toast 由业务 onConfirm 内负责。这里只补一下晃动。\n shakeCard()\n } finally {\n loading.value = false\n }\n}\n\n// keep-alive 页签切走时,teleport 到 body 的弹层会残留可见 → 强制收起。\nonDeactivated(() => {\n show.value = false\n})\n</script>\n\n<template>\n <n-modal\n v-if=\"mode === 'modal'\"\n v-model:show=\"show\"\n preset=\"card\"\n :title=\"title\"\n :closable=\"canClose\"\n :close-on-esc=\"canClose\"\n :mask-closable=\"maskClose\"\n :class=\"{ 'smart-form--fullscreen': fullscreen, 'smart-form--fixed-foot': fixedFoot }\"\n :style=\"modalStyle\"\n >\n <template v-if=\"$slots['header-extra']\" #header-extra>\n <slot name=\"header-extra\" />\n </template>\n <i ref=\"anchor\" hidden aria-hidden=\"true\" />\n <slot />\n <template #footer>\n <slot name=\"footer\">\n <n-space justify=\"end\">\n <n-button secondary :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </n-space>\n </slot>\n </template>\n </n-modal>\n\n <n-drawer\n v-else\n v-model:show=\"show\"\n :placement=\"sheet ? 'bottom' : 'right'\"\n :width=\"sheet ? undefined : w\"\n :height=\"sheet ? '92%' : undefined\"\n :class=\"{ 'smart-form-sheet': sheet }\"\n :close-on-esc=\"canClose\"\n :mask-closable=\"maskClose\"\n >\n <n-drawer-content :title=\"title\" :closable=\"canClose\" :native-scrollbar=\"false\">\n <!-- n-drawer-content 无 header-extra 原生槽:业务传了才自拼 #header(标题 + 附加区并排) -->\n <template v-if=\"$slots['header-extra']\" #header>\n <span>{{ title }}</span>\n <slot name=\"header-extra\" />\n </template>\n <slot />\n <template #footer>\n <slot name=\"footer\">\n <!-- 窄档底部抽屉:两个按钮等分整行、大号 -->\n <div v-if=\"sheet\" class=\"smart-form-foot\">\n <n-button secondary :size=\"btnSize\" :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :size=\"btnSize\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </div>\n <n-space v-else justify=\"end\">\n <n-button secondary :disabled=\"loading\" @click=\"show = false\">\n {{ cancelText ?? t('common.cancel') }}\n </n-button>\n <n-button type=\"primary\" :loading=\"loading\" @click=\"handleConfirm\">\n {{ confirmText ?? t('common.confirm') }}\n </n-button>\n </n-space>\n </slot>\n </template>\n </n-drawer-content>\n </n-drawer>\n</template>\n\n<!-- 不能加 scoped:n-modal / n-drawer 的内容 teleport 到 body,scoped 编译出的 data-v 属性到不了那棵子树,\n 写了等于没写(这正是\"全屏样式不生效\"最常见的原因)。故用带前缀的类名自行隔离。 -->\n<style>\n.smart-form--fullscreen {\n width: 100vw;\n max-width: 100vw;\n height: 100dvh;\n max-height: 100dvh;\n display: flex;\n flex-direction: column;\n border-radius: 0;\n}\n/* 窄档非全屏弹窗:卡片限高在视口内(上下各留 12px 的遮罩),头/脚按内容高,正文吃满剩余并自己滚。 */\n.smart-form--fixed-foot {\n display: flex;\n flex-direction: column;\n max-height: calc(100dvh - 24px);\n}\n/* 头/脚按内容高,正文吃满剩余并自己滚 —— 否则长表单会把整张卡撑出视口,底部按钮点不到。\n 内容层的类名两种都写:naive-ui 2.45 输出 .n-card-content,只有 content-scrollable 时的滚动外层才带 __content-scrollbar,\n 光写 .n-card__content 在 2.45 上一条都命不中。 */\n.smart-form--fullscreen > :is(.n-card-content, .n-card__content),\n.smart-form--fixed-foot > :is(.n-card-content, .n-card__content) {\n flex: 1;\n min-height: 0;\n overflow: auto;\n}\n/* 窄档底部抽屉:圆角只在上沿;按钮等分整行。\n 高度用 92% 而不是随内容:表单长短不一,固定高度让不同表单的抽屉一致,正文自己滚。 */\n.n-drawer.smart-form-sheet {\n border-radius: var(--radius-xl) var(--radius-xl) 0 0;\n}\n.smart-form-foot {\n display: flex;\n gap: 10px;\n width: 100%;\n}\n.smart-form-foot > .n-button {\n flex: 1;\n}\n</style>\n"],"names":["show","_useModel","__props","props","t","useI18n","app","useAppStore","mode","computed","loading","ref","winW","useWindowSize","compact","useCompactScreen","fullscreen","bp","useShellBreakpoint","sheet","fixedFoot","btnSize","w","modalStyle","canClose","maskClose","anchor","shakeCard","shake","_a","handleConfirm","onDeactivated","_createBlock","_unref","NModal","$event","_normalizeClass","_renderSlot","_ctx","_createVNode","NSpace","NButton","_createTextVNode","_toDisplayString","_createElementVNode","$slots","NDrawer","NDrawerContent","_openBlock","_createElementBlock","_hoisted_1"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ type __VLS_Props = {
2
+ /** 抽屉标题,通常是分组名(「机构」「分类」) */
3
+ title: string;
4
+ };
5
+ type __VLS_ModelProps = {
6
+ 'show'?: boolean;
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
9
+ declare var __VLS_14: {};
10
+ type __VLS_Slots = {} & {
11
+ default?: (props: typeof __VLS_14) => any;
12
+ };
13
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
14
+ "update:show": (value: boolean) => any;
15
+ }, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
16
+ "onUpdate:show"?: ((value: boolean) => any) | undefined;
17
+ }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
18
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
19
+ declare const _default: typeof __VLS_export;
20
+ export default _default;
21
+ type __VLS_WithSlots<T, S> = T & {
22
+ new (): {
23
+ $slots: S;
24
+ };
25
+ };
@@ -0,0 +1,40 @@
1
+ import { defineComponent as r, useModel as d, openBlock as i, createBlock as f, unref as l, withCtx as s, createVNode as u, createElementVNode as w, renderSlot as m, mergeModels as p } from "vue";
2
+ import { NDrawer as c, NDrawerContent as h } from "naive-ui";
3
+ const _ = { class: "side-panel-body" }, N = /* @__PURE__ */ r({
4
+ __name: "index",
5
+ props: /* @__PURE__ */ p({
6
+ title: {}
7
+ }, {
8
+ show: { type: Boolean, default: !1 },
9
+ showModifiers: {}
10
+ }),
11
+ emits: ["update:show"],
12
+ setup(e) {
13
+ const t = d(e, "show");
14
+ return (a, o) => (i(), f(l(c), {
15
+ show: t.value,
16
+ "onUpdate:show": o[0] || (o[0] = (n) => t.value = n),
17
+ placement: "left",
18
+ width: "min(300px, 88vw)"
19
+ }, {
20
+ default: s(() => [
21
+ u(l(h), {
22
+ title: e.title,
23
+ closable: ""
24
+ }, {
25
+ default: s(() => [
26
+ w("div", _, [
27
+ m(a.$slots, "default")
28
+ ])
29
+ ]),
30
+ _: 3
31
+ }, 8, ["title"])
32
+ ]),
33
+ _: 3
34
+ }, 8, ["show"]));
35
+ }
36
+ });
37
+ export {
38
+ N as default
39
+ };
40
+ //# sourceMappingURL=index.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.vue.js","sources":["../../../src/components/SidePanelDrawer/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 带左侧面板的页面在窄内容区里的面板抽屉:从左侧滑出,标题栏写分组名,插槽放面板内容。\n// 与 useSidePanel 配对使用:`compact` 时不画 aside,点工具栏按钮把 `drawerOpen` 置 true 打开这里。\n// 面板里的 `.side-filter__btn` 等沿用 layout.css 的侧栏约定,抽屉里是触屏,图标按钮放大到可点的尺寸。\nimport { NDrawer, NDrawerContent } from 'naive-ui'\n\ndefineProps<{\n /** 抽屉标题,通常是分组名(「机构」「分类」) */\n title: string\n}>()\n\nconst show = defineModel<boolean>('show', { default: false })\n</script>\n\n<template>\n <n-drawer v-model:show=\"show\" placement=\"left\" width=\"min(300px, 88vw)\">\n <n-drawer-content :title=\"title\" closable>\n <div class=\"side-panel-body\">\n <slot />\n </div>\n </n-drawer-content>\n </n-drawer>\n</template>\n\n<!-- 不加 scoped:面板内容是调用方模板里的节点,带的是调用方的 data-v,组件自己的 scoped 规则够不着里面的 .side-filter__btn。 -->\n<style>\n/* 与 aside 里同样的纵向堆叠与间距 */\n.side-panel-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 抽屉在触屏上用:头部图标按钮放大到可点的尺寸 */\n.side-panel-body .side-filter__btn {\n width: 32px;\n height: 32px;\n}\n</style>\n"],"names":["show","_useModel","__props","_createBlock","_unref","NDrawer","$event","_createVNode","NDrawerContent","_createElementVNode","_hoisted_1","_renderSlot","_ctx"],"mappings":";;;;;;;;;;;;AAWA,UAAMA,IAAOC,EAAoBC,GAAC,MAA0B;2BAI1DC,EAMWC,EAAAC,CAAA,GAAA;AAAA,MANO,MAAML,EAAA;AAAA,8CAAAA,EAAI,QAAAM;AAAA,MAAE,WAAU;AAAA,MAAO,OAAM;AAAA,IAAA;iBACnD,MAImB;AAAA,QAJnBC,EAImBH,EAAAI,CAAA,GAAA;AAAA,UAJA,OAAON,EAAA;AAAA,UAAO,UAAA;AAAA,QAAA;qBAC/B,MAEM;AAAA,YAFNO,EAEM,OAFNC,GAEM;AAAA,cADJC,EAAQC,EAAA,QAAA,SAAA;AAAA,YAAA;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ import o from "./index.vue.js";
2
+ /* empty css */
3
+ export {
4
+ o as default
5
+ };
6
+ //# sourceMappingURL=index3.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index3.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -1,17 +1,17 @@
1
- import { inject as v, computed as c, ref as h, provide as w, watch as m, onScopeDispose as S } from "vue";
2
- import { useWindowSize as b } from "@vueuse/core";
3
- const R = 600, l = 1400;
1
+ import { inject as v, computed as c, ref as h, provide as w, watch as S, onScopeDispose as _ } from "vue";
2
+ import { useWindowSize as m } from "@vueuse/core";
3
+ const E = 600, l = 1400, A = 1e3;
4
4
  function u(t) {
5
- return t >= l ? "wide" : t >= R ? "mid" : "narrow";
5
+ return t >= l ? "wide" : t >= E ? "mid" : "narrow";
6
6
  }
7
7
  const p = Symbol("shellBreakpoint");
8
- function E(t) {
8
+ function k(t) {
9
9
  const n = h(l), d = c(() => u(n.value)), o = { width: n, bp: d };
10
10
  w(p, o);
11
11
  let e;
12
- return m(
12
+ return S(
13
13
  t,
14
- (i, _, f) => {
14
+ (i, L, f) => {
15
15
  !i || typeof ResizeObserver > "u" || (e = new ResizeObserver((a) => {
16
16
  var s;
17
17
  const r = (s = a[0]) == null ? void 0 : s.contentRect.width;
@@ -19,19 +19,20 @@ function E(t) {
19
19
  }), e.observe(i), f(() => e == null ? void 0 : e.disconnect()));
20
20
  },
21
21
  { flush: "post" }
22
- ), S(() => e == null ? void 0 : e.disconnect()), o;
22
+ ), _(() => e == null ? void 0 : e.disconnect()), o;
23
23
  }
24
- function L() {
24
+ function B() {
25
25
  const t = v(p, null);
26
26
  if (t) return t;
27
- const { width: n } = b();
27
+ const { width: n } = m();
28
28
  return { width: n, bp: c(() => u(n.value)) };
29
29
  }
30
30
  export {
31
- R as SHELL_NARROW_MAX,
31
+ E as SHELL_NARROW_MAX,
32
+ A as SHELL_PANEL_DRAWER_MAX,
32
33
  l as SHELL_WIDE_MIN,
33
- E as provideShellBreakpoint,
34
+ k as provideShellBreakpoint,
34
35
  u as shellBreakpointOf,
35
- L as useShellBreakpoint
36
+ B as useShellBreakpoint
36
37
  };
37
38
  //# sourceMappingURL=useShellBreakpoint.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useShellBreakpoint.js","sources":["../../src/composables/useShellBreakpoint.ts"],"sourcesContent":["// 壳层档位:按「内容区」宽度判(不是视口),侧栏展开 / 收起、抽屉开合都会改变档位。\n//\n// wide ≥ 1400 统计卡等页面栅格铺开\n// mid 600–1399 栅格收窄;带左侧面板的页面在内容区 < 1000 时面板收进抽屉\n// narrow < 600 整页自然滚动,不锁一屏高度;统计卡两列;设置行上下排\n//\n// 阈值 600 = smart-naive-table 的窄档(表格根节点宽 < 600 换卡片)。壳层 narrow 必须和它对齐:\n// 壳层进入「整页自然滚动」时表格一定已是卡片列表;壳层不是 narrow 时表格一定仍是表格。\n// 两层阈值错开会出现一段宽度:页面不定高、表格却还在 fill-height,表体塌成最小高度。\n// 阈值直接取 600:量的是 `.page` 的 content box(ResizeObserver 的 contentRect 本身已扣掉内边距),\n// 留白不进数字,日后改 `.page` 的内边距不用回来改阈值。\n//\n// 用法:default.vue 调 provideShellBreakpoint(el) 绑定被量的元素;页面里调 useShellBreakpoint()\n// 取 { bp, width }(没有壳层时,如单测或独立挂载,退回按视口宽度估算)。\nimport {\n computed,\n inject,\n onScopeDispose,\n provide,\n ref,\n watch,\n type InjectionKey,\n type Ref,\n} from 'vue'\nimport { useWindowSize } from '@vueuse/core'\n\nexport type ShellBreakpoint = 'wide' | 'mid' | 'narrow'\n\n/** 壳层 narrow 的上界(不含)。与表格窄档 600 对齐,见文件头。 */\nexport const SHELL_NARROW_MAX = 600\n/** 壳层 wide 的下界(含)。 */\nexport const SHELL_WIDE_MIN = 1400\n/** 带左侧面板的页面:内容区窄于此值时面板收进抽屉。 */\nexport const SHELL_PANEL_DRAWER_MAX = 1000\n\n/** 纯判定,便于单测。 */\nexport function shellBreakpointOf(width: number): ShellBreakpoint {\n return width >= SHELL_WIDE_MIN ? 'wide' : width >= SHELL_NARROW_MAX ? 'mid' : 'narrow'\n}\n\nexport interface ShellBreakpointState {\n /** 内容区(`.page` content box)宽度,px */\n width: Ref<number>\n bp: Readonly<Ref<ShellBreakpoint>>\n}\n\nconst KEY: InjectionKey<ShellBreakpointState> = Symbol('shellBreakpoint')\n\n/** 壳层调用:盯住 el 的 content box,把 { width, bp } 提供给后代。 */\nexport function provideShellBreakpoint(el: Ref<HTMLElement | undefined>): ShellBreakpointState {\n const width = ref(SHELL_WIDE_MIN) // 首帧按宽档,避免挂载瞬间闪一下 narrow 的自然滚动布局\n const bp = computed(() => shellBreakpointOf(width.value))\n const state: ShellBreakpointState = { width, bp }\n provide(KEY, state)\n\n let ro: ResizeObserver | undefined\n watch(\n el,\n (node, _old, onCleanup) => {\n if (!node || typeof ResizeObserver === 'undefined') return\n ro = new ResizeObserver(entries => {\n const w = entries[0]?.contentRect.width\n if (w !== undefined) width.value = w\n })\n ro.observe(node)\n onCleanup(() => ro?.disconnect())\n },\n { flush: 'post' },\n )\n onScopeDispose(() => ro?.disconnect())\n return state\n}\n\n/** 页面 / 组件调用:取壳层档位。壳层之外退回视口宽度(近似,够单测与独立挂载用)。 */\nexport function useShellBreakpoint(): ShellBreakpointState {\n const injected = inject(KEY, null)\n if (injected) return injected\n const { width } = useWindowSize()\n return { width, bp: computed(() => shellBreakpointOf(width.value)) }\n}\n"],"names":["SHELL_NARROW_MAX","SHELL_WIDE_MIN","shellBreakpointOf","width","KEY","provideShellBreakpoint","el","ref","bp","computed","state","provide","ro","watch","node","_old","onCleanup","entries","w","_a","onScopeDispose","useShellBreakpoint","injected","inject","useWindowSize"],"mappings":";;AA6BO,MAAMA,IAAmB,KAEnBC,IAAiB;AAKvB,SAASC,EAAkBC,GAAgC;AAChE,SAAOA,KAASF,IAAiB,SAASE,KAASH,IAAmB,QAAQ;AAChF;AAQA,MAAMI,IAA0C,OAAO,iBAAiB;AAGjE,SAASC,EAAuBC,GAAwD;AAC7F,QAAMH,IAAQI,EAAIN,CAAc,GAC1BO,IAAKC,EAAS,MAAMP,EAAkBC,EAAM,KAAK,CAAC,GAClDO,IAA8B,EAAE,OAAAP,GAAO,IAAAK,EAAA;AAC7C,EAAAG,EAAQP,GAAKM,CAAK;AAElB,MAAIE;AACJ,SAAAC;AAAA,IACEP;AAAA,IACA,CAACQ,GAAMC,GAAMC,MAAc;AACzB,MAAI,CAACF,KAAQ,OAAO,iBAAmB,QACvCF,IAAK,IAAI,eAAe,CAAAK,MAAW;;AACjC,cAAMC,KAAIC,IAAAF,EAAQ,CAAC,MAAT,gBAAAE,EAAY,YAAY;AAClC,QAAID,MAAM,WAAWf,EAAM,QAAQe;AAAA,MACrC,CAAC,GACDN,EAAG,QAAQE,CAAI,GACfE,EAAU,MAAMJ,KAAA,gBAAAA,EAAI,YAAY;AAAA,IAClC;AAAA,IACA,EAAE,OAAO,OAAA;AAAA,EAAO,GAElBQ,EAAe,MAAMR,KAAA,gBAAAA,EAAI,YAAY,GAC9BF;AACT;AAGO,SAASW,IAA2C;AACzD,QAAMC,IAAWC,EAAOnB,GAAK,IAAI;AACjC,MAAIkB,EAAU,QAAOA;AACrB,QAAM,EAAE,OAAAnB,EAAA,IAAUqB,EAAA;AAClB,SAAO,EAAE,OAAArB,GAAO,IAAIM,EAAS,MAAMP,EAAkBC,EAAM,KAAK,CAAC,EAAA;AACnE;"}
1
+ {"version":3,"file":"useShellBreakpoint.js","sources":["../../src/composables/useShellBreakpoint.ts"],"sourcesContent":["// 壳层档位:按「内容区」宽度判(不是视口),侧栏展开 / 收起、抽屉开合都会改变档位。\n//\n// wide ≥ 1400 统计卡等页面栅格铺开\n// mid 600–1399 栅格收窄;带左侧面板的页面在内容区 < 1000 时面板收进抽屉(useSidePanel + SidePanelDrawer)\n// narrow < 600 整页自然滚动,不锁一屏高度;统计卡两列;设置行上下排\n//\n// 阈值 600 = smart-naive-table 的窄档(表格根节点宽 < 600 换卡片)。壳层 narrow 必须和它对齐:\n// 壳层进入「整页自然滚动」时表格一定已是卡片列表;壳层不是 narrow 时表格一定仍是表格。\n// 两层阈值错开会出现一段宽度:页面不定高、表格却还在 fill-height,表体塌成最小高度。\n// 这条对齐只对开了 card-on-narrow 的表成立;没开的表(表格形态的 fill-height 表)由 layout.css ①′ 让那一页保持定高兜底。\n// 阈值直接取 600:量的是 `.page` 的 content box(ResizeObserver 的 contentRect 本身已扣掉内边距),\n// 留白不进数字,日后改 `.page` 的内边距不用回来改阈值。\n//\n// 用法:default.vue 调 provideShellBreakpoint(el) 绑定被量的元素;页面里调 useShellBreakpoint()\n// 取 { bp, width }(没有壳层时,如单测或独立挂载,退回按视口宽度估算)。\nimport {\n computed,\n inject,\n onScopeDispose,\n provide,\n ref,\n watch,\n type InjectionKey,\n type Ref,\n} from 'vue'\nimport { useWindowSize } from '@vueuse/core'\n\nexport type ShellBreakpoint = 'wide' | 'mid' | 'narrow'\n\n/** 壳层 narrow 的上界(不含)。与表格窄档 600 对齐,见文件头。 */\nexport const SHELL_NARROW_MAX = 600\n/** 壳层 wide 的下界(含)。 */\nexport const SHELL_WIDE_MIN = 1400\n/** 带左侧面板的页面:内容区窄于此值时面板收进抽屉。 */\nexport const SHELL_PANEL_DRAWER_MAX = 1000\n\n/** 纯判定,便于单测。 */\nexport function shellBreakpointOf(width: number): ShellBreakpoint {\n return width >= SHELL_WIDE_MIN ? 'wide' : width >= SHELL_NARROW_MAX ? 'mid' : 'narrow'\n}\n\nexport interface ShellBreakpointState {\n /** 内容区(`.page` content box)宽度,px */\n width: Ref<number>\n bp: Readonly<Ref<ShellBreakpoint>>\n}\n\nconst KEY: InjectionKey<ShellBreakpointState> = Symbol('shellBreakpoint')\n\n/** 壳层调用:盯住 el 的 content box,把 { width, bp } 提供给后代。 */\nexport function provideShellBreakpoint(el: Ref<HTMLElement | undefined>): ShellBreakpointState {\n const width = ref(SHELL_WIDE_MIN) // 首帧按宽档,避免挂载瞬间闪一下 narrow 的自然滚动布局\n const bp = computed(() => shellBreakpointOf(width.value))\n const state: ShellBreakpointState = { width, bp }\n provide(KEY, state)\n\n let ro: ResizeObserver | undefined\n watch(\n el,\n (node, _old, onCleanup) => {\n if (!node || typeof ResizeObserver === 'undefined') return\n ro = new ResizeObserver(entries => {\n const w = entries[0]?.contentRect.width\n if (w !== undefined) width.value = w\n })\n ro.observe(node)\n onCleanup(() => ro?.disconnect())\n },\n { flush: 'post' },\n )\n onScopeDispose(() => ro?.disconnect())\n return state\n}\n\n/** 页面 / 组件调用:取壳层档位。壳层之外退回视口宽度(近似,够单测与独立挂载用)。 */\nexport function useShellBreakpoint(): ShellBreakpointState {\n const injected = inject(KEY, null)\n if (injected) return injected\n const { width } = useWindowSize()\n return { width, bp: computed(() => shellBreakpointOf(width.value)) }\n}\n"],"names":["SHELL_NARROW_MAX","SHELL_WIDE_MIN","SHELL_PANEL_DRAWER_MAX","shellBreakpointOf","width","KEY","provideShellBreakpoint","el","ref","bp","computed","state","provide","ro","watch","node","_old","onCleanup","entries","w","_a","onScopeDispose","useShellBreakpoint","injected","inject","useWindowSize"],"mappings":";;AA8BO,MAAMA,IAAmB,KAEnBC,IAAiB,MAEjBC,IAAyB;AAG/B,SAASC,EAAkBC,GAAgC;AAChE,SAAOA,KAASH,IAAiB,SAASG,KAASJ,IAAmB,QAAQ;AAChF;AAQA,MAAMK,IAA0C,OAAO,iBAAiB;AAGjE,SAASC,EAAuBC,GAAwD;AAC7F,QAAMH,IAAQI,EAAIP,CAAc,GAC1BQ,IAAKC,EAAS,MAAMP,EAAkBC,EAAM,KAAK,CAAC,GAClDO,IAA8B,EAAE,OAAAP,GAAO,IAAAK,EAAA;AAC7C,EAAAG,EAAQP,GAAKM,CAAK;AAElB,MAAIE;AACJ,SAAAC;AAAA,IACEP;AAAA,IACA,CAACQ,GAAMC,GAAMC,MAAc;AACzB,MAAI,CAACF,KAAQ,OAAO,iBAAmB,QACvCF,IAAK,IAAI,eAAe,CAAAK,MAAW;;AACjC,cAAMC,KAAIC,IAAAF,EAAQ,CAAC,MAAT,gBAAAE,EAAY,YAAY;AAClC,QAAID,MAAM,WAAWf,EAAM,QAAQe;AAAA,MACrC,CAAC,GACDN,EAAG,QAAQE,CAAI,GACfE,EAAU,MAAMJ,KAAA,gBAAAA,EAAI,YAAY;AAAA,IAClC;AAAA,IACA,EAAE,OAAO,OAAA;AAAA,EAAO,GAElBQ,EAAe,MAAMR,KAAA,gBAAAA,EAAI,YAAY,GAC9BF;AACT;AAGO,SAASW,IAA2C;AACzD,QAAMC,IAAWC,EAAOnB,GAAK,IAAI;AACjC,MAAIkB,EAAU,QAAOA;AACrB,QAAM,EAAE,OAAAnB,EAAA,IAAUqB,EAAA;AAClB,SAAO,EAAE,OAAArB,GAAO,IAAIM,EAAS,MAAMP,EAAkBC,EAAM,KAAK,CAAC,EAAA;AACnE;"}
@@ -0,0 +1,9 @@
1
+ import { ComputedRef, Ref } from 'vue';
2
+ export interface SidePanelState {
3
+ /** 内容区宽度 < 阈值:面板该收进抽屉。 */
4
+ compact: ComputedRef<boolean>;
5
+ /** 抽屉开合;宽度回到不紧凑时自动关,别留一个对不上布局的遮罩。 */
6
+ drawerOpen: Ref<boolean>;
7
+ }
8
+ /** @param max 内容区窄于它时面板收进抽屉;默认 `SHELL_PANEL_DRAWER_MAX`(1000)。 */
9
+ export declare function useSidePanel(max?: number): SidePanelState;
@@ -0,0 +1,12 @@
1
+ import { computed as n, ref as c, watch as f } from "vue";
2
+ import { SHELL_PANEL_DRAWER_MAX as i, useShellBreakpoint as l } from "./useShellBreakpoint.js";
3
+ function u(o = i) {
4
+ const { width: r } = l(), e = n(() => r.value < o), t = c(!1);
5
+ return f(e, (a) => {
6
+ a || (t.value = !1);
7
+ }), { compact: e, drawerOpen: t };
8
+ }
9
+ export {
10
+ u as useSidePanel
11
+ };
12
+ //# sourceMappingURL=useSidePanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSidePanel.js","sources":["../../src/composables/useSidePanel.ts"],"sourcesContent":["// 带左侧面板(分组 / 机构树…)的页面:内容区窄于阈值时,面板不再占一列,收进左侧抽屉。\n//\n// 判据是壳层内容区宽度(`useShellBreakpoint().width`,不是视口):侧栏展开 / 收起、抽屉开合都会改变它。\n// 面板占掉的宽度本来就会让右侧表格变窄,收进抽屉后表格得到整行宽度;表格自己的窄档(卡片列表)由库按表格宽度另行判定。\n//\n// 用法(范例 views/system/user):\n// const { compact, drawerOpen } = useSidePanel()\n// <aside v-if=\"!compact\" class=\"side-filter\">…面板…</aside>\n// <SmartTable> … #toolbar 里放一个按钮:`@click=\"drawerOpen = true\"` …</SmartTable>\n// <SidePanelDrawer v-model:show=\"drawerOpen\" :title=\"…\">…同一份面板内容…</SidePanelDrawer>\n// 面板内容只写一份用 VueUse 的 createReusableTemplate,宽屏放进 aside、窄屏放进抽屉。\nimport { computed, ref, watch, type ComputedRef, type Ref } from 'vue'\nimport { SHELL_PANEL_DRAWER_MAX, useShellBreakpoint } from './useShellBreakpoint'\n\nexport interface SidePanelState {\n /** 内容区宽度 < 阈值:面板该收进抽屉。 */\n compact: ComputedRef<boolean>\n /** 抽屉开合;宽度回到不紧凑时自动关,别留一个对不上布局的遮罩。 */\n drawerOpen: Ref<boolean>\n}\n\n/** @param max 内容区窄于它时面板收进抽屉;默认 `SHELL_PANEL_DRAWER_MAX`(1000)。 */\nexport function useSidePanel(max: number = SHELL_PANEL_DRAWER_MAX): SidePanelState {\n const { width } = useShellBreakpoint()\n const compact = computed(() => width.value < max)\n const drawerOpen = ref(false)\n watch(compact, isCompact => {\n if (!isCompact) drawerOpen.value = false\n })\n return { compact, drawerOpen }\n}\n"],"names":["useSidePanel","max","SHELL_PANEL_DRAWER_MAX","width","useShellBreakpoint","compact","computed","drawerOpen","ref","watch","isCompact"],"mappings":";;AAsBO,SAASA,EAAaC,IAAcC,GAAwC;AACjF,QAAM,EAAE,OAAAC,EAAA,IAAUC,EAAA,GACZC,IAAUC,EAAS,MAAMH,EAAM,QAAQF,CAAG,GAC1CM,IAAaC,EAAI,EAAK;AAC5B,SAAAC,EAAMJ,GAAS,CAAAK,MAAa;AAC1B,IAAKA,MAAWH,EAAW,QAAQ;AAAA,EACrC,CAAC,GACM,EAAE,SAAAF,GAAS,YAAAE,EAAA;AACpB;"}
package/dist/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export { useTableZoom } from './composables/useTableZoom';
38
38
  export { useTabTitle } from './composables/useTabTitle';
39
39
  export { useTableTitle } from './composables/useTableTitle';
40
40
  export { useShellBreakpoint } from './composables/useShellBreakpoint';
41
+ export { useSidePanel, type SidePanelState } from './composables/useSidePanel';
41
42
  export { useTheme } from './composables/useTheme';
42
43
  export { useVisualViewportHeight } from './composables/useVisualViewportHeight';
43
44
  export { default as ApiSelect } from './components/ApiSelect/index.vue';
@@ -64,6 +65,7 @@ export { default as MarkdownView } from './components/MarkdownEditor/MarkdownVie
64
65
  export { default as OrgTreeSelect } from './components/OrgTreeSelect/index.vue';
65
66
  export { default as PasswordStrength } from './components/PasswordStrength/index.vue';
66
67
  export { default as RoleSelect } from './components/RoleSelect/index.vue';
68
+ export { default as SidePanelDrawer } from './components/SidePanelDrawer/index.vue';
67
69
  export { default as StatusSwitch } from './components/StatusSwitch/index.vue';
68
70
  export { default as TableTotal } from './components/TableTotal/index.vue';
69
71
  export { default as TableZoomButton } from './components/TableZoomButton/index.vue';