@hlw-uni/mp-vue 2.1.57 → 2.1.59

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.
Files changed (49) hide show
  1. package/dist/app.d.ts +12 -1
  2. package/dist/composables/ad/index.d.ts +26 -0
  3. package/dist/composables/device/index.d.ts +14 -0
  4. package/dist/composables/index.d.ts +1 -1
  5. package/dist/composables/msg/index.d.ts +44 -3
  6. package/dist/composables/navigator/index.d.ts +37 -1
  7. package/dist/composables/refs/index.d.ts +15 -1
  8. package/dist/composables/request/client.d.ts +87 -0
  9. package/dist/composables/request/index.d.ts +1 -1
  10. package/dist/composables/request/service.d.ts +73 -0
  11. package/dist/composables/request/types.d.ts +50 -13
  12. package/dist/composables/share/index.d.ts +38 -0
  13. package/dist/composables/theme/appearance.d.ts +30 -4
  14. package/dist/composables/theme/font.d.ts +23 -0
  15. package/dist/composables/theme/index.d.ts +19 -6
  16. package/dist/composables/theme/palette.d.ts +23 -0
  17. package/dist/composables/theme/typography.d.ts +9 -1
  18. package/dist/composables/utils/index.d.ts +120 -13
  19. package/dist/directives/copy.d.ts +5 -0
  20. package/dist/hlw.d.ts +10 -0
  21. package/dist/index.js +310 -199
  22. package/dist/index.mjs +311 -200
  23. package/dist/stores/theme.d.ts +3 -0
  24. package/package.json +2 -2
  25. package/src/app.ts +20 -3
  26. package/src/components/hlw-add-mini/README.md +10 -11
  27. package/src/components/hlw-add-mini/index.vue +93 -66
  28. package/src/components/hlw-button/index.vue +33 -18
  29. package/src/components/hlw-custom/hlw-custom.vue +118 -0
  30. package/src/composables/ad/index.ts +136 -62
  31. package/src/composables/device/index.ts +32 -2
  32. package/src/composables/index.ts +18 -1
  33. package/src/composables/msg/index.ts +70 -16
  34. package/src/composables/navigator/index.ts +45 -1
  35. package/src/composables/refs/index.ts +27 -4
  36. package/src/composables/request/client.ts +149 -0
  37. package/src/composables/request/index.ts +1 -1
  38. package/src/composables/request/service.ts +72 -0
  39. package/src/composables/request/types.ts +53 -13
  40. package/src/composables/share/index.ts +48 -0
  41. package/src/composables/theme/appearance.ts +31 -4
  42. package/src/composables/theme/font.ts +23 -0
  43. package/src/composables/theme/index.ts +23 -7
  44. package/src/composables/theme/palette.ts +32 -0
  45. package/src/composables/theme/typography.ts +9 -1
  46. package/src/composables/utils/index.ts +227 -127
  47. package/src/directives/copy.ts +31 -19
  48. package/src/hlw.ts +11 -0
  49. package/src/stores/theme.ts +28 -5
@@ -1,191 +1,291 @@
1
1
  /**
2
2
  * 小程序通用工具。
3
+ * 包含查询字符串参数拼接、数据转换、剪贴板交互、授权及图片/视频等多媒体资源下载保存。
3
4
  */
4
5
 
6
+ /**
7
+ * 文件下载选项配置接口。
8
+ */
5
9
  export interface DownloadOpt {
10
+ /** 文件的网络下载链接 */
6
11
  url: string;
12
+ /** 指定文件保存的本地目标路径,可选 */
7
13
  path?: string;
14
+ /** 请求的自定义 HTTP 请求头 */
8
15
  header?: Record<string, string>;
16
+ /** 下载进度更新的回调函数 */
9
17
  progress?: (value: number, done: number, total: number) => void;
10
18
  }
11
19
 
