@hlw-uni/mp-vue 1.1.22 → 1.2.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/dist/composables/theme/appearance.d.ts +28 -0
- package/dist/composables/theme/index.d.ts +2 -0
- package/dist/index.js +90 -2
- package/dist/index.mjs +90 -2
- package/dist/stores/theme.d.ts +19 -4
- package/package.json +1 -1
- package/src/components/hlw-page/index.vue +2 -0
- package/src/composables/theme/appearance.ts +101 -0
- package/src/composables/theme/index.ts +12 -0
- package/src/stores/theme.ts +27 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 外观模式 — 浅色 / 深色 / 跟随系统
|
|
3
|
+
*
|
|
4
|
+
* 提供一套语义化 CSS 变量(--bg-page, --surface-card, --text-primary ...),
|
|
5
|
+
* 页面通过 hlw-page 的 `:page-style` 注入到 `page` 元素,让内部组件和样式统一消费。
|
|
6
|
+
*
|
|
7
|
+
* 使用指南:
|
|
8
|
+
* - 业务样式里用 `var(--text-primary)` 代替硬编码 `#0f172a`
|
|
9
|
+
* - 用 `var(--surface-card)` 代替硬编码 `#ffffff`
|
|
10
|
+
* - 用 `var(--border-color-light)` 代替硬编码 `#f1f5f9`
|
|
11
|
+
*/
|
|
12
|
+
export type Appearance = "light" | "dark" | "auto";
|
|
13
|
+
export type AppearanceMode = "light" | "dark";
|
|
14
|
+
export interface AppearancePreset {
|
|
15
|
+
value: Appearance;
|
|
16
|
+
label: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const APPEARANCE_KEY = "hlw_appearance";
|
|
19
|
+
export declare const APPEARANCE_PRESETS: AppearancePreset[];
|
|
20
|
+
export declare const APPEARANCE_VAR_MAP: Record<AppearanceMode, Record<string, string>>;
|
|
21
|
+
/** 读取用户设置的外观(light/dark/auto),默认 auto */
|
|
22
|
+
export declare function getCurrentAppearance(): Appearance;
|
|
23
|
+
/** 将 auto 解析为具体的 light/dark,依据系统主题 */
|
|
24
|
+
export declare function resolveAppearance(appearance: Appearance): AppearanceMode;
|
|
25
|
+
/** 当前实际生效的模式(light 或 dark) */
|
|
26
|
+
export declare function getCurrentAppearanceMode(): AppearanceMode;
|
|
27
|
+
/** 当前外观对应的 CSS 变量 */
|
|
28
|
+
export declare function getCurrentAppearanceVars(): Record<string, string>;
|
|
@@ -8,3 +8,5 @@ export type { FontScale, FontPreset } from './font';
|
|
|
8
8
|
export { FONT_SCALE_KEY, FONT_PRESETS, getCurrentFontScale, getCurrentFontVars } from './font';
|
|
9
9
|
export type { ThemeColor } from './palette';
|
|
10
10
|
export { THEME_COLOR_KEY, THEME_SEMANTIC_COLORS, DEFAULT_THEMES, getCurrentThemeColor, getCurrentThemeVars, } from './palette';
|
|
11
|
+
export type { Appearance, AppearanceMode, AppearancePreset } from './appearance';
|
|
12
|
+
export { APPEARANCE_KEY, APPEARANCE_PRESETS, APPEARANCE_VAR_MAP, getCurrentAppearance, getCurrentAppearanceMode, getCurrentAppearanceVars, resolveAppearance, } from './appearance';
|
package/dist/index.js
CHANGED
|
@@ -118,12 +118,85 @@
|
|
|
118
118
|
"--info-dark": darkenHex(THEME_SEMANTIC_COLORS.info)
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
+
const APPEARANCE_KEY = "hlw_appearance";
|
|
122
|
+
const APPEARANCE_PRESETS = [
|
|
123
|
+
{ value: "light", label: "浅色模式" },
|
|
124
|
+
{ value: "dark", label: "深色模式" },
|
|
125
|
+
{ value: "auto", label: "跟随系统" }
|
|
126
|
+
];
|
|
127
|
+
const LIGHT_VARS = {
|
|
128
|
+
"--bg-page": "#f6f6f6",
|
|
129
|
+
"--bg-elevated": "#ffffff",
|
|
130
|
+
"--surface-card": "#ffffff",
|
|
131
|
+
"--surface-card-muted": "#f8fafc",
|
|
132
|
+
"--surface-secondary": "#f1f5f9",
|
|
133
|
+
"--surface-tertiary": "#e2e8f0",
|
|
134
|
+
"--text-primary": "#0f172a",
|
|
135
|
+
"--text-secondary": "#334155",
|
|
136
|
+
"--text-muted": "#64748b",
|
|
137
|
+
"--text-subtle": "#94a3b8",
|
|
138
|
+
"--text-disabled": "#cbd5e1",
|
|
139
|
+
"--border-color": "#e2e8f0",
|
|
140
|
+
"--border-color-light": "#f1f5f9",
|
|
141
|
+
"--border-color-focus": "#bfdbfe",
|
|
142
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(15, 23, 42, 0.04)",
|
|
143
|
+
"--shadow-card": "0 4rpx 16rpx rgba(15, 23, 42, 0.06)"
|
|
144
|
+
};
|
|
145
|
+
const DARK_VARS = {
|
|
146
|
+
"--bg-page": "#0b1020",
|
|
147
|
+
"--bg-elevated": "#111827",
|
|
148
|
+
"--surface-card": "#1e293b",
|
|
149
|
+
"--surface-card-muted": "#273549",
|
|
150
|
+
"--surface-secondary": "#273549",
|
|
151
|
+
"--surface-tertiary": "#334155",
|
|
152
|
+
"--text-primary": "#f8fafc",
|
|
153
|
+
"--text-secondary": "#cbd5e1",
|
|
154
|
+
"--text-muted": "#94a3b8",
|
|
155
|
+
"--text-subtle": "#64748b",
|
|
156
|
+
"--text-disabled": "#475569",
|
|
157
|
+
"--border-color": "#334155",
|
|
158
|
+
"--border-color-light": "#1e293b",
|
|
159
|
+
"--border-color-focus": "#3b82f6",
|
|
160
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(0, 0, 0, 0.3)",
|
|
161
|
+
"--shadow-card": "0 4rpx 16rpx rgba(0, 0, 0, 0.35)"
|
|
162
|
+
};
|
|
163
|
+
const APPEARANCE_VAR_MAP = {
|
|
164
|
+
light: LIGHT_VARS,
|
|
165
|
+
dark: DARK_VARS
|
|
166
|
+
};
|
|
167
|
+
function getCurrentAppearance() {
|
|
168
|
+
try {
|
|
169
|
+
const v = uni.getStorageSync(APPEARANCE_KEY);
|
|
170
|
+
if (v === "light" || v === "dark" || v === "auto")
|
|
171
|
+
return v;
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
return "auto";
|
|
175
|
+
}
|
|
176
|
+
function resolveAppearance(appearance) {
|
|
177
|
+
if (appearance === "light" || appearance === "dark")
|
|
178
|
+
return appearance;
|
|
179
|
+
try {
|
|
180
|
+
const info = uni.getSystemInfoSync();
|
|
181
|
+
if (info.theme === "dark")
|
|
182
|
+
return "dark";
|
|
183
|
+
} catch {
|
|
184
|
+
}
|
|
185
|
+
return "light";
|
|
186
|
+
}
|
|
187
|
+
function getCurrentAppearanceMode() {
|
|
188
|
+
return resolveAppearance(getCurrentAppearance());
|
|
189
|
+
}
|
|
190
|
+
function getCurrentAppearanceVars() {
|
|
191
|
+
return APPEARANCE_VAR_MAP[getCurrentAppearanceMode()];
|
|
192
|
+
}
|
|
121
193
|
const { varsToStyle } = mpCore.useColor();
|
|
122
194
|
const THEME_CHANGE_EVENT = "hlw:theme-change";
|
|
123
195
|
function buildThemeStyle() {
|
|
124
196
|
return varsToStyle({
|
|
125
197
|
...getCurrentFontVars(),
|
|
126
|
-
...getCurrentThemeVars()
|
|
198
|
+
...getCurrentThemeVars(),
|
|
199
|
+
...getCurrentAppearanceVars()
|
|
127
200
|
});
|
|
128
201
|
}
|
|
129
202
|
function useThemePageStyle() {
|
|
@@ -158,6 +231,15 @@
|
|
|
158
231
|
uni.setStorageSync(THEME_COLOR_KEY, color);
|
|
159
232
|
uni.$emit(THEME_CHANGE_EVENT);
|
|
160
233
|
}
|
|
234
|
+
const appearance = vue.ref("auto");
|
|
235
|
+
const appearanceOptions = APPEARANCE_PRESETS;
|
|
236
|
+
const appearanceMode = vue.computed(() => resolveAppearance(appearance.value));
|
|
237
|
+
const isDark = vue.computed(() => appearanceMode.value === "dark");
|
|
238
|
+
function setAppearance(a) {
|
|
239
|
+
appearance.value = a;
|
|
240
|
+
uni.setStorageSync(APPEARANCE_KEY, a);
|
|
241
|
+
uni.$emit(THEME_CHANGE_EVENT);
|
|
242
|
+
}
|
|
161
243
|
return {
|
|
162
244
|
// 字体
|
|
163
245
|
scale,
|
|
@@ -167,7 +249,13 @@
|
|
|
167
249
|
primaryColor,
|
|
168
250
|
themes,
|
|
169
251
|
activeTheme,
|
|
170
|
-
setTheme
|
|
252
|
+
setTheme,
|
|
253
|
+
// 外观模式
|
|
254
|
+
appearance,
|
|
255
|
+
appearanceOptions,
|
|
256
|
+
appearanceMode,
|
|
257
|
+
isDark,
|
|
258
|
+
setAppearance
|
|
171
259
|
};
|
|
172
260
|
},
|
|
173
261
|
{ unistorage: true }
|
package/dist/index.mjs
CHANGED
|
@@ -117,12 +117,85 @@ function getCurrentThemeVars() {
|
|
|
117
117
|
"--info-dark": darkenHex(THEME_SEMANTIC_COLORS.info)
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
|
+
const APPEARANCE_KEY = "hlw_appearance";
|
|
121
|
+
const APPEARANCE_PRESETS = [
|
|
122
|
+
{ value: "light", label: "浅色模式" },
|
|
123
|
+
{ value: "dark", label: "深色模式" },
|
|
124
|
+
{ value: "auto", label: "跟随系统" }
|
|
125
|
+
];
|
|
126
|
+
const LIGHT_VARS = {
|
|
127
|
+
"--bg-page": "#f6f6f6",
|
|
128
|
+
"--bg-elevated": "#ffffff",
|
|
129
|
+
"--surface-card": "#ffffff",
|
|
130
|
+
"--surface-card-muted": "#f8fafc",
|
|
131
|
+
"--surface-secondary": "#f1f5f9",
|
|
132
|
+
"--surface-tertiary": "#e2e8f0",
|
|
133
|
+
"--text-primary": "#0f172a",
|
|
134
|
+
"--text-secondary": "#334155",
|
|
135
|
+
"--text-muted": "#64748b",
|
|
136
|
+
"--text-subtle": "#94a3b8",
|
|
137
|
+
"--text-disabled": "#cbd5e1",
|
|
138
|
+
"--border-color": "#e2e8f0",
|
|
139
|
+
"--border-color-light": "#f1f5f9",
|
|
140
|
+
"--border-color-focus": "#bfdbfe",
|
|
141
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(15, 23, 42, 0.04)",
|
|
142
|
+
"--shadow-card": "0 4rpx 16rpx rgba(15, 23, 42, 0.06)"
|
|
143
|
+
};
|
|
144
|
+
const DARK_VARS = {
|
|
145
|
+
"--bg-page": "#0b1020",
|
|
146
|
+
"--bg-elevated": "#111827",
|
|
147
|
+
"--surface-card": "#1e293b",
|
|
148
|
+
"--surface-card-muted": "#273549",
|
|
149
|
+
"--surface-secondary": "#273549",
|
|
150
|
+
"--surface-tertiary": "#334155",
|
|
151
|
+
"--text-primary": "#f8fafc",
|
|
152
|
+
"--text-secondary": "#cbd5e1",
|
|
153
|
+
"--text-muted": "#94a3b8",
|
|
154
|
+
"--text-subtle": "#64748b",
|
|
155
|
+
"--text-disabled": "#475569",
|
|
156
|
+
"--border-color": "#334155",
|
|
157
|
+
"--border-color-light": "#1e293b",
|
|
158
|
+
"--border-color-focus": "#3b82f6",
|
|
159
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(0, 0, 0, 0.3)",
|
|
160
|
+
"--shadow-card": "0 4rpx 16rpx rgba(0, 0, 0, 0.35)"
|
|
161
|
+
};
|
|
162
|
+
const APPEARANCE_VAR_MAP = {
|
|
163
|
+
light: LIGHT_VARS,
|
|
164
|
+
dark: DARK_VARS
|
|
165
|
+
};
|
|
166
|
+
function getCurrentAppearance() {
|
|
167
|
+
try {
|
|
168
|
+
const v = uni.getStorageSync(APPEARANCE_KEY);
|
|
169
|
+
if (v === "light" || v === "dark" || v === "auto")
|
|
170
|
+
return v;
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
return "auto";
|
|
174
|
+
}
|
|
175
|
+
function resolveAppearance(appearance) {
|
|
176
|
+
if (appearance === "light" || appearance === "dark")
|
|
177
|
+
return appearance;
|
|
178
|
+
try {
|
|
179
|
+
const info = uni.getSystemInfoSync();
|
|
180
|
+
if (info.theme === "dark")
|
|
181
|
+
return "dark";
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
return "light";
|
|
185
|
+
}
|
|
186
|
+
function getCurrentAppearanceMode() {
|
|
187
|
+
return resolveAppearance(getCurrentAppearance());
|
|
188
|
+
}
|
|
189
|
+
function getCurrentAppearanceVars() {
|
|
190
|
+
return APPEARANCE_VAR_MAP[getCurrentAppearanceMode()];
|
|
191
|
+
}
|
|
120
192
|
const { varsToStyle } = useColor();
|
|
121
193
|
const THEME_CHANGE_EVENT = "hlw:theme-change";
|
|
122
194
|
function buildThemeStyle() {
|
|
123
195
|
return varsToStyle({
|
|
124
196
|
...getCurrentFontVars(),
|
|
125
|
-
...getCurrentThemeVars()
|
|
197
|
+
...getCurrentThemeVars(),
|
|
198
|
+
...getCurrentAppearanceVars()
|
|
126
199
|
});
|
|
127
200
|
}
|
|
128
201
|
function useThemePageStyle() {
|
|
@@ -157,6 +230,15 @@ const useThemeStore = defineStore(
|
|
|
157
230
|
uni.setStorageSync(THEME_COLOR_KEY, color);
|
|
158
231
|
uni.$emit(THEME_CHANGE_EVENT);
|
|
159
232
|
}
|
|
233
|
+
const appearance = ref("auto");
|
|
234
|
+
const appearanceOptions = APPEARANCE_PRESETS;
|
|
235
|
+
const appearanceMode = computed(() => resolveAppearance(appearance.value));
|
|
236
|
+
const isDark = computed(() => appearanceMode.value === "dark");
|
|
237
|
+
function setAppearance(a) {
|
|
238
|
+
appearance.value = a;
|
|
239
|
+
uni.setStorageSync(APPEARANCE_KEY, a);
|
|
240
|
+
uni.$emit(THEME_CHANGE_EVENT);
|
|
241
|
+
}
|
|
160
242
|
return {
|
|
161
243
|
// 字体
|
|
162
244
|
scale,
|
|
@@ -166,7 +248,13 @@ const useThemeStore = defineStore(
|
|
|
166
248
|
primaryColor,
|
|
167
249
|
themes,
|
|
168
250
|
activeTheme,
|
|
169
|
-
setTheme
|
|
251
|
+
setTheme,
|
|
252
|
+
// 外观模式
|
|
253
|
+
appearance,
|
|
254
|
+
appearanceOptions,
|
|
255
|
+
appearanceMode,
|
|
256
|
+
isDark,
|
|
257
|
+
setAppearance
|
|
170
258
|
};
|
|
171
259
|
},
|
|
172
260
|
{ unistorage: true }
|
package/dist/stores/theme.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FontScale, ThemeColor } from '../composables/theme';
|
|
1
|
+
import { FontScale, ThemeColor, Appearance, AppearanceMode, AppearancePreset } from '../composables/theme';
|
|
2
2
|
import { StoreDefinition } from 'pinia';
|
|
3
3
|
import { Ref, ComputedRef } from 'vue';
|
|
4
4
|
|
|
@@ -13,7 +13,12 @@ export declare const useThemeStore: StoreDefinition<"theme", Pick<{
|
|
|
13
13
|
themes: ThemeColor[];
|
|
14
14
|
activeTheme: ComputedRef<ThemeColor>;
|
|
15
15
|
setTheme: (color: string) => void;
|
|
16
|
-
|
|
16
|
+
appearance: Ref<Appearance, Appearance>;
|
|
17
|
+
appearanceOptions: AppearancePreset[];
|
|
18
|
+
appearanceMode: ComputedRef<AppearanceMode>;
|
|
19
|
+
isDark: ComputedRef<boolean>;
|
|
20
|
+
setAppearance: (a: Appearance) => void;
|
|
21
|
+
}, "scale" | "fontOptions" | "primaryColor" | "themes" | "appearance" | "appearanceOptions">, Pick<{
|
|
17
22
|
scale: Ref<FontScale, FontScale>;
|
|
18
23
|
fontOptions: {
|
|
19
24
|
value: FontScale;
|
|
@@ -24,7 +29,12 @@ export declare const useThemeStore: StoreDefinition<"theme", Pick<{
|
|
|
24
29
|
themes: ThemeColor[];
|
|
25
30
|
activeTheme: ComputedRef<ThemeColor>;
|
|
26
31
|
setTheme: (color: string) => void;
|
|
27
|
-
|
|
32
|
+
appearance: Ref<Appearance, Appearance>;
|
|
33
|
+
appearanceOptions: AppearancePreset[];
|
|
34
|
+
appearanceMode: ComputedRef<AppearanceMode>;
|
|
35
|
+
isDark: ComputedRef<boolean>;
|
|
36
|
+
setAppearance: (a: Appearance) => void;
|
|
37
|
+
}, "activeTheme" | "appearanceMode" | "isDark">, Pick<{
|
|
28
38
|
scale: Ref<FontScale, FontScale>;
|
|
29
39
|
fontOptions: {
|
|
30
40
|
value: FontScale;
|
|
@@ -35,4 +45,9 @@ export declare const useThemeStore: StoreDefinition<"theme", Pick<{
|
|
|
35
45
|
themes: ThemeColor[];
|
|
36
46
|
activeTheme: ComputedRef<ThemeColor>;
|
|
37
47
|
setTheme: (color: string) => void;
|
|
38
|
-
|
|
48
|
+
appearance: Ref<Appearance, Appearance>;
|
|
49
|
+
appearanceOptions: AppearancePreset[];
|
|
50
|
+
appearanceMode: ComputedRef<AppearanceMode>;
|
|
51
|
+
isDark: ComputedRef<boolean>;
|
|
52
|
+
setAppearance: (a: Appearance) => void;
|
|
53
|
+
}, "setScale" | "setTheme" | "setAppearance">>;
|
package/package.json
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 外观模式 — 浅色 / 深色 / 跟随系统
|
|
3
|
+
*
|
|
4
|
+
* 提供一套语义化 CSS 变量(--bg-page, --surface-card, --text-primary ...),
|
|
5
|
+
* 页面通过 hlw-page 的 `:page-style` 注入到 `page` 元素,让内部组件和样式统一消费。
|
|
6
|
+
*
|
|
7
|
+
* 使用指南:
|
|
8
|
+
* - 业务样式里用 `var(--text-primary)` 代替硬编码 `#0f172a`
|
|
9
|
+
* - 用 `var(--surface-card)` 代替硬编码 `#ffffff`
|
|
10
|
+
* - 用 `var(--border-color-light)` 代替硬编码 `#f1f5f9`
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type Appearance = "light" | "dark" | "auto";
|
|
14
|
+
export type AppearanceMode = "light" | "dark";
|
|
15
|
+
|
|
16
|
+
export interface AppearancePreset {
|
|
17
|
+
value: Appearance;
|
|
18
|
+
label: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const APPEARANCE_KEY = "hlw_appearance";
|
|
22
|
+
|
|
23
|
+
export const APPEARANCE_PRESETS: AppearancePreset[] = [
|
|
24
|
+
{ value: "light", label: "浅色模式" },
|
|
25
|
+
{ value: "dark", label: "深色模式" },
|
|
26
|
+
{ value: "auto", label: "跟随系统" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
/** 浅色模式语义变量 */
|
|
30
|
+
const LIGHT_VARS: Record<string, string> = {
|
|
31
|
+
"--bg-page": "#f6f6f6",
|
|
32
|
+
"--bg-elevated": "#ffffff",
|
|
33
|
+
"--surface-card": "#ffffff",
|
|
34
|
+
"--surface-card-muted": "#f8fafc",
|
|
35
|
+
"--surface-secondary": "#f1f5f9",
|
|
36
|
+
"--surface-tertiary": "#e2e8f0",
|
|
37
|
+
"--text-primary": "#0f172a",
|
|
38
|
+
"--text-secondary": "#334155",
|
|
39
|
+
"--text-muted": "#64748b",
|
|
40
|
+
"--text-subtle": "#94a3b8",
|
|
41
|
+
"--text-disabled": "#cbd5e1",
|
|
42
|
+
"--border-color": "#e2e8f0",
|
|
43
|
+
"--border-color-light": "#f1f5f9",
|
|
44
|
+
"--border-color-focus": "#bfdbfe",
|
|
45
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(15, 23, 42, 0.04)",
|
|
46
|
+
"--shadow-card": "0 4rpx 16rpx rgba(15, 23, 42, 0.06)",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** 深色模式语义变量 */
|
|
50
|
+
const DARK_VARS: Record<string, string> = {
|
|
51
|
+
"--bg-page": "#0b1020",
|
|
52
|
+
"--bg-elevated": "#111827",
|
|
53
|
+
"--surface-card": "#1e293b",
|
|
54
|
+
"--surface-card-muted": "#273549",
|
|
55
|
+
"--surface-secondary": "#273549",
|
|
56
|
+
"--surface-tertiary": "#334155",
|
|
57
|
+
"--text-primary": "#f8fafc",
|
|
58
|
+
"--text-secondary": "#cbd5e1",
|
|
59
|
+
"--text-muted": "#94a3b8",
|
|
60
|
+
"--text-subtle": "#64748b",
|
|
61
|
+
"--text-disabled": "#475569",
|
|
62
|
+
"--border-color": "#334155",
|
|
63
|
+
"--border-color-light": "#1e293b",
|
|
64
|
+
"--border-color-focus": "#3b82f6",
|
|
65
|
+
"--shadow-soft": "0 2rpx 8rpx rgba(0, 0, 0, 0.3)",
|
|
66
|
+
"--shadow-card": "0 4rpx 16rpx rgba(0, 0, 0, 0.35)",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const APPEARANCE_VAR_MAP: Record<AppearanceMode, Record<string, string>> = {
|
|
70
|
+
light: LIGHT_VARS,
|
|
71
|
+
dark: DARK_VARS,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** 读取用户设置的外观(light/dark/auto),默认 auto */
|
|
75
|
+
export function getCurrentAppearance(): Appearance {
|
|
76
|
+
try {
|
|
77
|
+
const v = uni.getStorageSync(APPEARANCE_KEY);
|
|
78
|
+
if (v === "light" || v === "dark" || v === "auto") return v;
|
|
79
|
+
} catch {}
|
|
80
|
+
return "auto";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 将 auto 解析为具体的 light/dark,依据系统主题 */
|
|
84
|
+
export function resolveAppearance(appearance: Appearance): AppearanceMode {
|
|
85
|
+
if (appearance === "light" || appearance === "dark") return appearance;
|
|
86
|
+
try {
|
|
87
|
+
const info = uni.getSystemInfoSync();
|
|
88
|
+
if ((info as { theme?: string }).theme === "dark") return "dark";
|
|
89
|
+
} catch {}
|
|
90
|
+
return "light";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 当前实际生效的模式(light 或 dark) */
|
|
94
|
+
export function getCurrentAppearanceMode(): AppearanceMode {
|
|
95
|
+
return resolveAppearance(getCurrentAppearance());
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 当前外观对应的 CSS 变量 */
|
|
99
|
+
export function getCurrentAppearanceVars(): Record<string, string> {
|
|
100
|
+
return APPEARANCE_VAR_MAP[getCurrentAppearanceMode()];
|
|
101
|
+
}
|
|
@@ -4,6 +4,7 @@ import { useColor } from "@hlw-uni/mp-core";
|
|
|
4
4
|
const { varsToStyle } = useColor();
|
|
5
5
|
import { getCurrentFontVars } from "./font";
|
|
6
6
|
import { getCurrentThemeVars } from "./palette";
|
|
7
|
+
import { getCurrentAppearanceVars } from "./appearance";
|
|
7
8
|
|
|
8
9
|
export const THEME_CHANGE_EVENT = "hlw:theme-change";
|
|
9
10
|
|
|
@@ -11,6 +12,7 @@ export function buildThemeStyle(): string {
|
|
|
11
12
|
return varsToStyle({
|
|
12
13
|
...getCurrentFontVars(),
|
|
13
14
|
...getCurrentThemeVars(),
|
|
15
|
+
...getCurrentAppearanceVars(),
|
|
14
16
|
});
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -37,3 +39,13 @@ export {
|
|
|
37
39
|
getCurrentThemeColor,
|
|
38
40
|
getCurrentThemeVars,
|
|
39
41
|
} from "./palette";
|
|
42
|
+
export type { Appearance, AppearanceMode, AppearancePreset } from "./appearance";
|
|
43
|
+
export {
|
|
44
|
+
APPEARANCE_KEY,
|
|
45
|
+
APPEARANCE_PRESETS,
|
|
46
|
+
APPEARANCE_VAR_MAP,
|
|
47
|
+
getCurrentAppearance,
|
|
48
|
+
getCurrentAppearanceMode,
|
|
49
|
+
getCurrentAppearanceVars,
|
|
50
|
+
resolveAppearance,
|
|
51
|
+
} from "./appearance";
|
package/src/stores/theme.ts
CHANGED
|
@@ -10,8 +10,11 @@ import {
|
|
|
10
10
|
DEFAULT_THEMES,
|
|
11
11
|
THEME_COLOR_KEY,
|
|
12
12
|
THEME_CHANGE_EVENT,
|
|
13
|
+
APPEARANCE_KEY,
|
|
14
|
+
APPEARANCE_PRESETS,
|
|
15
|
+
resolveAppearance,
|
|
13
16
|
} from "../composables/theme";
|
|
14
|
-
import type { FontScale, ThemeColor } from "../composables/theme";
|
|
17
|
+
import type { FontScale, ThemeColor, Appearance, AppearanceMode } from "../composables/theme";
|
|
15
18
|
|
|
16
19
|
export const useThemeStore = defineStore(
|
|
17
20
|
"theme",
|
|
@@ -49,6 +52,23 @@ export const useThemeStore = defineStore(
|
|
|
49
52
|
uni.$emit(THEME_CHANGE_EVENT);
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
// ─── 外观模式(light / dark / auto) ─────────
|
|
56
|
+
const appearance = ref<Appearance>("auto");
|
|
57
|
+
|
|
58
|
+
const appearanceOptions = APPEARANCE_PRESETS;
|
|
59
|
+
|
|
60
|
+
/** 当前实际生效的模式(auto 会被解析为 light/dark) */
|
|
61
|
+
const appearanceMode = computed<AppearanceMode>(() => resolveAppearance(appearance.value));
|
|
62
|
+
|
|
63
|
+
/** 是否深色模式(业务组件可直接读取做条件渲染) */
|
|
64
|
+
const isDark = computed(() => appearanceMode.value === "dark");
|
|
65
|
+
|
|
66
|
+
function setAppearance(a: Appearance) {
|
|
67
|
+
appearance.value = a;
|
|
68
|
+
uni.setStorageSync(APPEARANCE_KEY, a);
|
|
69
|
+
uni.$emit(THEME_CHANGE_EVENT);
|
|
70
|
+
}
|
|
71
|
+
|
|
52
72
|
return {
|
|
53
73
|
// 字体
|
|
54
74
|
scale,
|
|
@@ -59,6 +79,12 @@ export const useThemeStore = defineStore(
|
|
|
59
79
|
themes,
|
|
60
80
|
activeTheme,
|
|
61
81
|
setTheme,
|
|
82
|
+
// 外观模式
|
|
83
|
+
appearance,
|
|
84
|
+
appearanceOptions,
|
|
85
|
+
appearanceMode,
|
|
86
|
+
isDark,
|
|
87
|
+
setAppearance,
|
|
62
88
|
};
|
|
63
89
|
},
|
|
64
90
|
{ unistorage: true },
|