@hlw-uni/mp-vue 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
+ import { Ref } from 'vue';
1
2
  export declare const THEME_CHANGE_EVENT = "hlw:theme-change";
2
3
  export declare function buildThemeStyle(): string;
3
4
  export declare function useThemePageStyle(): {
4
- themePageStyle: any;
5
+ themePageStyle: Ref<string, string>;
5
6
  };
6
7
  export type { FontScale, FontPreset } from './font';
7
8
  export { FONT_SCALE_KEY, FONT_PRESETS, getCurrentFontScale, getCurrentFontVars } from './font';
package/dist/index.d.ts CHANGED
@@ -7,5 +7,6 @@
7
7
  * compiled SFC runtime helpers from the library entry.
8
8
  */
9
9
  export type { FontScale, FontPreset, ThemeColor } from './composables/theme';
10
+ export type { HlwMenuItem } from './components/hlw-menu/types';
10
11
  export { FONT_PRESETS, FONT_SCALE_KEY, DEFAULT_THEMES, THEME_COLOR_KEY, THEME_CHANGE_EVENT, getCurrentFontScale, getCurrentFontVars, getCurrentThemeColor, getCurrentThemeVars, buildThemeStyle, useThemePageStyle, } from './composables/theme';
11
12
  export { useThemeStore } from './stores/theme';
@@ -1 +1,38 @@
1
- export declare const useThemeStore: any;
1
+ import { FontScale, ThemeColor } from '../composables/theme';
2
+ import { StoreDefinition } from 'pinia';
3
+ import { Ref, ComputedRef } from 'vue';
4
+
5
+ export declare const useThemeStore: StoreDefinition<"theme", Pick<{
6
+ scale: Ref<FontScale, FontScale>;
7
+ fontOptions: {
8
+ value: FontScale;
9
+ label: string;
10
+ }[];
11
+ setScale: (s: FontScale) => void;
12
+ primaryColor: Ref<string, string>;
13
+ themes: ThemeColor[];
14
+ activeTheme: ComputedRef<ThemeColor>;
15
+ setTheme: (color: string) => void;
16
+ }, "scale" | "fontOptions" | "primaryColor" | "themes">, Pick<{
17
+ scale: Ref<FontScale, FontScale>;
18
+ fontOptions: {
19
+ value: FontScale;
20
+ label: string;
21
+ }[];
22
+ setScale: (s: FontScale) => void;
23
+ primaryColor: Ref<string, string>;
24
+ themes: ThemeColor[];
25
+ activeTheme: ComputedRef<ThemeColor>;
26
+ setTheme: (color: string) => void;
27
+ }, "activeTheme">, Pick<{
28
+ scale: Ref<FontScale, FontScale>;
29
+ fontOptions: {
30
+ value: FontScale;
31
+ label: string;
32
+ }[];
33
+ setScale: (s: FontScale) => void;
34
+ primaryColor: Ref<string, string>;
35
+ themes: ThemeColor[];
36
+ activeTheme: ComputedRef<ThemeColor>;
37
+ setTheme: (color: string) => void;
38
+ }, "setScale" | "setTheme">>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlw-uni/mp-vue",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "hlw-uni Vue 组件库 — 供小程序业务方使用的 UI 组件集合",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -2,48 +2,52 @@
2
2
  <button
3
3
  class="hlw-btn"
4
4
  :class="[
5
- `hlw-btn--${type}`,
6
- `hlw-btn--${size}`,
7
- { 'hlw-btn--block': block, 'hlw-btn--round': round, 'hlw-btn--disabled': disabled, 'hlw-btn--loading': loading },
5
+ `hlw-btn--${props.type}`,
6
+ `hlw-btn--${props.size}`,
7
+ {
8
+ 'hlw-btn--block': props.block,
9
+ 'hlw-btn--round': props.round,
10
+ 'hlw-btn--disabled': props.disabled,
11
+ 'hlw-btn--loading': props.loading,
12
+ },
8
13
  ]"
9
- :disabled="disabled || loading"
10
- :open-type="openType"
11
- @tap="$emit('click')"
14
+ :style="buttonStyle"
15
+ :disabled="props.disabled || props.loading"
16
+ :open-type="props.openType"
17
+ @tap="handleTap"
12
18
  >
