@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.
- package/dist/app.d.ts +12 -1
- package/dist/composables/ad/index.d.ts +26 -0
- package/dist/composables/device/index.d.ts +14 -0
- package/dist/composables/index.d.ts +1 -1
- package/dist/composables/msg/index.d.ts +44 -3
- package/dist/composables/navigator/index.d.ts +37 -1
- package/dist/composables/refs/index.d.ts +15 -1
- package/dist/composables/request/client.d.ts +87 -0
- package/dist/composables/request/index.d.ts +1 -1
- package/dist/composables/request/service.d.ts +73 -0
- package/dist/composables/request/types.d.ts +50 -13
- package/dist/composables/share/index.d.ts +38 -0
- package/dist/composables/theme/appearance.d.ts +30 -4
- package/dist/composables/theme/font.d.ts +23 -0
- package/dist/composables/theme/index.d.ts +19 -6
- package/dist/composables/theme/palette.d.ts +23 -0
- package/dist/composables/theme/typography.d.ts +9 -1
- package/dist/composables/utils/index.d.ts +120 -13
- package/dist/directives/copy.d.ts +5 -0
- package/dist/hlw.d.ts +10 -0
- package/dist/index.js +310 -199
- package/dist/index.mjs +311 -200
- package/dist/stores/theme.d.ts +3 -0
- package/package.json +2 -2
- package/src/app.ts +20 -3
- package/src/components/hlw-add-mini/README.md +10 -11
- package/src/components/hlw-add-mini/index.vue +93 -66
- package/src/components/hlw-button/index.vue +33 -18
- package/src/components/hlw-custom/hlw-custom.vue +118 -0
- package/src/composables/ad/index.ts +136 -62
- package/src/composables/device/index.ts +32 -2
- package/src/composables/index.ts +18 -1
- package/src/composables/msg/index.ts +70 -16
- package/src/composables/navigator/index.ts +45 -1
- package/src/composables/refs/index.ts +27 -4
- package/src/composables/request/client.ts +149 -0
- package/src/composables/request/index.ts +1 -1
- package/src/composables/request/service.ts +72 -0
- package/src/composables/request/types.ts +53 -13
- package/src/composables/share/index.ts +48 -0
- package/src/composables/theme/appearance.ts +31 -4
- package/src/composables/theme/font.ts +23 -0
- package/src/composables/theme/index.ts +23 -7
- package/src/composables/theme/palette.ts +32 -0
- package/src/composables/theme/typography.ts +9 -1
- package/src/composables/utils/index.ts +227 -127
- package/src/directives/copy.ts +31 -19
- package/src/hlw.ts +11 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
ok
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
uni.showToast({ title: "
|
|
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
|
-
|
|
298
|
+
saveVideoFile,
|
|
199
299
|
download,
|
|
200
300
|
saveImageUrl,
|
|
201
301
|
saveVideoUrl,
|
package/src/directives/copy.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
+
|
package/src/stores/theme.ts
CHANGED
|
@@ -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
|
-
/**
|
|
77
|
+
/** 当前最终解析的实际生效模式(auto 会依据系统检测解析为 light 或 dark) */
|
|
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
|
+
|