@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.
- package/dist/components/hlw-reward-ad/types.d.ts +10 -0
- package/dist/core/theme/index.d.ts +1 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -35
- package/dist/index.mjs +1 -35
- package/dist/stores/theme.d.ts +0 -7
- package/package.json +1 -1
- package/src/components/hlw-add-mini/index.vue +2 -2
- package/src/components/hlw-avatar-upload/index.vue +7 -48
- package/src/components/hlw-nav-bar/index.vue +68 -92
- package/src/components/hlw-page/index.vue +41 -8
- package/src/components/hlw-reward-ad/index.vue +21 -16
- package/src/components/hlw-reward-ad/types.ts +10 -0
- package/src/components/hlw-status-bar/index.vue +2 -23
- package/src/components/hlw-tabs/index.vue +34 -11
- package/src/core/theme/index.ts +2 -16
- package/src/index.ts +1 -0
- package/src/stores/theme.ts +0 -28
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { ComputedRef } from 'vue';
|
|
2
2
|
import { Store } from 'pinia';
|
|
3
3
|
|
|
4
|
-
export {
|
|
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
|
|
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
|
|
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,
|
package/dist/stores/theme.d.ts
CHANGED
|
@@ -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
|
@@ -86,8 +86,8 @@ const top = computed(() => {
|
|
|
86
86
|
try {
|
|
87
87
|
const menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
|
88
88
|
if (menuButtonInfo && menuButtonInfo.bottom > 0) {
|
|
89
|
-
// 胶囊底部高度 +
|
|
90
|
-
return `${menuButtonInfo.bottom +
|
|
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
|
-
<
|
|
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:
|
|
40
|
+
display: block;
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
62
43
|
}
|
|
63
44
|
|
|
64
45
|
.avatar-wrapper {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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="
|
|
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
|
|
26
|
-
* isBar
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
173
|
-
|
|
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="[
|
|
3
|
-
<hlw-nav-bar v-if="props.isNav"
|
|
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
|
|
18
|
-
* isBar
|
|
19
|
-
* title
|
|
20
|
-
* isBack
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
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:
|
|
127
|
+
display: block;
|
|
123
128
|
}
|
|
124
129
|
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view
|
|
3
3
|
class="hlw-status-bar"
|
|
4
|
-
:class="[
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
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:
|
|
119
|
-
left: 50%;
|
|
120
|
-
transform: translateX(-50%);
|
|
140
|
+
bottom: 6rpx;
|
|
121
141
|
height: 6rpx;
|
|
122
|
-
border-radius:
|
|
142
|
+
border-radius: 999rpx; /* 胶囊圆角 */
|
|
123
143
|
background: var(--primary-color, #3b82f6);
|
|
124
|
-
|
|
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>
|
package/src/core/theme/index.ts
CHANGED
|
@@ -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
|
|
71
|
-
|
|
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";
|
package/src/stores/theme.ts
CHANGED
|
@@ -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
|
}),
|