savor-ui 0.12.0 → 0.12.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
 
4
4
 
5
+ ## [0.12.1](https://gitee.com/mach552/savor-ui/compare/v0.12.0...v0.12.1) (2026-07-16)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **cascader:** 修复类型错误 ([84cd4a2](https://gitee.com/mach552/savor-ui/commits/84cd4a23c677746fa75446b00b66b9b76081f1d9))
11
+ * **menu:** 修复菜单的level错误问题 ([e956f57](https://gitee.com/mach552/savor-ui/commits/e956f573138984ce39af1ded97bc3ff9abb7f6a0))
12
+ * **modal:** 修复过渡组件报无子元素警告的问题 ([0e29418](https://gitee.com/mach552/savor-ui/commits/0e29418a85cd27f08b77cfbf9ed9ba9fe078f463))
13
+ * **modal:** 修复内容尺寸变化不居中的问题 ([0cb4e66](https://gitee.com/mach552/savor-ui/commits/0cb4e665a027cb5814b24c734516c450ff6aa3f6))
14
+
5
15
  # [0.12.0](https://gitee.com/mach552/savor-ui/compare/v0.11.0...v0.12.0) (2026-07-15)
6
16
 
7
17
 
package/README.md CHANGED
@@ -1,39 +1,70 @@
1
- <H1 align="center">savor-ui</H1>
1
+ # savor-ui
2
2
 
3
- - 基于 Vue3 的UI组件库,仅支持 Vue Vapor 模式
3
+ 基于 Vue3 Vapor 模式打造的轻量自用 UI 组件库
4
+
5
+ **savor-ui** 是一款轻量级 UI 组件库,专为 **Vue Vapor 编译模式** 设计,针对性适配 Vapor 编译特性,全部采用**组合式 Api**编写,主打轻量化、高性能、高定制化,适配现代前端工程化开发场景。
6
+
7
+ 本组件库为个人自用开源组件库,整合多款主流优秀 Vue UI 组件库的设计思路与交互逻辑,优化适配 Vapor 专属编译规则,保留简洁易用的开发体验,同时支持灵活的主题自定义能力,满足日常后台系统、中后台业务页面开发需求。
8
+
9
+ - **专属适配**:仅支持 Vue3 Vapor 编译模式,不兼容 Runtime 模式,深度适配 Vapor 编译优化特性,打包体积更小、运行性能更高
10
+
11
+ - **轻量高效**:组件轻量化封装,无冗余依赖,按需加载友好,适配现代化工程化项目
12
+
13
+ - **主题定制**:内置完整明暗主题体系,支持全局一键主题切换、主色自定义、样式灵活覆盖
14
+
15
+ - **规范统一**:参考主流企业级组件库设计规范,交互逻辑统一,降低开发上手成本
16
+
17
+ 项目部分组件设计、交互逻辑、主题配置方案参考以下业界优秀开源组件库,特此致谢!
18
+
19
+ - [Element Plus](https://cn.element-plus.org/):Vue3 主流企业级组件库,成熟的组件交互与设计体系
20
+
21
+ - [Arco Design Vue](https://arco.design/vue):字节跳动出品,优秀的按需加载、主题配置与工程化适配方案
22
+
23
+ - [Antdv Next](https://www.antdv-next.cn/index-cn):参考 Ant Design v6 设计规范,灵活的主题算法与现代化 UI 设计
24
+
25
+ - [Naive UI](https://www.naiveui.com/zh-CN):高质量、类型完备的 Vue3 组件库,严谨的组件设计逻辑
26
+
27
+ - [Prime Vue](https://primevue.dev/):功能丰富的跨端 UI 组件库,多样化业务组件交互参考
28
+
29
+ - [Vexip UI](https://www.vexipui.com/):轻量化、高自定义的 Vue3 组件库设计思路
4
30
 
5
31
  ## 快速开始
6
32
 
7
- ### Vue 版本
33
+ [文档地址](http://122.51.220.99:12121/)
34
+
35
+ 适配 Vue3 最新 Vapor 编译模式,最低版本要求:
8
36
 
9
- vue >= 3.6.0-beta.11
37
+ `vue >= 3.6.0-beta.11`
10
38
 
11
- ### 安装
39
+ **注意**:本组件库**仅支持 Vue Vapor 编译模式**,不支持 IE 浏览器及传统 Vue3 Runtime 编译模式。
40
+
41
+ 推荐使用 `pnpm` 安装(极速、节省磁盘空间)
12
42
 
13
43
  ```shell
14
44
  pnpm add savor-ui
15
45
  ```
16
46
 
17
- ### 完整引入
47
+ 在项目入口文件全局引入组件库及样式,完成全局注册:
18
48
 
19
49
  ```ts
20
50
  import { createVaporApp } from "vue";
21
51
  import App from "./App.vue";
22
52
  import SavorUI from "savor-ui";
23
53
 
54
+ // 引入组件库全局主题样式
24
55
  import "savor-ui/theme/index.scss";
25
56
 
26
57
  const app = createVaporApp(App);
58
+ // 全局注册所有 SavorUI 组件
27
59
  app.use(SavorUI);
28
60
  app.mount("#app");
29
61
  ```
30
62
 
31
- ### 调整主题
32
-
33
- 主题可以通过使用 `s-config-provider` 组件调整。
63
+ 组件库内置明暗两套主题,通过 `s-config-provider` 全局配置组件实现**全局主题、主色值自定义**,支持动态切换主题:
34
64
 
35
65
  ```html
36
66
  <template>
67
+ <!-- 全局主题配置包裹根路由,全局生效 -->
37
68
  <s-config-provider :theme="theme">
38
69
  <VaporRouterView />
39
70
  </s-config-provider>
@@ -43,12 +74,15 @@ app.mount("#app");
43
74
  import { reactive } from "vue";
44
75
  import { VaporRouterView } from "vue-router";
45
76
 
77
+ // 自定义明暗主题主色及配置
46
78
  const theme = reactive({
79
+ // 亮色主题配置
47
80
  lightTheme: {
48
- colorPrimary: "#1890ff",
81
+ colorPrimary: "#1890ff", // 全局主色
49
82
  },
83
+ // 暗色主题配置
50
84
  darkTheme: {
51
- colorPrimary: "#1890ff",
85
+ colorPrimary: "#1890ff", // 全局主色
52
86
  },
53
87
  });
54
88
  </script>
@@ -115,7 +115,7 @@ var k = /*@__PURE__*/ p({
115
115
  if (h.pathMode && typeof A.value == "string") {
116
116
  let t = A.value.split(h.pathSeparator);
117
117
  e = X(h.options, t);
118
- } else e = Y(h.options, A.value);
118
+ } else Array.isArray(A.value) || (e = Y(h.options, A.value));
119
119
  e && (F.value = e.map((e) => e[L.value.key]), I.value = e, R.value = V(e));
120
120
  }, Q = (e) => {
121
121
  A.value === void 0 ? e.forEach((e) => {
@@ -58,7 +58,7 @@ var P = /*@__PURE__*/ m({
58
58
  }), A(() => I?.openKeys.value || [], (e) => {
59
59
  e && e.length > 0 && (B.value = e.includes(E.name));
60
60
  }, { immediate: !0 }), y(() => {
61
- F.value?.style.setProperty("--s-menu-level", `${z}`), L?.registerDescendant(E.name);
61
+ F.value?.style.setProperty("--s-menu-level", `${z.value}`), L?.registerDescendant(E.name);
62
62
  }), b(o, { level: z }), b(s, {
63
63
  registerDescendant: (e) => {
64
64
  V.value.add(e), L?.registerDescendant(e);
@@ -2,25 +2,25 @@ import { getSize as e } from "../../../utils/src/tool.mjs";
2
2
  import "../../../utils/index.mjs";
3
3
  import { useDrag as t } from "../../../hooks/src/use-drag.mjs";
4
4
  import { useNamespace as n } from "../../../hooks/src/use-namespace.mjs";
5
- import { useResizeObserver as ee } from "../../../hooks/src/use-resize-observer.mjs";
6
- import { useScrollHidden as r } from "../../../hooks/src/use-scroll-hidden.mjs";
7
- import { useZIndex as i } from "../../../hooks/src/use-zIndex.mjs";
5
+ import { useResizeObserver as r } from "../../../hooks/src/use-resize-observer.mjs";
6
+ import { useScrollHidden as ee } from "../../../hooks/src/use-scroll-hidden.mjs";
7
+ import { useZIndex as te } from "../../../hooks/src/use-zIndex.mjs";
8
8
  import "../../../hooks/index.mjs";
9
- import te from "../../../icons/src/close.vue.mjs";
10
- import ne from "../../../icons/src/fullscreen-exit.vue.mjs";
11
- import re from "../../../icons/src/fullscreen.vue.mjs";
9
+ import ne from "../../../icons/src/close.vue.mjs";
10
+ import re from "../../../icons/src/fullscreen-exit.vue.mjs";
11
+ import ie from "../../../icons/src/fullscreen.vue.mjs";
12
12
  import "../../../icons/index.mjs";
13
- import { SScrollbar as ie } from "../scrollbar/index.mjs";
14
- import { SButton as a } from "../button/index.mjs";
15
- import { STransition as o } from "../transition/index.mjs";
16
- import { VaporTeleport as s, applyVShow as c, child as l, computed as u, createComponent as d, createIf as f, createInvoker as ae, createSlot as p, createTemplateRefSetter as oe, defineVaporComponent as m, delegateEvents as h, mergeModels as g, next as se, nextTick as _, onUnmounted as ce, ref as v, renderEffect as y, setClass as b, setInsertionState as x, setStyle as S, setText as C, template as w, toDisplayString as T, unref as E, useModel as le, useTemplateRef as D, watch as O, watchEffect as ue } from "vue";
13
+ import { SScrollbar as ae } from "../scrollbar/index.mjs";
14
+ import { SButton as i } from "../button/index.mjs";
15
+ import { STransition as a } from "../transition/index.mjs";
16
+ import { VaporTeleport as oe, applyVShow as o, child as s, computed as c, createComponent as l, createIf as u, createInvoker as se, createSlot as d, createTemplateRefSetter as ce, defineVaporComponent as f, delegateEvents as p, mergeModels as m, next as h, nextTick as g, onUnmounted as le, ref as _, renderEffect as v, setClass as y, setInsertionState as b, setStyle as x, setText as S, template as C, toDisplayString as w, unref as T, useModel as E, useTemplateRef as D, watch as O, watchEffect as ue } from "vue";
17
17
  //#region ../components/src/modal/modal.vue?vue&type=script&setup=true&vapor=true&lang.ts
18
- var k = w("<div>"), A = w(" "), de = w("<div><div></div><div></div>"), j = w("<div style=\"flex: 1; overflow: hidden; display: flex; flex-direction: column\">"), fe = w("<div><div>");
19
- h("click");
20
- var M = /*@__PURE__*/ m({
18
+ var k = C("<div>"), A = C(" "), de = C("<div><div></div><div></div>"), j = C("<div style=\"flex: 1; overflow: hidden; display: flex; flex-direction: column\">"), fe = C("<div><div>");
19
+ p("click");
20
+ var M = /*@__PURE__*/ f({
21
21
  name: "SModal",
22
22
  __name: "modal",
23
- props: /*@__PURE__*/ g({
23
+ props: /*@__PURE__*/ m({
24
24
  modalValue: { type: Boolean },
25
25
  width: { default: "0px" },
26
26
  height: {},
@@ -82,33 +82,35 @@ var M = /*@__PURE__*/ m({
82
82
  },
83
83
  modelModifiers: {}
84
84
  }),
85
- emits: /*@__PURE__*/ g([
85
+ emits: /*@__PURE__*/ m([
86
86
  "close",
87
87
  "closed",
88
88
  "opened",
89
89
  "confirm"
90
90
  ], ["update:modelValue"]),
91
- setup(m, { expose: h, emit: g }) {
92
- let w = m, M = g, N = n("modal"), { lockScroll: pe, unlockScroll: P } = r(), { nextZIndex: me } = i(), F = v(0), I = D("modalRef"), L = D("headerRef"), R = le(m, "modelValue"), z = v(!1), B = v(!1), V = v(!1), H = v(!1), U = v(0), W = v(0), G = v(!1), K = v(0), q = v(0), J = u(() => typeof w.to == "string" ? document.querySelector(w.to) || document.body : w.to);
93
- ee(J, () => {
91
+ setup(f, { expose: p, emit: m }) {
92
+ let C = f, M = m, N = n("modal"), { lockScroll: pe, unlockScroll: P } = ee(), { nextZIndex: me } = te(), F = _(0), I = D("modalRef"), L = D("headerRef"), R = E(f, "modelValue"), z = _(!1), B = _(!1), V = _(!1), H = _(!1), U = _(0), W = _(0), G = _(!1), K = _(0), q = _(0), J = c(() => typeof C.to == "string" ? document.querySelector(C.to) || document.body : C.to);
93
+ r(J, () => {
94
94
  let e = J.value;
95
95
  K.value = e.clientHeight, q.value = e.clientWidth, Q();
96
+ }), r(I, () => {
97
+ z.value && Q();
96
98
  });
97
- let he = u(() => ({
99
+ let he = c(() => ({
98
100
  zIndex: F.value,
99
- position: w.to === "body" ? "fixed" : "absolute"
100
- })), ge = u(() => V.value ? {} : {
101
- width: e(w.width),
102
- height: e(w.height),
101
+ position: C.to === "body" ? "fixed" : "absolute"
102
+ })), ge = c(() => V.value ? {} : {
103
+ width: e(C.width),
104
+ height: e(C.height),
103
105
  top: `${U.value}px`,
104
106
  left: `${W.value}px`,
105
- ...w.modalStyle
107
+ ...C.modalStyle
106
108
  }), _e = () => {
107
109
  M("opened");
108
110
  }, ve = () => {
109
- B.value = !1, H.value = !1, G.value = !1, M("closed"), _(P);
111
+ B.value = !1, H.value = !1, G.value = !1, M("closed"), g(P);
110
112
  }, ye = () => {
111
- w.closeOnOverlayClick && X();
113
+ C.closeOnOverlayClick && X();
112
114
  }, be = (e) => {
113
115
  e ? R.value = !1 : H.value = !1;
114
116
  }, Y = () => {
@@ -118,117 +120,117 @@ var M = /*@__PURE__*/ m({
118
120
  }, xe = () => {
119
121
  V.value = !V.value;
120
122
  };
121
- O(() => w.fullscreen, (e) => V.value = e, { immediate: !0 });
123
+ O(() => C.fullscreen, (e) => V.value = e, { immediate: !0 });
122
124
  let Z = (e) => {
123
- e.key === "Escape" && w.closeOnEsc && z.value && X();
125
+ e.key === "Escape" && C.closeOnEsc && z.value && X();
124
126
  }, Q = async () => {
125
- if (await _(), !I.value || G.value) return;
127
+ if (await g(), !I.value || G.value) return;
126
128
  let e = I.value;
127
- w.center ? U.value = Math.max(0, (K.value - e.offsetHeight) / 2) : U.value = w.offset, W.value = Math.max(0, (q.value - e.offsetWidth) / 2);
129
+ C.center ? U.value = Math.max(0, (K.value - e.offsetHeight) / 2) : U.value = C.offset, W.value = Math.max(0, (q.value - e.offsetWidth) / 2);
128
130
  };
129
131
  O(() => R.value, async (e) => {
130
- e ? (F.value = me(), pe(), B.value = !0, await _(), z.value = !0, Q()) : z.value = !1;
132
+ e ? (F.value = me(), pe(), B.value = !0, await g(), z.value = !0, Q()) : z.value = !1;
131
133
  }, { immediate: !0 }), ue((e) => {
132
134
  z.value && (document.addEventListener("keydown", Z), e(() => document.removeEventListener("keydown", Z)));
133
135
  }), t({
134
136
  container: I,
135
137
  target: L,
136
138
  parent: J,
137
- enable: u(() => w.draggable && !V.value),
139
+ enable: c(() => C.draggable && !V.value),
138
140
  divLeft: W,
139
141
  divTop: U,
140
142
  hasDragged: G
141
- }), ce(() => {
143
+ }), le(() => {
142
144
  P();
143
- }), h({ handleClose: X });
144
- let $ = oe();
145
- return d(s, {
146
- to: () => m.to,
145
+ }), p({ handleClose: X });
146
+ let $ = ce();
147
+ return l(oe, {
148
+ to: () => f.to,
147
149
  defer: ""
148
150
  }, () => {
149
- let e = fe(), t = l(e, 1);
150
- return y(() => {
151
- b(e, [E(N).e("container")]), S(e, he.value);
152
- }), x(e, 0, 0), d(E(o), { type: "opacity" }, () => f(() => m.mask && z.value, () => {
151
+ let e = fe(), t = s(e, 1);
152
+ return v(() => {
153
+ y(e, [T(N).e("container")]), x(e, he.value);
154
+ }), b(e, 0, 0), l(T(a), { type: "opacity" }, () => u(() => f.mask, () => {
153
155
  let e = k();
154
- return e.$evtclick = ae(ye), y(() => {
155
- S(e, m.maskStyle), b(e, [E(N).e("mask")]);
156
+ return o(e, () => z.value), e.$evtclick = se(ye), v(() => {
157
+ x(e, f.maskStyle), y(e, [T(N).e("mask")]);
156
158
  }), e;
157
- }, null, 129)), y(() => b(t, [E(N).e("wrapper")])), x(t, null, 0), d(E(o), {
159
+ }, null, 129)), v(() => y(t, [T(N).e("wrapper")])), b(t, null, 0), l(T(a), {
158
160
  type: "scale",
159
161
  onAfterLeave: () => ve,
160
162
  onAfterEnter: () => _e
161
163
  }, () => {
162
164
  let e = k();
163
- return y(() => {
164
- let t = E(N);
165
- b(e, [
165
+ return v(() => {
166
+ let t = T(N);
167
+ y(e, [
166
168
  t.b(),
167
- t.is("drag", m.draggable),
169
+ t.is("drag", f.draggable),
168
170
  t.is("fullscreen", V.value),
169
- m.className
170
- ]), S(e, ge.value);
171
- }), x(e, null, 0), f(() => m.header, () => {
172
- let e = de(), t = l(e), n = se(t, 1);
173
- return y(() => {
174
- let n = E(N);
175
- b(e, [n.e("header")]), b(t, [n.e("title")]);
176
- }), x(t, null, 0), p("header", null, () => {
171
+ f.className
172
+ ]), x(e, ge.value);
173
+ }), b(e, null, 0), u(() => f.header, () => {
174
+ let e = de(), t = s(e), n = h(t, 1);
175
+ return v(() => {
176
+ let n = T(N);
177
+ y(e, [n.e("header")]), y(t, [n.e("title")]);
178
+ }), b(t, null, 0), d("header", null, () => {
177
179
  let e = A();
178
- return y(() => C(e, T(m.title))), e;
179
- }), y(() => b(n, [E(N).e("header-icon")])), x(n, null, 0), f(() => m.fullable, () => d(E(a), {
180
+ return v(() => S(e, w(f.title))), e;
181
+ }), v(() => y(n, [T(N).e("header-icon")])), b(n, null, 0), u(() => f.fullable, () => l(T(i), {
180
182
  text: "",
181
183
  size: "small",
182
184
  onClick: () => xe
183
- }, { icon: () => f(() => V.value, () => d(E(ne)), () => d(E(re)), 645) })), x(n, null, 1), f(() => m.closable, () => d(E(a), {
185
+ }, { icon: () => u(() => V.value, () => l(T(re)), () => l(T(ie)), 645) })), b(n, null, 1), u(() => f.closable, () => l(T(i), {
184
186
  text: "",
185
187
  size: "small",
186
188
  circle: "",
187
189
  onClick: () => X
188
- }, { icon: () => d(E(te)) })), $(e, L, null, "headerRef"), e;
189
- }), x(e, null, 1), f(() => !m.scrollContent, () => {
190
+ }, { icon: () => l(T(ne)) })), $(e, L, null, "headerRef"), e;
191
+ }), b(e, null, 1), u(() => !f.scrollContent, () => {
190
192
  let e = k();
191
- return y(() => {
192
- b(e, [E(N).e("body")]), S(e, {
193
+ return v(() => {
194
+ y(e, [T(N).e("body")]), x(e, {
193
195
  padding: "16px",
194
- ...m.bodyStyle
196
+ ...f.bodyStyle
195
197
  });
196
- }), x(e, null, 0), p("default", null, () => {
198
+ }), b(e, null, 0), d("default", null, () => {
197
199
  let e = A();
198
- return y(() => C(e, T(m.content))), e;
200
+ return v(() => S(e, w(f.content))), e;
199
201
  }), e;
200
202
  }, () => {
201
203
  let e = j();
202
- return x(e, null, 0), d(E(ie), { $: [() => ({
204
+ return b(e, null, 0), l(T(ae), { $: [() => ({
203
205
  padding: "16px",
204
- ...m.scrollbarProps
205
- })] }, () => p("default", null, () => {
206
+ ...f.scrollbarProps
207
+ })] }, () => d("default", null, () => {
206
208
  let e = A();
207
- return y(() => C(e, T(m.content))), e;
209
+ return v(() => S(e, w(f.content))), e;
208
210
  }, 4)), e;
209
- }, 1541), x(e, null, 2), f(() => m.footer, () => {
211
+ }, 1541), b(e, null, 2), u(() => f.footer, () => {
210
212
  let e = k();
211
- return y(() => b(e, [E(N).e("footer")])), x(e, null, 0), p("footer", null, () => f(() => m.btnReverse, () => [d(E(a), {
213
+ return v(() => y(e, [T(N).e("footer")])), b(e, null, 0), d("footer", null, () => u(() => f.btnReverse, () => [l(T(i), {
212
214
  label: "确定",
213
215
  loading: () => H.value,
214
216
  type: "primary",
215
217
  onClick: () => Y
216
- }), d(E(a), {
218
+ }), l(T(i), {
217
219
  label: "取消",
218
220
  secondary: "",
219
221
  onClick: () => X
220
- })], () => [d(E(a), {
222
+ })], () => [l(T(i), {
221
223
  label: "取消",
222
224
  secondary: "",
223
225
  onClick: () => X
224
- }), d(E(a), {
226
+ }), l(T(i), {
225
227
  label: "确定",
226
228
  loading: () => H.value,
227
229
  type: "primary",
228
230
  onClick: () => Y
229
231
  })], 1930)), e;
230
- }), c(e, () => z.value), $(e, I, null, "modalRef"), e;
231
- }), c(e, () => B.value), e;
232
+ }), o(e, () => z.value), $(e, I, null, "modalRef"), e;
233
+ }), o(e, () => B.value), e;
232
234
  }, !0);
233
235
  }
234
236
  });
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json",
3
3
  "framework": "vue",
4
4
  "name": "savor-ui",
5
- "version": "0.11.0",
5
+ "version": "0.12.0",
6
6
  "contributions": {
7
7
  "html": {
8
8
  "types-syntax": "typescript",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "savor-ui",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "A Vue3 Components Library",
5
5
  "keywords": [
6
6
  "component library",