13
- <view v-if="loading" class="hlw-btn-spinner" />
14
- <view v-else-if="icon" :class="icon" class="hlw-btn-icon" />
15
- <slot />
19
+ <view v-if="props.loading" class="hlw-btn-spinner" />
20
+ <template v-else>
21
+ <view v-if="hasIcon" class="hlw-btn-icon-wrap">
22
+ <slot name="icon">
23
+ <view v-if="props.icon" :class="props.icon" class="hlw-btn-icon" />
24
+ </slot>
25
+ </view>
26
+ <view class="hlw-btn-content">
27
+ <slot />
28
+ </view>
29
+ </template>
16
30
  </button>
17
31
  </template>
18
32
 
19
33
  <script setup lang="ts">
20
- /**
21
- * HlwButton — 主题按钮
22
- *
23
- * 跟随 --primary-color 主题色,支持多种类型、尺寸和状态。
24
- *
25
- * @props
26
- * type - 按钮类型:primary / outline / text / ghost,默认 primary
27
- * size - 尺寸:small / medium / large,默认 medium
28
- * loading - 加载状态(显示 spinner 并禁止点击),默认 false
29
- * disabled - 禁用状态,默认 false
30
- * block - 块级按钮(占满父容器宽度),默认 false
31
- * round - 圆角药丸形状,默认 false
32
- * icon - 左侧图标 class(如 i-fa6-solid-plus)
33
- * openType - 微信原生 open-type(如 share、getPhoneNumber)
34
- *
35
- * @events
36
- * click - 点击事件
37
- *
38
- * @example
39
- * ```vue
40
- * <HlwButton type="primary" @click="submit">提交</HlwButton>
41
- * <HlwButton type="outline" loading>加载中</HlwButton>
42
- * <HlwButton type="text" icon="i-fa6-solid-plus">新增</HlwButton>
43
- * ```
44
- */
34
+ import { computed, useSlots } from "vue";
35
+
36
+ type ButtonType =
37
+ | "primary"
38
+ | "success"
39
+ | "warning"
40
+ | "danger"
41
+ | "error"
42
+ | "info"
43
+ | "outline"
44
+ | "text"
45
+ | "ghost";
46
+
47
+ type NavigateType = "navigateTo" | "redirectTo" | "switchTab" | "reLaunch" | "navigateBack";
48
+
45
49
  interface Props {
46
- type?: "primary" | "outline" | "text" | "ghost";
50
+ type?: ButtonType;
47
51
  size?: "small" | "medium" | "large";
48
52
  loading?: boolean;
49
53
  disabled?: boolean;
@@ -51,9 +55,15 @@ interface Props {
51
55
  round?: boolean;
52
56
  icon?: string;
53
57
  openType?: string;
58
+ bgColor?: string;
59
+ textColor?: string;
60
+ borderColor?: string;
61
+ url?: string;
62
+ navigateType?: NavigateType;
63
+ delta?: number;
54
64
  }
55
65
 
56
- withDefaults(defineProps<Props>(), {
66
+ const props = withDefaults(defineProps<Props>(), {
57
67
  type: "primary",
58
68
  size: "medium",
59
69
  loading: false,
@@ -62,38 +72,107 @@ withDefaults(defineProps<Props>(), {
62
72
  round: false,
63
73
  icon: "",
64
74
  openType: "",
75
+ bgColor: "",
76
+ textColor: "",
77
+ borderColor: "",
78
+ url: "",
79
+ navigateType: "navigateTo",
80
+ delta: 1,
81
+ });
82
+
83
+ const emit = defineEmits<{ click: [] }>();
84
+
85
+ const slots = useSlots();
86
+ const hasIcon = computed(() => Boolean(props.icon || slots.icon));
87
+
88
+ const buttonStyle = computed(() => {
89
+ const style: Record<string, string> = {};
90
+
91
+ if (props.bgColor) style.background = props.bgColor;
92
+ if (props.textColor) style.color = props.textColor;
93
+ if (props.borderColor) style.borderColor = props.borderColor;
94
+
95
+ return style;
65
96
  });
66
97
 
67
- defineEmits<{ click: [] }>();
98
+ const handleTap = () => {
99
+ if (props.disabled || props.loading) return;
100
+
101
+ emit("click");
102
+
103
+ if (!props.url && props.navigateType !== "navigateBack") return;
104
+
105
+ switch (props.navigateType) {
106
+ case "redirectTo":
107
+ if (props.url) uni.redirectTo({ url: props.url });
108
+ break;
109
+ case "switchTab":
110
+ if (props.url) uni.switchTab({ url: props.url });
111
+ break;
112
+ case "reLaunch":
113
+ if (props.url) uni.reLaunch({ url: props.url });
114
+ break;
115
+ case "navigateBack":
116
+ uni.navigateBack({ delta: props.delta });
117
+ break;
118
+ default:
119
+ if (props.url) uni.navigateTo({ url: props.url });
120
+ break;
121
+ }
122
+ };
68
123
  </script>
69
124
 
70
125
  <style lang="scss" scoped>
126
+ $semantic-colors: (
127
+ primary: var(--primary-color),
128
+ success: #10b981,
129
+ warning: #f59e0b,
130
+ danger: #ef4444,
131
+ error: #ef4444,
132
+ info: #64748b,
133
+ );
134
+
71
135
  .hlw-btn {
72
136
  display: inline-flex;
73
137
  align-items: center;
74
138
  justify-content: center;
75
139
  gap: 8rpx;
140
+ flex-shrink: 0;
76
141
  border: none;
77
142
  font-weight: 500;
78
143
  line-height: 1;
79
144
  transition: opacity 0.2s;
145
+ box-sizing: border-box;
146
+ background: transparent;
147
+ white-space: nowrap;
80
148
 
81
- &::after { display: none; }
82
- &:active { opacity: 0.7; }
149
+ &::after {
150
+ display: none;
151
+ }
83
152
 
84
- &--primary {
85
- background: var(--primary-color);
86
- color: #fff;
153
+ &:active {
154
+ opacity: 0.7;
155
+ }
156
+
157
+ @each $name, $color in $semantic-colors {
158
+ &--#{$name} {
159
+ background: #{$color};
160
+ color: #fff;
161
+ border: none;
162
+ }
87
163
  }
164
+
88
165
  &--outline {
89
166
  background: transparent;
90
167
  color: var(--primary-color);
91
168
  border: 2rpx solid var(--primary-color);
92
169
  }
170
+
93
171
  &--text {
94
172
  background: transparent;
95
173
  color: var(--primary-color);
96
174
  }
175
+
97
176
  &--ghost {
98
177
  background: transparent;
99
178
  color: #fff;
@@ -101,25 +180,54 @@ defineEmits<{ click: [] }>();
101
180
  }
102
181
 
103
182
  &--small {
104
- padding: 8rpx 20rpx;
183
+ min-height: 56rpx;
184
+ padding: 12rpx 20rpx;
105
185
  font-size: var(--font-xs, 20rpx);
106
186
  border-radius: var(--radius-sm, 8rpx);
107
187
  }
188
+
108
189
  &--medium {
109
- padding: 16rpx 32rpx;
190
+ min-height: 72rpx;
191
+ padding: 18rpx 32rpx;
110
192
  font-size: var(--font-sm, 24rpx);
111
193
  border-radius: var(--radius-md, 16rpx);
112
194
  }
195
+
113
196
  &--large {
114
- padding: 24rpx 48rpx;
197
+ min-height: 88rpx;
198
+ padding: 22rpx 48rpx;
115
199
  font-size: var(--font-base, 28rpx);
116
200
  border-radius: var(--radius-md, 16rpx);
117
201
  }
118
202
 
119
- &--block { display: flex; width: 100%; }
120
- &--round { border-radius: 999rpx; }
121
- &--disabled { opacity: 0.4; &:active { opacity: 0.4; } }
122
- &--loading { opacity: 0.7; }
203
+ &--block {
204
+ display: flex;
205
+ width: 100%;
206
+ }
207
+
208
+ &--round {
209
+ border-radius: 999rpx;
210
+ }
211
+
212
+ &--disabled {
213
+ opacity: 0.4;
214
+
215
+ &:active {
216
+ opacity: 0.4;
217
+ }
218
+ }
219
+
220
+ &--loading {
221
+ opacity: 0.7;
222
+ }
223
+ }
224
+
225
+ .hlw-btn-icon-wrap,
226
+ .hlw-btn-content {
227
+ display: inline-flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ white-space: nowrap;
123
231
  }
124
232
 
125
233
  .hlw-btn-icon {
@@ -136,6 +244,8 @@ defineEmits<{ click: [] }>();
136
244
  }
137
245
 
138
246
  @keyframes hlw-spin {
139
- to { transform: rotate(360deg); }
247
+ to {
248
+ transform: rotate(360deg);
249
+ }
140
250
  }
141
251
  </style>
@@ -152,6 +152,10 @@ const handleClick = (item: HlwMenuItem) => {
152
152
 
153
153
  <style lang="scss" scoped>
154
154
  .hlw-menu {
155
+ --hlw-menu-icon-size: var(--font-sm, 24rpx);
156
+ --hlw-menu-icon-box-size: calc(var(--hlw-menu-icon-size) + 40rpx);
157
+ --hlw-menu-grid-icon-size: var(--font-lg, 36rpx);
158
+ --hlw-menu-grid-icon-box-size: calc(var(--hlw-menu-grid-icon-size) + 52rpx);
155
159
  background: #fff;
156
160
  border-radius: var(--radius-lg, 24rpx);
157
161
  border: 1rpx solid var(--border-color, #e2e8f0);
@@ -254,8 +258,8 @@ const handleClick = (item: HlwMenuItem) => {
254
258
 
255
259
  /* ========== 图标 ========== */
256
260
  .hlw-menu-icon {
257
- width: 64rpx;
258
- height: 64rpx;
261
+ width: var(--hlw-menu-icon-box-size);
262
+ height: var(--hlw-menu-icon-box-size);
259
263
  border-radius: var(--radius-md, 16rpx);
260
264
  display: flex;
261
265
  align-items: center;
@@ -263,15 +267,15 @@ const handleClick = (item: HlwMenuItem) => {
263
267
  flex-shrink: 0;
264
268
 
265
269
  text {
266
- font-size: 20rpx;
270
+ font-size: var(--hlw-menu-icon-size);
267
271
  }
268
272
 
269
273
  &--grid {
270
- width: 88rpx;
271
- height: 88rpx;
274
+ width: var(--hlw-menu-grid-icon-box-size);
275
+ height: var(--hlw-menu-grid-icon-box-size);
272
276
  border-radius: var(--radius-lg, 24rpx);
273
277
  text {
274
- font-size: 32rpx;
278
+ font-size: var(--hlw-menu-grid-icon-size);
275
279
  }
276
280
  }
277
281
 
@@ -365,14 +369,14 @@ const handleClick = (item: HlwMenuItem) => {
365
369
 
366
370
  .hlw-menu-arrow {
367
371
  color: #d1d5db;
368
- font-size: var(--font-xs, 20rpx);
372
+ font-size: var(--font-sm, 24rpx);
369
373
  }
370
374
  .hlw-menu-spin {
371
375
  animation: icon-spin 1s linear infinite;
372
376
  }
373
377
  .hlw-menu-muted {
374
378
  color: #94a3b8;
375
- font-size: var(--font-xs, 20rpx);
379
+ font-size: var(--font-sm, 24rpx);
376
380
  }
377
381
 
378
382
  @keyframes icon-spin {
@@ -0,0 +1,8 @@
1
+ import "pinia";
2
+ import type { StateTree } from "pinia";
3
+
4
+ declare module "pinia" {
5
+ interface DefineStoreOptionsBase<S extends StateTree, Store> {
6
+ unistorage?: boolean;
7
+ }
8
+ }
@@ -38,7 +38,7 @@ export const useThemeStore = defineStore(
38
38
 
39
39
  /** 当前激活的主题信息 */
40
40
  const activeTheme = computed<ThemeColor>(() =>
41
- themes.find((t) => t.value === primaryColor.value)
41
+ themes.find((t: ThemeColor) => t.value === primaryColor.value)
42
42
  ?? { label: "自定义", value: primaryColor.value },
43
43
  );
44
44