@hlw-uni/mp-vue 2.3.3 → 2.3.6

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.
@@ -0,0 +1,10 @@
1
+ export interface HlwRewardAdResult {
2
+ /** 是否播放成功 */
3
+ success: boolean;
4
+ /** 广告是否完整播放结束 */
5
+ isEnded: boolean;
6
+ /** 广告加载是否失败 */
7
+ loadFailed?: boolean;
8
+ /** 加载/播放失败时的错误对象 */
9
+ err?: any;
10
+ }
@@ -1,9 +1,8 @@
1
1
  import { ComputedRef } from 'vue';
2
2
  import { Store } from 'pinia';
3
3
 
4
- export { themePresets, type ThemePreset, fontSizePresets, type FontSizePreset, fontFamilyPresets, type FontFamilyPreset } from '../../stores/theme';
4
+ export { fontSizePresets, type FontSizePreset, fontFamilyPresets, type FontFamilyPreset } from '../../stores/theme';
5
5
  export declare function useTheme(): {
6
- theme: ComputedRef<string>;
7
6
  fontSize: ComputedRef<string>;
8
7
  fontSizeClass: ComputedRef<string>;
9
8
  setFontSize: (size: string) => void;
@@ -11,7 +10,6 @@ export declare function useTheme(): {
11
10
  fontFamilyClass: ComputedRef<string>;
12
11
  setFontFamily: (font: string) => void;
13
12
  store: Store<"theme", {
14
- theme: string;
15
13
  fontSize: string;
16
14
  fontFamily: string;
17
15
  }, {}, {
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from './core';
11
11
  export * from './utils';
12
12
  export type { HlwMenuItem } from './components/hlw-menu/types';
13
13
  export type { HlwPagingRef, HlwPagingInstance } from './components/hlw-paging/types';
14
+ export type { HlwRewardAdResult } from './components/hlw-reward-ad/types';
14
15
  export { useApp } from './app';
15
16
  export { hlw, type HlwInstance } from './hlw';
16
17
  export { vCopy } from './directives';
package/dist/index.js CHANGED
@@ -8,28 +8,6 @@ var __publicField = (obj, key, value) => {
8
8
  return value;
9
9
  };
10
10
 
11
- const themePresets = [
12
- {
13
- id: "white-theme",
14
- name: "白色主题",
15
- color: "#ffffff"
16
- },
17
- {
18
- id: "light-theme",
19
- name: "简洁主题",
20
- color: "var(--bg-page, #f8f8f8)"
21
- },
22
- {
23
- id: "mono-theme",
24
- name: "单色主题",
25
- color: "var(--primary-color, #3b82f6)"
26
- },
27
- {
28
- id: "color-theme",
29
- name: "颜色主题",
30
- color: "var(--primary-color, #3b82f6)"
31
- }
32
- ];
33
11
  const fontSizePresets = [
34
12
  {
35
13
  id: "small",
@@ -76,7 +54,6 @@ var __publicField = (obj, key, value) => {
76
54
  ];
77
55
  const useThemeStore = pinia.defineStore("theme", {
78
56
  state: () => ({
79
- theme: "white-theme",
80
57
  fontSize: "standard",
81
58
  fontFamily: "system"
82
59
  }),
@@ -97,7 +74,6 @@ var __publicField = (obj, key, value) => {
97
74
  });
98
75
  function useTheme() {
99
76
  const store = useThemeStore();
100
- const theme = vue.computed(() => store.theme);
101
77
  const fontSize = vue.computed(() => store.fontSize);
102
78
  const fontSizeClass = vue.computed(() => {
103
79
  const found = fontSizePresets.find((p) => p.id === store.fontSize);
@@ -115,7 +91,6 @@ var __publicField = (obj, key, value) => {
115
91
  store.setFontFamily(font);
116
92
  }
117
93
  return {
118
- theme,
119
94
  fontSize,
120
95
  fontSizeClass,
121
96
  setFontSize,
@@ -125,15 +100,7 @@ var __publicField = (obj, key, value) => {
125
100
  store
126
101
  };
127
102
  }
128
- function initTheme(defaultTheme = "mono-theme") {
129
- const hasCachedTheme = uni.getStorageSync("theme");
130
- if (!hasCachedTheme) {
131
- const store = useThemeStore();
132
- const validThemes = themePresets.map((t) => t.id);
133
- if (validThemes.includes(defaultTheme)) {
134
- store.theme = defaultTheme;
135
- }
136
- }
103
+ function initTheme(defaultTheme) {
137
104
  }
138
105
  function useMsg() {
139
106
  function toast(opts) {
@@ -1202,7 +1169,6 @@ var __publicField = (obj, key, value) => {
1202
1169
  exports2.showRewardAd = showRewardAd;
1203
1170
  exports2.signText = signText;
1204
1171
  exports2.switchTab = switchTab;
1205
- exports2.themePresets = themePresets;
1206
1172
  exports2.toBoolean = toBoolean;
1207
1173
  exports2.toNumber = toNumber;
1208
1174
  exports2.toQuery = toQuery;
package/dist/index.mjs CHANGED
@@ -7,28 +7,6 @@ var __publicField = (obj, key, value) => {
7
7
  import { computed, ref, onBeforeUpdate, onUnmounted, createSSRApp } from "vue";
8
8
  import { defineStore } from "pinia";
9
9
  import { onShareAppMessage, onShareTimeline } from "@dcloudio/uni-app";
10
- const themePresets = [
11
- {
12
- id: "white-theme",
13
- name: "白色主题",
14
- color: "#ffffff"
15
- },
16
- {
17
- id: "light-theme",
18
- name: "简洁主题",
19
- color: "var(--bg-page, #f8f8f8)"
20
- },
21
- {
22
- id: "mono-theme",
23
- name: "单色主题",
24
- color: "var(--primary-color, #3b82f6)"
25
- },
26
- {
27
- id: "color-theme",
28
- name: "颜色主题",
29
- color: "var(--primary-color, #3b82f6)"
30
- }
31
- ];
32
10
  const fontSizePresets = [
33
11
  {
34
12
  id: "small",
@@ -75,7 +53,6 @@ const fontFamilyPresets = [
75
53
  ];
76
54
  const useThemeStore = defineStore("theme", {
77
55
  state: () => ({
78
- theme: "white-theme",
79
56
  fontSize: "standard",
80
57
  fontFamily: "system"
81
58
  }),
@@ -96,7 +73,6 @@ const useThemeStore = defineStore("theme", {
96
73
  });
97
74
  function useTheme() {
98
75
  const store = useThemeStore();
99
- const theme = computed(() => store.theme);
100
76
  const fontSize = computed(() => store.fontSize);
101
77
  const fontSizeClass = computed(() => {
102
78
  const found = fontSizePresets.find((p) => p.id === store.fontSize);
@@ -114,7 +90,6 @@ function useTheme() {
114
90
  store.setFontFamily(font);
115
91
  }
116
92
  return {
117
- theme,
118
93
  fontSize,
119
94
  fontSizeClass,
120
95
  setFontSize,
@@ -124,15 +99,7 @@ function useTheme() {
124
99
  store
125
100
  };
126
101
  }
127
- function initTheme(defaultTheme = "mono-theme") {
128
- const hasCachedTheme = uni.getStorageSync("theme");
129
- if (!hasCachedTheme) {
130
- const store = useThemeStore();
131
- const validThemes = themePresets.map((t) => t.id);
132
- if (validThemes.includes(defaultTheme)) {
133
- store.theme = defaultTheme;
134
- }
135
- }
102
+ function initTheme(defaultTheme) {
136
103
  }
137
104
  function useMsg() {
138
105
  function toast(opts) {
@@ -1202,7 +1169,6 @@ export {
1202
1169
  showRewardAd,
1203
1170
  signText,
1204
1171
  switchTab,
1205
- themePresets,
1206
1172
  toBoolean,
1207
1173
  toNumber,
1208
1174
  toQuery,
@@ -1,10 +1,4 @@
1
1
  import { StoreDefinition } from 'pinia';
2
- export interface ThemePreset {
3
- id: string;
4
- name: string;
5
- color: string;
6
- }
7
- export declare const themePresets: ThemePreset[];
8
2
  export interface FontSizePreset {
9
3
  id: string;
10
4
  name: string;
@@ -18,7 +12,6 @@ export interface FontFamilyPreset {
18
12
  }
19
13
  export declare const fontFamilyPresets: FontFamilyPreset[];
20
14
  export declare const useThemeStore: StoreDefinition<"theme", {
21
- theme: string;
22
15
  fontSize: string;
23
16
  fontFamily: string;
24
17
  }, {}, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlw-uni/mp-vue",
3
- "version": "2.3.3",
3
+ "version": "2.3.6",
4
4
  "description": "hlw-uni工具集",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -86,8 +86,8 @@ const top = computed(() => {
86
86
  try {
87
87
  const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
88
88
  if (menuButtonInfo && menuButtonInfo.bottom > 0) {
89
- // 胶囊底部高度 + 6px 作为气泡定位的顶部基准,使箭头离胶囊更近
90
- return `${menuButtonInfo.bottom + 2}px`;
89
+ // 胶囊底部高度 + 12px 作为气泡定位的顶部基准,避免箭头与胶囊重叠
90
+ return `${menuButtonInfo.bottom + 12}px`;
91
91
  }
92
92
  } catch (e) {
93
93
  // 跨端环境不支持或报错时,执行安全降级计算
@@ -1,10 +1,7 @@
1
1
  <template>
2
2
  <view class="hlw-avatar-upload">
3
3
  <button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar" @click="onClick">
4
- <hlw-avatar :src="props.src || ''" :name="props.name || '微信用户'" :size="props.size" :border="props.border" />
5
- <view class="edit-avatar">
6
- <span class="iconfont icon-xiangji edit-avatar-icon" />
7
- </view>
4
+ <slot />
8
5
  </button>
9
6
  </view>
10
7
  </template>
@@ -12,24 +9,6 @@
12
9
  <script setup lang="ts">
13
10
  defineOptions({ name: "HlwAvatarUpload" });
14
11
 
15
- interface Props {
16
- /** 当前头像的网络/本地路径 */
17
- src?: string;
18
- /** 无头像时的备用展示昵称 */
19
- name?: string;
20
- /** 头像尺寸,可选 'small' | 'medium' | 'large' */
21
- size?: "small" | "medium" | "large";
22
- /** 边框线宽 */
23
- border?: number;
24
- }
25
-
26
- const props = withDefaults(defineProps<Props>(), {
27
- src: "",
28
- name: "微信用户",
29
- size: "large",
30
- border: 3,
31
- });
32
-
33
12
  const emit = defineEmits<{
34
13
  (e: "onAvatar", filePath: string): void;
35
14
  }>();
@@ -58,13 +37,15 @@ function onClick() {
58
37
 
59
38
  <style scoped>
60
39
  .hlw-avatar-upload {
61
- display: inline-block;
40
+ display: block;
41
+ width: 100%;
42
+ height: 100%;
62
43
  }
63
44
 
64
45
  .avatar-wrapper {
65
- position: relative;
66
- display: inline-block;
67
- border-radius: 50%;
46
+ display: block;
47
+ width: 100%;
48
+ height: 100%;
68
49
  margin: 0;
69
50
  padding: 0;
70
51
  background: transparent;
@@ -78,26 +59,4 @@ function onClick() {
78
59
  border: none;
79
60
  display: none;
80
61
  }
81
-
82
- .edit-avatar {
83
- position: absolute;
84
- bottom: -8rpx;
85
- right: -8rpx;
86
- display: flex;
87
- align-items: center;
88
- justify-content: center;
89
- width: 44rpx;
90
- height: 44rpx;
91
- margin: 0;
92
- padding: 0;
93
- border: 1rpx solid #e2e8f0; /* 圆角灰线 */
94
- border-radius: 999rpx;
95
- background: #ffffff; /* 白色背景 */
96
- color: #64748b; /* 灰色相机图标 */
97
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
98
- }
99
-
100
- .edit-avatar .edit-avatar-icon {
101
- font-size: 22rpx;
102
- }
103
62
  </style>
@@ -1,14 +1,11 @@
1
1
  <template>
2
- <view class="navbar" :class="theme">
2
+ <view class="navbar" :class="[props.border ? '' : 'no-border']">
3
3
  <view :style="bar_style"></view>
4
- <view class="header" :style="{ height: header_height + 'px' }">
4
+ <view class="header" :style="{ height: header_height + 'px' }" :class="['align-' + props.titleAlign, props.isBack ? 'has-back' : '']">
5
5
  <view @tap="tapBack" class="left" v-if="props.isBack">
6
6
  <span class="i-fa6-solid-chevron-left icon-left"></span>
7
7
  </view>
8
- <text class="title">{{ title }}</text>
9
- </view>
10
- <view class="status-bar" v-if="theme === 'color-theme'">
11
- <view class="within"></view>
8
+ <text class="title" :style="titleCustomStyle">{{ title }}</text>
12
9
  </view>
13
10
  </view>
14
11
  <view :style="{ height: navbar_height + 'px' }"></view>
@@ -21,9 +18,14 @@
21
18
  * 自适应状态栏高度与胶囊按钮,完美替代微信小程序原生导航栏。支持不同主题配色、返回按钮及自适应高度。
22
19
  *
23
20
  * @props
24
- * title - 导航栏标题文字
25
- * isBack - 是否显示返回按钮,默认 false;点击自动回退或回到首页
26
- * isBar - 是否占用状态栏高度,默认 true
21
+ * title - 导航栏标题文字
22
+ * isBack - 是否显示返回按钮,默认 false;点击自动回退或回到首页
23
+ * isBar - 是否占用状态栏高度,默认 true
24
+ * titleAlign - 标题对齐方式,'left' | 'center'
25
+ * titleSize - 标题字体大小,支持 CSS 单位或变量
26
+ * titleStyle - 标题字体,如 'sans-serif'
27
+ * titleWeight - 标题字重,如 '500' | 'bold'
28
+ * border - 是否显示下边框(下划线),默认 true
27
29
  *
28
30
  * @example
29
31
  * ```vue
@@ -31,9 +33,6 @@
31
33
  * ```
32
34
  */
33
35
  import { computed, ref } from "vue";
34
- import { useTheme } from "@/core";
35
-
36
- const { theme } = useTheme();
37
36
 
38
37
  const statusBarHeight: number = uni.getSystemInfoSync()?.statusBarHeight || 0;
39
38
  const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
@@ -51,6 +50,40 @@ const props = defineProps({
51
50
  type: Boolean,
52
51
  default: false,
53
52
  },
53
+ titleAlign: {
54
+ type: String,
55
+ default: "center", // 'left' | 'center'
56
+ },
57
+ titleSize: {
58
+ type: String,
59
+ default: "",
60
+ },
61
+ titleStyle: {
62
+ type: String,
63
+ default: "",
64
+ },
65
+ titleWeight: {
66
+ type: String,
67
+ default: "500",
68
+ },
69
+ border: {
70
+ type: Boolean,
71
+ default: true,
72
+ },
73
+ });
74
+
75
+ const titleCustomStyle = computed(() => {
76
+ const style: Record<string, string> = {};
77
+ if (props.titleSize) {
78
+ style["font-size"] = props.titleSize;
79
+ }
80
+ if (props.titleStyle) {
81
+ style["font-family"] = props.titleStyle;
82
+ }
83
+ if (props.titleWeight) {
84
+ style["font-weight"] = props.titleWeight;
85
+ }
86
+ return style;
54
87
  });
55
88
 
56
89
  const bar_style = computed(() => {
@@ -59,14 +92,9 @@ const bar_style = computed(() => {
59
92
  };
60
93
  return style;
61
94
  });
62
- let status_bar_height = 0;
63
- if (props.isBar) {
64
- status_bar_height = 15;
65
- }
66
95
 
67
96
  const header_height = ref<number>(menuButtonInfo.bottom - statusBarHeight + 6);
68
97
  const navbar_height = ref(header_height.value + statusBarHeight);
69
- const status_bar_height_style = `${status_bar_height}px`;
70
98
 
71
99
  function tapBack() {
72
100
  uni.navigateBack({
@@ -86,65 +114,14 @@ function tapBack() {
86
114
  position: fixed;
87
115
  width: 750rpx;
88
116
  z-index: 999;
89
- border-bottom: 1rpx solid rgba(226, 232, 240, 0);
117
+ background-color: var(--navbar-bg-color, #ffffff);
118
+ border-bottom: var(--navbar-border-bottom, none);
90
119
  transition:
91
120
  background-color 0.2s ease,
92
121
  border-bottom 0.2s ease;
93
122
 
94
- /* 白色主题:白色导航栏,下方加一条灰色的细边框 */
95
- &.white-theme {
96
- background-color: var(--navbar-bg-color, #ffffff);
97
- border-bottom: var(--navbar-border-bottom, 1rpx solid #e7e7e7);
98
-
99
- .title {
100
- color: var(--text-primary, #303048);
101
- }
102
-
103
- .icon-left {
104
- color: var(--text-primary, #303048);
105
- }
106
- }
107
-
108
- /* 简洁主题:背景色与页面全局背景色一致,无明显界限,无边框 */
109
- &.light-theme {
110
- background-color: var(--bg-page, #f8f8f8);
111
- border-bottom: 1rpx solid rgba(226, 232, 240, 0);
112
-
113
- .title {
114
- color: var(--text-primary, #303048);
115
- }
116
-
117
- .icon-left {
118
- color: var(--text-primary, #303048);
119
- }
120
- }
121
-
122
- /* 单色主题:纯主题色导航栏,无边框,无圆角 */
123
- &.mono-theme {
124
- background-color: var(--primary-color, #3b82f6);
125
- border-bottom: 1rpx solid rgba(226, 232, 240, 0);
126
-
127
- .title {
128
- color: #ffffff;
129
- }
130
-
131
- .icon-left {
132
- color: #ffffff;
133
- }
134
- }
135
-
136
- /* 颜色主题:导航栏使用立体光影感的主题色渐变背景(反向:左上偏深,右下偏亮),下方带有白色圆角过渡 */
137
- &.color-theme {
138
- background: linear-gradient(135deg, rgba(0, 0, 0, 0.15) 0%, rgba(255, 255, 255, 0.15) 100%), var(--primary-color, #3b82f6);
139
- border-bottom: 1rpx solid rgba(226, 232, 240, 0);
140
-
141
- .title {
142
- color: #ffffff;
143
- }
144
-
145
- .icon-left {
146
- color: #ffffff;
147
- }
123
+ &.no-border {
124
+ border-bottom: none !important;
148
125
  }
149
126
 
150
127
  .header {
@@ -153,6 +130,20 @@ function tapBack() {
153
130
  align-items: center;
154
131
  justify-content: center;
155
132
  position: relative;
133
+ width: 100%;
134
+
135
+ &.align-left {
136
+ justify-content: flex-start;
137
+ padding-left: 32rpx;
138
+
139
+ &.has-back {
140
+ padding-left: 100rpx;
141
+ }
142
+ }
143
+
144
+ &.align-center {
145
+ justify-content: center;
146
+ }
156
147
 
157
148
  .left {
158
149
  position: absolute;
@@ -165,31 +156,16 @@ function tapBack() {
165
156
 
166
157
  .icon-left {
167
158
  font-size: 30rpx;
159
+ color: var(--text-primary, #303048);
168
160
  }
169
161
  }
170
162
 
171
163
  .title {
172
- font-size: var(--font-md, var(--navbar-font-size, 32rpx));
173
- letter-spacing: 2rpx;
164
+ font-size: var(--navbar-font-size, var(--font-md, 32rpx));
165
+ font-family: var(--navbar-font-family, inherit);
166
+ letter-spacing: 3rpx;
174
167
  font-weight: 500;
175
- }
176
- }
177
-
178
- .status-bar {
179
- background-color: transparent;
180
- height: v-bind(status_bar_height_style);
181
- width: 750rpx;
182
- position: relative;
183
-
184
- .within {
185
- position: absolute;
186
- left: 0;
187
- top: 0;
188
- width: 750rpx;
189
- height: calc(v-bind(status_bar_height_style) + 2rpx);
190
- background-color: var(--bg-color, var(--bg-page, #f8f8f8));
191
- border-top-left-radius: var(--status-bar-border, var(--card-radius, 32rpx));
192
- border-top-right-radius: var(--status-bar-border, var(--card-radius, 32rpx));
168
+ color: var(--text-primary, #303048);
193
169
  }
194
170
  }
195
171
  }
@@ -1,6 +1,15 @@
1
1
  <template>
2
- <view :class="[theme, fontSizeClass, fontFamilyClass]">
3
- <hlw-nav-bar v-if="props.isNav" :is-back="props.isBack" :title="title" :is-bar="props.isBar"></hlw-nav-bar>
2
+ <view :class="[fontSizeClass, fontFamilyClass]">
3
+ <hlw-nav-bar v-if="props.isNav"
4
+ :is-back="props.isBack"
5
+ :title="title"
6
+ :is-bar="props.isBar"
7
+ :title-align="props.titleAlign"
8
+ :title-size="props.titleSize"
9
+ :title-style="props.titleStyle"
10
+ :title-weight="props.titleWeight"
11
+ :border="props.border">
12
+ </hlw-nav-bar>
4
13
  <slot></slot>
5
14
  <view class="h-[60rpx]"></view>
6
15
  </view>
@@ -14,10 +23,15 @@
14
23
  * 可以快捷集成自定义导航栏(HlwNavBar),保持整个页面结构的一致性。
15
24
  *
16
25
  * @props
17
- * isNav - 是否显示自定义导航栏,默认 false
18
- * isBar - 是否占用状态栏高度,默认 true
19
- * title - 自定义导航栏标题文字
20
- * isBack - 是否显示自定义导航栏的返回键,默认 false
26
+ * isNav - 是否显示自定义导航栏,默认 false
27
+ * isBar - 是否占用状态栏高度,默认 true
28
+ * title - 自定义导航栏标题文字
29
+ * isBack - 是否显示自定义导航栏的返回键,默认 false
30
+ * titleAlign - 标题对齐方式,'left' | 'center'
31
+ * titleSize - 标题字体大小
32
+ * titleStyle - 标题字体样式
33
+ * titleWeight - 标题字重
34
+ * border - 是否显示自定义导航栏的下边框(下划线),默认 true
21
35
  *
22
36
  * @example
23
37
  * ```vue
@@ -27,9 +41,8 @@
27
41
  * ```
28
42
  */
29
43
  import { useTheme } from "@/core";
30
- import { onLoad, onShow } from "@dcloudio/uni-app";
31
44
  import { ref } from "vue";
32
- const { theme, fontSizeClass, fontFamilyClass } = useTheme();
45
+ const { fontSizeClass, fontFamilyClass } = useTheme();
33
46
 
34
47
  const props = defineProps({
35
48
  isNav: {
@@ -48,6 +61,26 @@ const props = defineProps({
48
61
  type: Boolean,
49
62
  default: false,
50
63
  },
64
+ titleAlign: {
65
+ type: String,
66
+ default: "center",
67
+ },
68
+ titleSize: {
69
+ type: String,
70
+ default: "",
71
+ },
72
+ titleStyle: {
73
+ type: String,
74
+ default: "",
75
+ },
76
+ titleWeight: {
77
+ type: String,
78
+ default: "500",
79
+ },
80
+ border: {
81
+ type: Boolean,
82
+ default: true,
83
+ }
51
84
  });
52
85
 
53
86
  const title = ref(props.title);
@@ -1,10 +1,13 @@
1
1
  <template>
2
- <view class="hlw-reward-ad" />
2
+ <view class="hlw-reward-ad" @tap="open">
3
+ <slot />
4
+ </view>
3
5
  </template>
4
6
 
5
7
  <script setup lang="ts">
6
8
  import { ref, onUnmounted } from "vue";
7
9
  import { setRewardAd, showRewardAd, confirmRewardAd, destroyRewardAd } from "../../utils/ad";
10
+ import type { HlwRewardAdResult } from "./types";
8
11
 
9
12
  defineOptions({ name: "HlwRewardAd" });
10
13
 
@@ -16,7 +19,7 @@ interface Props {
16
19
  const props = defineProps<Props>();
17
20
 
18
21
  const emit = defineEmits<{
19
- (e: "onHandle", res: { success: boolean; isEnded: boolean; loadFailed?: boolean; err?: any }): void;
22
+ (e: "onHandle", res: HlwRewardAdResult): void;
20
23
  }>();
21
24
 
22
25
  // 点击锁定状态,防止连续多次点击重复触发广告
@@ -90,6 +93,20 @@ async function playRewardAdFlow(): Promise<void> {
90
93
  }
91
94
  }
92
95
 
96
+ async function open() {
97
+ if (isClicked.value) return;
98
+ if (!props.unitId) {
99
+ console.warn("[HlwRewardAd] unitId is required but empty.");
100
+ return;
101
+ }
102
+ isClicked.value = true;
103
+ try {
104
+ await playRewardAdFlow();
105
+ } finally {
106
+ isClicked.value = false;
107
+ }
108
+ }
109
+
93
110
  /**
94
111
  * Expose a method for parent components to open the reward ad programmatically via ref.
95
112
  * Usage: const adRef = ref(null);
@@ -97,19 +114,7 @@ async function playRewardAdFlow(): Promise<void> {
97
114
  * adRef.value?.open();
98
115
  */
99
116
  defineExpose({
100
- async open() {
101
- if (isClicked.value) return;
102
- if (!props.unitId) {
103
- console.warn("[HlwRewardAd] unitId is required but empty.");
104
- return;
105
- }
106
- isClicked.value = true;
107
- try {
108
- await playRewardAdFlow();
109
- } finally {
110
- isClicked.value = false;
111
- }
112
- }
117
+ open
113
118
  });
114
119
 
115
120
  onUnmounted(() => {
@@ -119,6 +124,6 @@ onUnmounted(() => {
119
124
 
120
125
  <style scoped>
121
126
  .hlw-reward-ad {
122
- display: none;
127
+ display: block;
123
128
  }
124
129
  </style>
@@ -0,0 +1,10 @@
1
+ export interface HlwRewardAdResult {
2
+ /** 是否播放成功 */
3
+ success: boolean;
4
+ /** 广告是否完整播放结束 */
5
+ isEnded: boolean;
6
+ /** 广告加载是否失败 */
7
+ loadFailed?: boolean;
8
+ /** 加载/播放失败时的错误对象 */
9
+ err?: any;
10
+ }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <view
3
3
  class="hlw-status-bar"
4
- :class="[theme, props.fixed ? 'fixed' : '', props.customClass]"
4
+ :class="[props.fixed ? 'fixed' : '', props.customClass]"
5
5
  :style="statusBarStyle"
6
6
  >
7
7
  <slot></slot>
@@ -30,9 +30,7 @@
30
30
  * ```
31
31
  */
32
32
  import { computed } from "vue";
33
- import { useTheme } from "@/core";
34
33
 
35
- const { theme } = useTheme();
36
34
 
37
35
  const props = defineProps({
38
36
  /** 是否固定在顶部 */
@@ -82,6 +80,7 @@ const statusBarStyle = computed(() => {
82
80
  <style lang="scss" scoped>
83
81
  .hlw-status-bar {
84
82
  width: 750rpx;
83
+ background-color: var(--navbar-bg-color, #ffffff);
85
84
  transition: background-color 0.2s ease, background 0.2s ease;
86
85
 
87
86
  &.fixed {
@@ -89,25 +88,5 @@ const statusBarStyle = computed(() => {
89
88
  top: 0;
90
89
  left: 0;
91
90
  }
92
-
93
- /* 白色主题:白色状态栏 */
94
- &.white-theme {
95
- background-color: var(--navbar-bg-color, #ffffff);
96
- }
97
-
98
- /* 简洁主题:背景色与页面全局背景色一致 */
99
- &.light-theme {
100
- background-color: var(--bg-page, #f8f8f8);
101
- }
102
-
103
- /* 单色主题:纯主题色状态栏 */
104
- &.mono-theme {
105
- background-color: var(--primary-color, #3b82f6);
106
- }
107
-
108
- /* 颜色主题:立体光影感的主题色渐变背景 */
109
- &.color-theme {
110
- background: linear-gradient(135deg, rgba(0, 0, 0, 0.15) 0%, rgba(255, 255, 255, 0.15) 100%), var(--primary-color, #3b82f6);
111
- }
112
91
  }
113
92
  </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <scroll-view class="hlw-tabs" :scroll-x="scrollable" :enhanced="true" :show-scrollbar="false">
3
- <view class="hlw-tabs-wrap">
3
+ <view class="hlw-tabs-wrap" style="position: relative;">
4
4
  <view
5
5
  v-for="(item, index) in items"
6
6
  :key="index"
@@ -13,8 +13,9 @@
13
13
  v-if="typeof item !== 'string' && item.badge"
14
14
  class="hlw-tab-badge"
15
15
  >{{ item.badge }}</view>
16
- <view v-if="modelValue === index" class="hlw-tab-line" :style="{ width: lineWidth }" />
17
16
  </view>
17
+ <!-- 唯一的一根滑动线条,放置在父容器下,以便做平移 (translateX) 动画 -->
18
+ <view class="hlw-tab-line" :style="lineStyle" />
18
19
  </view>
19
20
  </scroll-view>
20
21
  </template>
@@ -42,6 +43,8 @@
42
43
  * <HlwTabs v-model="tab" :items="[{ label: '消息', badge: '3' }, { label: '关注' }]" />
43
44
  * ```
44
45
  */
46
+ import { computed } from "vue";
47
+
45
48
  export interface HlwTabItem {
46
49
  label: string;
47
50
  badge?: string;
@@ -54,7 +57,7 @@ interface Props {
54
57
  lineWidth?: string;
55
58
  }
56
59
 
57
- withDefaults(defineProps<Props>(), {
60
+ const props = withDefaults(defineProps<Props>(), {
58
61
  modelValue: 0,
59
62
  items: () => [],
60
63
  scrollable: false,
@@ -63,6 +66,22 @@ withDefaults(defineProps<Props>(), {
63
66
 
64
67
  const emit = defineEmits<{ "update:modelValue": [index: number]; change: [index: number] }>();
65
68
 
69
+ // 计算滑动线条的位置
70
+ const lineStyle = computed(() => {
71
+ const count = props.items?.length || 0;
72
+ if (count === 0) return { display: 'none' };
73
+
74
+ // 假设在非滚动模式下,每一项平分宽度,即 100% / count
75
+ const tabWidthPercent = 100 / count;
76
+ // 居中偏移百分比 = (选中索引 + 0.5) * 每项宽度百分比
77
+ const leftOffset = (props.modelValue + 0.5) * tabWidthPercent;
78
+
79
+ return {
80
+ width: props.lineWidth,
81
+ left: `${leftOffset}%`
82
+ };
83
+ });
84
+
66
85
  function onChange(index: number) {
67
86
  emit("update:modelValue", index);
68
87
  emit("change", index);
@@ -90,15 +109,18 @@ function onChange(index: number) {
90
109
  gap: 6rpx;
91
110
  transition: color 0.2s;
92
111
 
93
- &--active .hlw-tab-text {
94
- color: var(--primary-color, #3b82f6);
95
- font-weight: 600;
112
+ &--active {
113
+ .hlw-tab-text {
114
+ color: var(--primary-color, #3b82f6);
115
+ font-weight: 600;
116
+ }
96
117
  }
97
118
  }
98
119
 
99
120
  .hlw-tab-text {
100
121
  font-size: var(--font-base, 28rpx);
101
122
  color: #64748b;
123
+ transition: color 0.2s;
102
124
  }
103
125
 
104
126
  .hlw-tab-badge {
@@ -115,12 +137,13 @@ function onChange(index: number) {
115
137
 
116
138
  .hlw-tab-line {
117
139
  position: absolute;
118
- bottom: 4rpx;
119
- left: 50%;
120
- transform: translateX(-50%);
140
+ bottom: 6rpx;
121
141
  height: 6rpx;
122
- border-radius: 6rpx;
142
+ border-radius: 999rpx; /* 胶囊圆角 */
123
143
  background: var(--primary-color, #3b82f6);
124
- transition: width 0.2s;
144
+ /* 居中定位 */
145
+ transform: translateX(-50%);
146
+ /* 在 left 变化时产生平移动画,采用缓动曲线 */
147
+ transition: left 0.3s cubic-bezier(0.25, 1, 0.5, 1);
125
148
  }
126
149
  </style>
@@ -2,7 +2,6 @@ import { computed } from "vue";
2
2
  import type { ComputedRef } from "vue";
3
3
  import {
4
4
  useThemeStore,
5
- themePresets,
6
5
  fontSizePresets,
7
6
  fontFamilyPresets
8
7
  } from "../../stores/theme";
@@ -10,8 +9,6 @@ import {
10
9
  declare const uni: any;
11
10
 
12
11
  export {
13
- themePresets,
14
- type ThemePreset,
15
12
  fontSizePresets,
16
13
  type FontSizePreset,
17
14
  fontFamilyPresets,
@@ -25,9 +22,6 @@ export {
25
22
  export function useTheme() {
26
23
  const store = useThemeStore();
27
24
 
28
- // 主题
29
- const theme: ComputedRef<string> = computed(() => store.theme);
30
-
31
25
  // 字体大小
32
26
  const fontSize: ComputedRef<string> = computed(() => store.fontSize);
33
27
  const fontSizeClass: ComputedRef<string> = computed(() => {
@@ -52,7 +46,6 @@ export function useTheme() {
52
46
  }
53
47
 
54
48
  return {
55
- theme,
56
49
  fontSize,
57
50
  fontSizeClass,
58
51
  setFontSize,
@@ -67,13 +60,6 @@ export function useTheme() {
67
60
  * 首次打开小程序时,初始化默认的主题配色
68
61
  * @param defaultTheme 默认主题 ID,默认为 "mono-theme"
69
62
  */
70
- export function initTheme(defaultTheme = "mono-theme"): void {
71
- const hasCachedTheme = uni.getStorageSync("theme");
72
- if (!hasCachedTheme) {
73
- const store = useThemeStore();
74
- const validThemes = themePresets.map((t) => t.id);
75
- if (validThemes.includes(defaultTheme)) {
76
- store.theme = defaultTheme;
77
- }
78
- }
63
+ export function initTheme(defaultTheme?: string): void {
64
+ // No-op after color theme presets removal
79
65
  }
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export * from "./utils";
15
15
  // 类型
16
16
  export type { HlwMenuItem } from "./components/hlw-menu/types";
17
17
  export type { HlwPagingRef, HlwPagingInstance } from "./components/hlw-paging/types";
18
+ export type { HlwRewardAdResult } from "./components/hlw-reward-ad/types";
18
19
 
19
20
  // App 根上下文
20
21
  export { useApp } from "./app";
@@ -4,34 +4,7 @@ import { defineStore } from "pinia";
4
4
  // 1. 主题 (Theme) 预设与类型
5
5
  // ==========================================
6
6
 
7
- export interface ThemePreset {
8
- id: string;
9
- name: string;
10
- color: string;
11
- }
12
7
 
13
- export const themePresets: ThemePreset[] = [
14
- {
15
- id: "white-theme",
16
- name: "白色主题",
17
- color: "#ffffff",
18
- },
19
- {
20
- id: "light-theme",
21
- name: "简洁主题",
22
- color: "var(--bg-page, #f8f8f8)",
23
- },
24
- {
25
- id: "mono-theme",
26
- name: "单色主题",
27
- color: "var(--primary-color, #3b82f6)",
28
- },
29
- {
30
- id: "color-theme",
31
- name: "颜色主题",
32
- color: "var(--primary-color, #3b82f6)",
33
- },
34
- ];
35
8
 
36
9
  // ==========================================
37
10
  // 2. 字体大小 (FontSize) 预设与类型
@@ -105,7 +78,6 @@ export const fontFamilyPresets: FontFamilyPreset[] = [
105
78
 
106
79
  export const useThemeStore = defineStore("theme", {
107
80
  state: () => ({
108
- theme: "white-theme",
109
81
  fontSize: "standard",
110
82
  fontFamily: "system",
111
83
  }),