20
+ /**
21
+ * 文件下载结果接口。
22
+ */
12
23
  export interface DownloadRes {
24
+ /** 是否成功下载 */
13
25
  ok: boolean;
26
+ /** 临时或保存后的本地文件路径 */
14
27
  path?: string;
28
+ /** 服务器返回的 HTTP 状态码 */
15
29
  code?: number;
30
+ /** 错误或提示信息 */
16
31
  msg?: string;
17
32
  }
18
33
 
19
- function withQuery(url: string, qs: string) {
34
+ /**
35
+ * 拼接 URL 与 Query String。
36
+ * 会根据原 URL 中是否包含问号,自动拼接 `?` 或 `&`。
37
+ * @param url 原 URL
38
+ * @param qs 格式化后的 query 字符串(如 'a=1&b=2')
39
+ * @returns 拼接后的完整 URL
40
+ */
41
+ export function withQuery(url: string, qs: string): string {
20
42
  if (!qs) return url;
21
43
  return `${url}${url.includes("?") ? "&" : "?"}${qs}`;
22
44
  }
23
45
 
24
- function toQuery(data: Record<string, unknown>) {
46
+ /**
47
+ * 将键值对对象转换为 URL 编码的 Query String。
48
+ * 会自动过滤值为 `undefined` 或 `null` 的键。
49
+ * @param data 需要转换的键值对数据对象
50
+ * @returns 格式化后的 Query String 字符串
51
+ */
52
+ export function toQuery(data: Record<string, unknown>): string {
25
53
  return Object.entries(data)
26
54
  .filter(([, val]) => val !== undefined && val !== null)
27
55
  .map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(val))}`)
28
56
  .join("&");
29
57
  }
30
58
 
31
- function signText(url: string) {
59
+ /**
60
+ * 按照特定规则对 URL 或者是 Query 参数进行排序并构造签名文本。
61
+ * 常用于 API 请求签名的加密前置数据处理。
62
+ * @param url 需要签名的完整 URL 或路径
63
+ * @returns 排序拼接后的签名基准字符串
64
+ */
65
+ export function signText(url: string): string {
32
66
  const [path, qs] = url.split("?");
33
67
  return qs ? `${qs.split("&").filter(Boolean).sort().join("&")}&` : `${path}&`;
34
68
  }
35
69
 
36
- function toNumber(val: unknown, def: number): number {
70
+ /**
71
+ * 安全转换未知值到数字类型,若转换失败则返回默认值。
72
+ * @param val 待转换的值
73
+ * @param def 默认数字
74
+ * @returns 转换后的数字或默认值
75
+ */
76
+ export function toNumber(val: unknown, def: number): number {
37
77
  const next = Number(val);
38
78
  return Number.isFinite(next) ? next : def;
39
79
  }
40
80
 
41
- function toBoolean(val: unknown, def: boolean): boolean {
81
+ /**
82
+ * 安全转换未知值到布尔值类型,若转换失败则返回默认值。
83
+ * 兼容特殊数值(如 0, "0", "false" 视为 false;1, "1", "true" 视为 true)。
84
+ * @param val 待转换的值
85
+ * @param def 默认布尔值
86
+ * @returns 转换后的布尔值或默认值
87
+ */
88
+ export function toBoolean(val: unknown, def: boolean): boolean {
42
89
  if (typeof val === "boolean") return val;
43
- if (val === 0 || val === "0") return false;
44
- if (val === 1 || val === "1") return true;
90
+ if (val === 0 || val === "0" || val === "false") return false;
91
+ if (val === 1 || val === "1" || val === "true") return true;
45
92
  return def;
46
93
  }
47
94
 
48
- export function useUtils() {
49
- function copy(text: string, tip = true): Promise<boolean> {
50
- return new Promise((ok) => {
51
- uni.setClipboardData({
52
- data: text,
53
- showToast: false,
54
- success: () => {
55
- if (tip) {
56
- uni.showToast({ title: "复制成功", icon: "none", duration: 1500 });
57
- }
58
- ok(true);
59
- },
60
- fail: () => ok(false),
61
- });
95
+ /**
96
+ * 复制文本内容至剪贴板。
97
+ * @param text 需要复制的文本
98
+ * @param tip 是否在成功时显示 "复制成功" 的 Toast 提示,默认 true
99
+ * @returns 是否复制成功
100
+ */
101
+ export function copy(text: string, tip = true): Promise<boolean> {
102
+ return new Promise((resolve) => {
103
+ uni.setClipboardData({
104
+ data: text,
105
+ showToast: false,
106
+ success: () => {
107
+ if (tip) {
108
+ uni.showToast({ title: "复制成功", icon: "none", duration: 1500 });
109
+ }
110
+ resolve(true);
111
+ },
112
+ fail: () => resolve(false),
62
113
  });
63
- }
114
+ });
115
+ }
64
116
 
65
- function paste(): Promise<string> {
66
- return new Promise((ok) => {
67
- uni.getClipboardData({
68
- success: (res) => ok(res.data),
69
- fail: () => ok(""),
70
- });
117
+ /**
118
+ * 从系统剪贴板中读取文本内容。
119
+ * @returns 剪贴板文本,若读取失败或无内容返回空字符串
120
+ */
121
+ export function paste(): Promise<string> {
122
+ return new Promise((resolve) => {
123
+ uni.getClipboardData({
124
+ success: (res) => resolve(res.data),
125
+ fail: () => resolve(""),
71
126
  });
72
- }
127
+ });
128
+ }
73
129
 
74
- function auth() {
75
- uni.showModal({
76
- title: "提示",
77
- content: "需要授权相册权限",
78
- confirmText: "去设置",
79
- success: (res) => {
80
- if (res.confirm) uni.openSetting();
130
+ /**
131
+ * 引导用户进行系统相册权限授权提示弹窗。
132
+ */
133
+ export function auth(): void {
134
+ uni.showModal({
135
+ title: "提示",
136
+ content: "需要授权相册权限",
137
+ confirmText: "去设置",
138
+ success: (res) => {
139
+ if (res.confirm) uni.openSetting();
140
+ },
141
+ });
142
+ }
143
+
144
+ /**
145
+ * 保存本地临时图片文件到系统相册中。
146
+ * 若无权限会自动调起 `auth()` 引导用户去设置页开启权限。
147
+ * @param path 本地临时图片路径 (如 wxfile://xxx, http://tmp/xxx)
148
+ * @returns 保存是否成功
149
+ */
150
+ export function saveImage(path: string): Promise<boolean> {
151
+ return new Promise((resolve) => {
152
+ uni.saveImageToPhotosAlbum({
153
+ filePath: path,
154
+ success: () => {
155
+ uni.showToast({ title: "保存成功", icon: "success" });
156
+ resolve(true);
157
+ },
158
+ fail: (err) => {
159
+ const msg = String(err.errMsg || "");
160
+ if (msg.includes("auth deny") || msg.includes("authorize")) {
161
+ auth();
162
+ } else {
163
+ uni.showToast({ title: "保存失败", icon: "none" });
164
+ }
165
+ resolve(false);
81
166
  },
82
167
  });
83
- }
168
+ });
169
+ }
84
170
 
85
- function saveImage(path: string): Promise<boolean> {
86
- return new Promise((ok) => {
87
- uni.saveImageToPhotosAlbum({
88
- filePath: path,
89
- success: () => {
90
- uni.showToast({ title: "保存成功", icon: "success" });
91
- ok(true);
92
- },
93
- fail: (err) => {
94
- const msg = String(err.errMsg || "");
95
- if (msg.includes("auth deny") || msg.includes("authorize")) {
96
- auth();
97
- } else {
98
- uni.showToast({ title: "保存失败", icon: "none" });
99
- }
100
- ok(false);
101
- },
102
- });
171
+ /**
172
+ * 保存本地临时视频文件到系统相册中。
173
+ * 若无权限会自动引导授权。
174
+ * @param path 本地临时视频路径
175
+ * @returns 保存是否成功
176
+ */
177
+ export function saveVideoFile(path: string): Promise<boolean> {
178
+ return new Promise((resolve) => {
179
+ uni.saveVideoToPhotosAlbum({
180
+ filePath: path,
181
+ success: () => {
182
+ uni.showToast({ title: "保存成功", icon: "success" });
183
+ resolve(true);
184
+ },
185
+ fail: (err) => {
186
+ const msg = String(err.errMsg || "");
187
+ if (msg.includes("auth deny") || msg.includes("authorize")) {
188
+ auth();
189
+ } else {
190
+ uni.showToast({ title: "保存失败", icon: "none" });
191
+ }
192
+ resolve(false);
193
+ },
103
194
  });
104
- }
195
+ });
196
+ }
105
197
 
106
- function saveVideo(path: string): Promise<boolean> {
107
- return new Promise((ok) => {
108
- uni.saveVideoToPhotosAlbum({
109
- filePath: path,
110
- success: () => {
111
- uni.showToast({ title: "保存成功", icon: "success" });
112
- ok(true);
113
- },
114
- fail: (err) => {
115
- const msg = String(err.errMsg || "");
116
- if (msg.includes("auth deny") || msg.includes("authorize")) {
117
- auth();
118
- } else {
119
- uni.showToast({ title: "保存失败", icon: "none" });
120
- }
121
- ok(false);
122
- },
123
- });
198
+ /**
199
+ * 基于 UniApp 下载网络资源至本地临时目录中。
200
+ * @param opt 下载参数配置项
201
+ * @returns 下载结果 Promise
202
+ */
203
+ export function download(opt: DownloadOpt): Promise<DownloadRes> {
204
+ return new Promise((resolve) => {
205
+ const task = uni.downloadFile({
206
+ url: opt.url,
207
+ filePath: opt.path,
208
+ header: opt.header,
209
+ success: (res) => {
210
+ if (res.statusCode === 200) {
211
+ resolve({ ok: true, path: res.tempFilePath, code: res.statusCode });
212
+ } else {
213
+ resolve({ ok: false, code: res.statusCode, msg: `下载失败,状态码:${res.statusCode}` });
214
+ }
215
+ },
216
+ fail: (err) => resolve({ ok: false, msg: err.errMsg }),
124
217
  });
125
- }
126
218
 
127
- function download(opt: DownloadOpt): Promise<DownloadRes> {
128
- return new Promise((ok) => {
129
- const task = uni.downloadFile({
130
- url: opt.url,
131
- filePath: opt.path,
132
- header: opt.header,
133
- success: (res) => {
134
- if (res.statusCode === 200) {
135
- ok({ ok: true, path: res.tempFilePath, code: res.statusCode });
136
- } else {
137
- ok({ ok: false, code: res.statusCode, msg: `下载失败,状态码:${res.statusCode}` });
138
- }
139
- },
140
- fail: (err) => ok({ ok: false, msg: err.errMsg }),
219
+ if (opt.progress) {
220
+ task.onProgressUpdate((res) => {
221
+ opt.progress!(res.progress, res.totalBytesWritten, res.totalBytesExpectedToWrite);
141
222
  });
223
+ }
224
+ });
225
+ }
142
226
 
143
- if (opt.progress) {
144
- task.onProgressUpdate((res) => {
145
- opt.progress!(res.progress, res.totalBytesWritten, res.totalBytesExpectedToWrite);
146
- });
147
- }
148
- });
149
- }
227
+ /**
228
+ * 下载并保存网络图片至系统相册。
229
+ * 过程中包含 Loading 提示以及权限处理。
230
+ * @param url 网络图片地址
231
+ * @param progress 可选的下载进度更新回调
232
+ * @returns 操作是否成功
233
+ */
234
+ export async function saveImageUrl(url: string, progress?: (value: number) => void): Promise<boolean> {
235
+ try {
236
+ uni.showLoading({ title: "下载中...", mask: true });
237
+ const res = await download({ url, progress: progress ? (value) => progress(value) : undefined });
238
+ uni.hideLoading();
150
239
 
151
- async function saveImageUrl(url: string, progress?: (value: number) => void): Promise<boolean> {
152
- try {
153
- uni.showLoading({ title: "下载中...", mask: true });
154
- const res = await download({ url, progress: progress ? (value) => progress(value) : undefined });
155
- uni.hideLoading();
156
-
157
- if (!res.ok || !res.path) {
158
- uni.showToast({ title: res.msg || "下载失败", icon: "none" });
159
- return false;
160
- }
161
-
162
- return await saveImage(res.path);
163
- } catch {
164
- uni.hideLoading();
165
- uni.showToast({ title: "操作失败", icon: "none" });
240
+ if (!res.ok || !res.path) {
241
+ uni.showToast({ title: res.msg || "下载失败", icon: "none" });
166
242
  return false;
167
243
  }
244
+
245
+ return await saveImage(res.path);
246
+ } catch {
247
+ uni.hideLoading();
248
+ uni.showToast({ title: "操作失败", icon: "none" });
249
+ return false;
168
250
  }
251
+ }
169
252
 
170
- async function saveVideoUrl(url: string, progress?: (value: number) => void): Promise<boolean> {
171
- try {
172
- uni.showLoading({ title: "下载中...", mask: true });
173
- const res = await download({ url, progress: progress ? (value) => progress(value) : undefined });
174
- uni.hideLoading();
175
-
176
- if (!res.ok || !res.path) {
177
- uni.showToast({ title: res.msg || "下载失败", icon: "none" });
178
- return false;
179
- }
180
-
181
- return await saveVideo(res.path);
182
- } catch {
183
- uni.hideLoading();
184
- uni.showToast({ title: "操作失败", icon: "none" });
253
+ /**
254
+ * 下载并保存网络视频至系统相册。
255
+ * 过程中包含 Loading 提示以及权限处理。
256
+ * @param url 网络视频地址
257
+ * @param progress 可选的下载进度更新回调
258
+ * @returns 操作是否成功
259
+ */
260
+ export async function saveVideoUrl(url: string, progress?: (value: number) => void): Promise<boolean> {
261
+ try {
262
+ uni.showLoading({ title: "下载中...", mask: true });
263
+ const res = await download({ url, progress: progress ? (value) => progress(value) : undefined });
264
+ uni.hideLoading();
265
+
266
+ if (!res.ok || !res.path) {
267
+ uni.showToast({ title: res.msg || "下载失败", icon: "none" });
185
268
  return false;
186
269
  }
270
+
271
+ return await saveVideoFile(res.path);
272
+ } catch {
273
+ uni.hideLoading();
274
+ uni.showToast({ title: "操作失败", icon: "none" });
275
+ return false;
187
276
  }
277
+ }
188
278
 
279
+ /**
280
+ * 获取系统通用工具方法的 hook。
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * const { copy, saveImageUrl } = useUtils();
285
+ * copy('hello');
286
+ * ```
287
+ */
288
+ export function useUtils() {
189
289
  return {
190
290
  withQuery,
191
291
  toQuery,
@@ -195,7 +295,7 @@ export function useUtils() {
195
295
  copy,
196
296
  paste,
197
297
  saveImage,
198
- saveVideo,
298
+ saveVideoFile,
199
299
  download,
200
300
  saveImageUrl,
201
301
  saveVideoUrl,
@@ -1,50 +1,62 @@
1
1
  /**
2
- * v-copy 指令 - 点击自动复制
2
+ * v-copy 指令 - 点击自动复制文本
3
3
  *
4
4
  * 用法:
5
5
  * <view v-copy="text">...</view>
6
6
  * <text v-copy="userId">{{ userId }}</text>
7
7
  */
8
- import type { Directive, DirectiveBinding, VNode } from 'vue'
8
+ import type { Directive, DirectiveBinding, VNode } from 'vue';
9
9
 
10
10
  /**
11
- * 将文本复制到剪贴板,并显示成功提示。
11
+ * 将文本复制到系统剪贴板,并显示成功提示。
12
+ *
13
+ * @param data 待复制的字符串
12
14
  */
13
15
  function copyText(data: string) {
14
16
  uni.setClipboardData({
15
17
  data,
16
- showToast: false,
18
+ showToast: false, // 禁用系统默认 Toast 提示,使用自定义的无图标 Toast
17
19
  success: () => uni.showToast({ title: '复制成功', icon: 'none', duration: 1500 }),
18
- })
20
+ });
19
21
  }
20
22
 
21
23
  /**
22
- * vnode 注入 onTap 事件,在点击时执行复制逻辑。
24
+ * 向虚拟节点 (VNode) props 中注入 onTap 事件。
25
+ * 当用户点击该组件时,执行原有的点击事件后自动触发复制绑定值。
26
+ *
27
+ * @param vnode 虚拟 DOM 节点
28
+ * @param binding 指令绑定对象
23
29
  */
24
30
  function injectTap(vnode: VNode, binding: DirectiveBinding) {
25
- if (!vnode.props) (vnode as any).props = {}
26
- const props = vnode.props as Record<string, any>
27
- const prev = props.onTap
31
+ if (!vnode.props) (vnode as any).props = {};
32
+ const props = vnode.props as Record<string, any>;
33
+ const prev = props.onTap;
28
34
 
29
35
  props.onTap = (e: any) => {
30
- prev?.(e)
31
- const value = binding.value
32
- if (value == null || value === '') return
33
- copyText(String(value))
34
- }
36
+ prev?.(e);
37
+ const value = binding.value;
38
+ if (value == null || value === '') return;
39
+ copyText(String(value));
40
+ };
35
41
  }
36
42
 
43
+ /**
44
+ * 自定义 Vue 指令 `v-copy`
45
+ *
46
+ * 在元素/组件被创建或更新前,拦截其点击事件并注入复制剪贴板逻辑。
47
+ */
37
48
  export const vCopy: Directive = {
38
49
  /**
39
- * 在指令创建时注入点击事件。
50
+ * 在指令创建时注入点击事件拦截器。
40
51
  */
41
52
  created(el: any, binding: DirectiveBinding, vnode: VNode) {
42
- injectTap(vnode, binding)
53
+ injectTap(vnode, binding);
43
54
  },
44
55
  /**
45
- * 在绑定值更新前重新注入点击事件。
56
+ * 在绑定值更新前重新注入点击事件拦截器,以获取最新的绑定值。
46
57
  */
47
58
  beforeUpdate(el: any, binding: DirectiveBinding, vnode: VNode) {
48
- injectTap(vnode, binding)
59
+ injectTap(vnode, binding);
49
60
  },
50
- }
61
+ };
62
+
package/src/hlw.ts CHANGED
@@ -7,16 +7,26 @@ import { useDevice, type DeviceInfo } from '@/composables/device';
7
7
  import { useRequest } from '@/composables/request';
8
8
  import { useUtils } from '@/composables/utils';
9
9
 
10
+ /**
11
+ * 全局 hlw 实例接口定义,聚合了框架的核心能力。
12
+ */
10
13
  export interface HlwInstance {
14
+ /** 统一的消息提示与模态弹窗管理模块 */
11
15
  $msg: ReturnType<typeof useMsg>;
16
+ /** 当前运行设备的详细系统信息 */
12
17
  $device: DeviceInfo;
18
+ /** 全局配置的统一 HTTP 请求实例 */
13
19
  $request: ReturnType<typeof useRequest>;
20
+ /** 聚合的常用小程序公共工具函数 */
14
21
  $utils: ReturnType<typeof useUtils>;
15
22
  }
16
23
 
17
24
  let _msg: ReturnType<typeof useMsg> | null = null;
18
25
  let _utils: ReturnType<typeof useUtils> | null = null;
19
26
 
27
+ /**
28
+ * 全局单例 `hlw` 实例,各核心模块在首次读取时延迟初始化并缓存。
29
+ */
20
30
  export const hlw: HlwInstance = {
21
31
  /** 延迟创建消息提示实例。 */
22
32
  get $msg() { return (_msg ??= useMsg()); },
@@ -27,3 +37,4 @@ export const hlw: HlwInstance = {
27
37
  /** 延迟创建通用工具实例。 */
28
38
  get $utils() { return (_utils ??= useUtils()); },
29
39
  };
40
+
@@ -16,36 +16,51 @@ import {
16
16
  } from "../composables/theme";
17
17
  import type { FontScale, ThemeColor, Appearance, AppearanceMode } from "../composables/theme";
18
18
 
19
+ /**
20
+ * 统一管理全局主题、字体缩放和外观模式的 Pinia Store。
21
+ */
19
22
  export const useThemeStore = defineStore(
20
23
  "theme",
21
24
  () => {
22
25
  // ─── 字体档位 ────────────────────────────
26
+ /** 当前生效的字体大小档位 */
23
27
  const scale = ref<FontScale>("normal");
24
28
 
29
+ /**
30
+ * 改变并保存当前的字体大小档位。
31
+ *
32
+ * @param s 目标字体大小档位
33
+ */
25
34
  function setScale(s: FontScale) {
26
35
  scale.value = s;
27
36
  uni.setStorageSync(FONT_SCALE_KEY, s);
28
37
  uni.$emit(THEME_CHANGE_EVENT);
29
38
  }
30
39
 
40
+ /** 供界面渲染选择的字体档位选项列表 */
31
41
  const fontOptions = (Object.keys(FONT_PRESETS) as FontScale[]).map((key) => ({
32
42
  value: key,
33
43
  label: FONT_PRESETS[key].label,
34
44
  }));
35
45
 
36
46
  // ─── 主题色 ─────────────────────────────
47
+ /** 当前生效的主题色十六进制值 (Hex Color) */
37
48
  const primaryColor = ref(DEFAULT_THEMES[0].value);
38
49
 
39
- /** 内置预设主题 */
50
+ /** 系统内置的预设主题颜色列表 */
40
51
  const themes = DEFAULT_THEMES;
41
52
 
42
- /** 当前激活的主题信息 */
53
+ /** 当前激活的主题色相关信息 */
43
54
  const activeTheme = computed<ThemeColor>(() =>
44
55
  themes.find((t: ThemeColor) => t.value === primaryColor.value)
45
56
  ?? { label: "自定义", value: primaryColor.value },
46
57
  );
47
58
 
48
- /** 设置主题色(预设或自定义颜色值) */
59
+ /**
60
+ * 更改并保存全局主题色。
61
+ *
62
+ * @param color 颜色 Hex 字符串
63
+ */
49
64
  function setTheme(color: string) {
50
65
  primaryColor.value = color;
51
66
  uni.setStorageSync(THEME_COLOR_KEY, color);
@@ -53,16 +68,23 @@ export const useThemeStore = defineStore(
53
68
  }
54
69
 
55
70
  // ─── 外观模式(light / dark / auto) ─────────
71
+ /** 当前设置的外观显示模式:浅色、深色或自动跟随系统 */
56
72
  const appearance = ref<Appearance>("auto");
57
73
 
74
+ /** 可供选择的外观预设配置列表 */
58
75
  const appearanceOptions = APPEARANCE_PRESETS;
59
76
 
60
- /** 当前实际生效的模式(auto 会被解析为 light/dark) */
77
+ /** 当前最终解析的实际生效模式(auto 会依据系统检测解析为 lightdark) */
61
78
  const appearanceMode = computed<AppearanceMode>(() => resolveAppearance(appearance.value));
62
79
 
63
- /** 是否深色模式(业务组件可直接读取做条件渲染) */
80
+ /** 是否正处于深色模式,可供业务组件在 template 中直接用作条件渲染 */
64
81
  const isDark = computed(() => appearanceMode.value === "dark");
65
82
 
83
+ /**
84
+ * 改变外观设置模式。
85
+ *
86
+ * @param a 目标模式
87
+ */
66
88
  function setAppearance(a: Appearance) {
67
89
  appearance.value = a;
68
90
  uni.setStorageSync(APPEARANCE_KEY, a);
@@ -89,3 +111,4 @@ export const useThemeStore = defineStore(
89
111
  },
90
112
  { unistorage: true },
91
113
  );
114
+