@hlw-uni/mp-vue 2.1.52 → 2.1.54
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 +2 -20
- package/dist/composables/device/index.d.ts +5 -70
- package/dist/composables/index.d.ts +5 -14
- package/dist/composables/navigator/index.d.ts +15 -39
- package/dist/composables/request/client.d.ts +21 -0
- package/dist/composables/request/index.d.ts +6 -0
- package/dist/composables/request/service.d.ts +30 -0
- package/dist/composables/{http → request}/types.d.ts +0 -3
- package/dist/composables/share/index.d.ts +7 -66
- package/dist/composables/utils/index.d.ts +25 -32
- package/dist/hlw.d.ts +2 -4
- package/dist/index.d.ts +2 -3
- package/dist/index.js +484 -1256
- package/dist/index.mjs +483 -1255
- package/package.json +2 -1
- package/src/app.ts +3 -143
- package/src/components/hlw-ad/index.vue +2 -2
- package/src/components/hlw-page/index.vue +1 -1
- package/src/composables/device/index.ts +110 -83
- package/src/composables/index.ts +9 -39
- package/src/composables/navigator/index.ts +77 -77
- package/src/composables/request/client.ts +204 -0
- package/src/composables/request/index.ts +15 -0
- package/src/composables/request/service.ts +54 -0
- package/src/composables/{http → request}/types.ts +0 -4
- package/src/composables/share/index.ts +64 -168
- package/src/composables/theme/index.ts +4 -2
- package/src/composables/theme/palette.ts +22 -4
- package/src/composables/utils/index.ts +131 -95
- package/src/hlw.ts +6 -14
- package/src/index.ts +2 -3
- package/dist/composables/_internal/unwrap.d.ts +0 -15
- package/dist/composables/ad/index.d.ts +0 -78
- package/dist/composables/algo/index.d.ts +0 -7
- package/dist/composables/algo/uuid.d.ts +0 -17
- package/dist/composables/color/index.d.ts +0 -8
- package/dist/composables/contact/index.d.ts +0 -32
- package/dist/composables/format/index.d.ts +0 -9
- package/dist/composables/http/client.d.ts +0 -66
- package/dist/composables/http/index.d.ts +0 -8
- package/dist/composables/loading/index.d.ts +0 -7
- package/dist/composables/page-meta/index.d.ts +0 -18
- package/dist/composables/storage/index.d.ts +0 -16
- package/dist/composables/validate/index.d.ts +0 -12
- package/dist/error.d.ts +0 -1
- package/src/composables/_internal/unwrap.ts +0 -19
- package/src/composables/ad/index.ts +0 -412
- package/src/composables/algo/index.ts +0 -7
- package/src/composables/algo/uuid.ts +0 -27
- package/src/composables/color/index.ts +0 -44
- package/src/composables/contact/index.ts +0 -92
- package/src/composables/format/index.ts +0 -48
- package/src/composables/http/client.ts +0 -237
- package/src/composables/http/index.ts +0 -8
- package/src/composables/http/useRequest.ts +0 -107
- package/src/composables/loading/index.ts +0 -23
- package/src/composables/page-meta/index.ts +0 -49
- package/src/composables/storage/index.ts +0 -76
- package/src/composables/validate/index.ts +0 -58
- package/src/error.ts +0 -5
- /package/dist/composables/{http → request}/adapters/alist.d.ts +0 -0
- /package/dist/composables/{http → request}/adapters/base.d.ts +0 -0
- /package/dist/composables/{http → request}/adapters/cos.d.ts +0 -0
- /package/dist/composables/{http → request}/adapters/index.d.ts +0 -0
- /package/dist/composables/{http → request}/adapters/oss.d.ts +0 -0
- /package/dist/composables/{http → request}/adapters/qiniu.d.ts +0 -0
- /package/src/composables/{http → request}/adapters/alist.ts +0 -0
- /package/src/composables/{http → request}/adapters/base.ts +0 -0
- /package/src/composables/{http → request}/adapters/cos.ts +0 -0
- /package/src/composables/{http → request}/adapters/index.ts +0 -0
- /package/src/composables/{http → request}/adapters/oss.ts +0 -0
- /package/src/composables/{http → request}/adapters/qiniu.ts +0 -0
|
@@ -1,189 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* - useShare(config) —— SDK 层:直接注册 onShareAppMessage / onShareTimeline 钩子
|
|
6
|
-
* - useShareConfig(...) —— 业务层:lazy load 后端配置 + cache + fallback resolver
|
|
7
|
-
*/
|
|
8
|
-
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
|
|
9
|
-
import { ref } from 'vue';
|
|
10
|
-
import { unwrapPayload, type AdapterPayload } from '../_internal/unwrap';
|
|
1
|
+
import {
|
|
2
|
+
onShareAppMessage as registerShareAppMessage,
|
|
3
|
+
onShareTimeline as registerShareTimeline,
|
|
4
|
+
} from "@dcloudio/uni-app";
|
|
11
5
|
|
|
12
|
-
export interface
|
|
13
|
-
/** 分享标题 */
|
|
6
|
+
export interface ShareConfig {
|
|
14
7
|
title?: string;
|
|
15
|
-
/** 分享路径,必须是以 / 开头的完整路径 */
|
|
16
8
|
path?: string;
|
|
17
|
-
/** 自定义图片路径 */
|
|
18
9
|
imageUrl?: string;
|
|
19
10
|
}
|
|
20
11
|
|
|
21
|
-
export
|
|
22
|
-
/** 朋友圈分享标题 */
|
|
23
|
-
title?: string;
|
|
24
|
-
/** 页面携带的 query 参数 */
|
|
25
|
-
query?: string;
|
|
26
|
-
/** 自定义图片路径 */
|
|
27
|
-
imageUrl?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ShareConfig extends ShareAppMessageContent {
|
|
31
|
-
/** 朋友圈专属配置,不填则复用朋友分享配置 */
|
|
32
|
-
timeline?: ShareTimelineContent;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* 分享来源。
|
|
37
|
-
* - `button` 页面内转发按钮
|
|
38
|
-
* - `menu` 右上角菜单
|
|
39
|
-
*/
|
|
40
|
-
export type ShareFrom = 'button' | 'menu';
|
|
12
|
+
export type ShareConfigResolver = ShareConfig | (() => ShareConfig);
|
|
41
13
|
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*/
|
|
47
|
-
export function useShare(config: ShareConfig | ShareConfigResolver) {
|
|
48
|
-
/**
|
|
49
|
-
* 根据分享来源解析最终分享配置。
|
|
50
|
-
*/
|
|
51
|
-
const resolve = (from: ShareFrom): ShareConfig =>
|
|
52
|
-
typeof config === 'function' ? config(from) : config;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* 注册分享给朋友的回调。
|
|
56
|
-
*/
|
|
57
|
-
onShareAppMessage((options: { from?: string } | undefined) => {
|
|
58
|
-
const resolved = resolve((options?.from as ShareFrom) ?? 'menu');
|
|
59
|
-
const payload: ShareAppMessageContent = {};
|
|
60
|
-
if (resolved.title !== undefined) payload.title = resolved.title;
|
|
61
|
-
if (resolved.path !== undefined) payload.path = resolved.path;
|
|
62
|
-
if (resolved.imageUrl !== undefined) payload.imageUrl = resolved.imageUrl;
|
|
63
|
-
return payload;
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 注册分享到朋友圈的回调。
|
|
68
|
-
*/
|
|
69
|
-
onShareTimeline(() => {
|
|
70
|
-
const resolved = resolve('menu');
|
|
71
|
-
const timeline = resolved.timeline ?? {};
|
|
72
|
-
const payload: ShareTimelineContent = {};
|
|
73
|
-
const title = timeline.title ?? resolved.title;
|
|
74
|
-
const imageUrl = timeline.imageUrl ?? resolved.imageUrl;
|
|
75
|
-
if (title !== undefined) payload.title = title;
|
|
76
|
-
if (timeline.query !== undefined) payload.query = timeline.query;
|
|
77
|
-
if (imageUrl !== undefined) payload.imageUrl = imageUrl;
|
|
78
|
-
return payload;
|
|
79
|
-
});
|
|
14
|
+
export interface ShareHandlers {
|
|
15
|
+
onShareAppMessage: (config?: ShareConfigResolver) => void;
|
|
16
|
+
onShareTimeline: (config?: ShareConfigResolver) => void;
|
|
17
|
+
showShareMenu: () => void;
|
|
80
18
|
}
|
|
81
19
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
*
|
|
85
|
-
* 后端按页面 key 聚合返回各页 title / image,前端拉一次缓存全局共用。
|
|
86
|
-
* 返回的 resolver 同时喂给 onShareAppMessage 和 onShareTimeline。
|
|
87
|
-
*
|
|
88
|
-
* 使用方式:
|
|
89
|
-
*
|
|
90
|
-
* 1. App.vue / bootstrap 注入回调:
|
|
91
|
-
*
|
|
92
|
-
* setConfigShare({
|
|
93
|
-
* getConfig: async () => {
|
|
94
|
-
* const res = await getShareConfig();
|
|
95
|
-
* return res.code === 1 ? res.data : null;
|
|
96
|
-
* },
|
|
97
|
-
* });
|
|
98
|
-
*
|
|
99
|
-
* 2. 任意页面 setup:
|
|
100
|
-
*
|
|
101
|
-
* const share = useShareConfig("index", {
|
|
102
|
-
* title: "兜底标题",
|
|
103
|
-
* path: "/pages/index/index",
|
|
104
|
-
* });
|
|
105
|
-
* onShareAppMessage(share);
|
|
106
|
-
* onShareTimeline(share);
|
|
107
|
-
* ============================================================ */
|
|
108
|
-
|
|
109
|
-
/** 单页分享配置 */
|
|
110
|
-
export interface PageShareItem {
|
|
111
|
-
title: string;
|
|
112
|
-
image: string;
|
|
20
|
+
function resolveConfig(config?: ShareConfigResolver): ShareConfig {
|
|
21
|
+
return typeof config === "function" ? config() : (config ?? {});
|
|
113
22
|
}
|
|
114
23
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
/** 页面声明的兜底文案 / 跳转路径 */
|
|
127
|
-
export interface PageShareFallback {
|
|
128
|
-
/** 后端 title 为空时用 */
|
|
129
|
-
title: string;
|
|
130
|
-
/** 分享跳转路径(必须 / 开头);path 不走后端,由页面自定义 */
|
|
131
|
-
path: string;
|
|
132
|
-
/** 后端 image 为空时用;通常留空让微信自动截图 */
|
|
133
|
-
image?: string;
|
|
24
|
+
function buildPayload(base: ShareConfigResolver, extra?: ShareConfigResolver): ShareConfig {
|
|
25
|
+
const current = {
|
|
26
|
+
...resolveConfig(base),
|
|
27
|
+
...resolveConfig(extra),
|
|
28
|
+
};
|
|
29
|
+
const payload: ShareConfig = {};
|
|
30
|
+
if (current.title) payload.title = current.title;
|
|
31
|
+
if (current.path) payload.path = current.path;
|
|
32
|
+
if (current.imageUrl) payload.imageUrl = current.imageUrl;
|
|
33
|
+
return payload;
|
|
134
34
|
}
|
|
135
35
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
36
|
+
function showShareMenu(): void {
|
|
37
|
+
const api = typeof uni !== "undefined" ? (uni as unknown as {
|
|
38
|
+
showShareMenu?: (options: {
|
|
39
|
+
withShareTicket?: boolean;
|
|
40
|
+
menus?: string[];
|
|
41
|
+
fail?: () => void;
|
|
42
|
+
}) => void;
|
|
43
|
+
}) : undefined;
|
|
44
|
+
|
|
45
|
+
api?.showShareMenu?.({
|
|
46
|
+
withShareTicket: true,
|
|
47
|
+
menus: ["shareAppMessage", "shareTimeline"],
|
|
48
|
+
fail: () => undefined,
|
|
49
|
+
});
|
|
141
50
|
}
|
|
142
51
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
let
|
|
52
|
+
export function useShare(config: ShareConfigResolver = {}): ShareHandlers {
|
|
53
|
+
let appMessageRegistered = false;
|
|
54
|
+
let timelineRegistered = false;
|
|
146
55
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
56
|
+
const onShareAppMessage = (extra?: ShareConfigResolver) => {
|
|
57
|
+
if (appMessageRegistered) return;
|
|
58
|
+
appMessageRegistered = true;
|
|
59
|
+
showShareMenu();
|
|
60
|
+
registerShareAppMessage(() => buildPayload(config, extra));
|
|
61
|
+
};
|
|
153
62
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})
|
|
166
|
-
.catch((e) => {
|
|
167
|
-
console.warn("[useShareConfig] load failed", e);
|
|
168
|
-
})
|
|
169
|
-
.finally(() => {
|
|
170
|
-
sharePending = null;
|
|
63
|
+
const onShareTimeline = (extra?: ShareConfigResolver) => {
|
|
64
|
+
if (timelineRegistered) return;
|
|
65
|
+
timelineRegistered = true;
|
|
66
|
+
showShareMenu();
|
|
67
|
+
registerShareTimeline(() => {
|
|
68
|
+
const payload = buildPayload(config, extra);
|
|
69
|
+
return {
|
|
70
|
+
title: payload.title,
|
|
71
|
+
query: payload.path?.split("?")[1],
|
|
72
|
+
imageUrl: payload.imageUrl,
|
|
73
|
+
};
|
|
171
74
|
});
|
|
172
|
-
|
|
173
|
-
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
onShareAppMessage();
|
|
78
|
+
onShareTimeline();
|
|
174
79
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
export function useShareConfig(pageKey: string, fallback: PageShareFallback) {
|
|
180
|
-
void loadShareConfig();
|
|
181
|
-
return (): SharePayload => {
|
|
182
|
-
const remote = shareCache.value?.[pageKey];
|
|
183
|
-
const title = remote?.title || fallback.title;
|
|
184
|
-
const image = remote?.image || fallback.image;
|
|
185
|
-
const payload: SharePayload = { title, path: fallback.path };
|
|
186
|
-
if (image) payload.imageUrl = image;
|
|
187
|
-
return payload;
|
|
80
|
+
return {
|
|
81
|
+
onShareAppMessage,
|
|
82
|
+
onShareTimeline,
|
|
83
|
+
showShareMenu,
|
|
188
84
|
};
|
|
189
85
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { computed } from "vue";
|
|
2
|
-
import { useColor } from "@/composables/color";
|
|
3
2
|
import { useThemeStore } from "../../stores/theme";
|
|
4
3
|
|
|
5
|
-
const { varsToStyle } = useColor();
|
|
6
4
|
import { getCurrentFontVars } from "./font";
|
|
7
5
|
import { getCurrentThemeVars } from "./palette";
|
|
8
6
|
|
|
@@ -25,6 +23,10 @@ export function buildThemeStyle(): string {
|
|
|
25
23
|
});
|
|
26
24
|
}
|
|
27
25
|
|
|
26
|
+
function varsToStyle(vars: Record<string, string>): string {
|
|
27
|
+
return Object.entries(vars).map(([key, value]) => `${key}:${value}`).join(";") + ";";
|
|
28
|
+
}
|
|
29
|
+
|
|
28
30
|
/**
|
|
29
31
|
* 获取主题样式字符串,用于注入 <page-meta :page-style>。
|
|
30
32
|
*
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { useColor } from "@/composables/color";
|
|
2
|
-
|
|
3
|
-
const { hexToRgba, darkenHex } = useColor();
|
|
4
|
-
|
|
5
1
|
export interface ThemeColor {
|
|
6
2
|
label: string;
|
|
7
3
|
value: string;
|
|
@@ -65,3 +61,25 @@ export function getCurrentThemeVars(): Record<string, string> {
|
|
|
65
61
|
"--info-dark": darkenHex(THEME_SEMANTIC_COLORS.info),
|
|
66
62
|
};
|
|
67
63
|
}
|
|
64
|
+
|
|
65
|
+
const HEX_RE = /^#[0-9a-fA-F]{6}$/;
|
|
66
|
+
|
|
67
|
+
function parseHex(hex: string): [number, number, number] {
|
|
68
|
+
if (!HEX_RE.test(hex)) throw new Error(`Invalid hex color: ${hex}`);
|
|
69
|
+
return [
|
|
70
|
+
parseInt(hex.slice(1, 3), 16),
|
|
71
|
+
parseInt(hex.slice(3, 5), 16),
|
|
72
|
+
parseInt(hex.slice(5, 7), 16),
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function hexToRgba(hex: string, alpha: number): string {
|
|
77
|
+
const [r, g, b] = parseHex(hex);
|
|
78
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function darkenHex(hex: string, amount = 0.15): string {
|
|
82
|
+
const [r, g, b] = parseHex(hex);
|
|
83
|
+
const darken = (value: number) => Math.max(0, Math.round(value * (1 - amount)));
|
|
84
|
+
return `#${darken(r).toString(16).padStart(2, "0")}${darken(g).toString(16).padStart(2, "0")}${darken(b).toString(16).padStart(2, "0")}`;
|
|
85
|
+
}
|
|
@@ -2,160 +2,183 @@
|
|
|
2
2
|
* 小程序通用工具。
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export interface
|
|
6
|
-
/** 下载地址 */
|
|
5
|
+
export interface DownloadOpt {
|
|
7
6
|
url: string;
|
|
8
|
-
|
|
9
|
-
filePath?: string;
|
|
10
|
-
/** HTTP 请求头,可选 */
|
|
7
|
+
path?: string;
|
|
11
8
|
header?: Record<string, string>;
|
|
12
|
-
|
|
13
|
-
onProgress?: (progress: number, totalBytesWritten: number, totalBytesExpectedToWrite: number) => void;
|
|
9
|
+
progress?: (value: number, done: number, total: number) => void;
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
/** 状态码 */
|
|
22
|
-
statusCode?: number;
|
|
23
|
-
/** 错误信息 */
|
|
24
|
-
errMsg?: string;
|
|
12
|
+
export interface DownloadRes {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
path?: string;
|
|
15
|
+
code?: number;
|
|
16
|
+
msg?: string;
|
|
25
17
|
}
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
19
|
+
function withQuery(url: string, qs: string) {
|
|
20
|
+
if (!qs) return url;
|
|
21
|
+
return `${url}${url.includes("?") ? "&" : "?"}${qs}`;
|
|
22
|
+
}
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
function toQuery(data: Record<string, unknown>) {
|
|
25
|
+
return Object.entries(data)
|
|
26
|
+
.filter(([, val]) => val !== undefined && val !== null)
|
|
27
|
+
.map(([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(val))}`)
|
|
28
|
+
.join("&");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function signText(url: string) {
|
|
32
|
+
const [path, qs] = url.split("?");
|
|
33
|
+
return qs ? `${qs.split("&").filter(Boolean).sort().join("&")}&` : `${path}&`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function toNumber(val: unknown, def: number): number {
|
|
37
|
+
const next = Number(val);
|
|
38
|
+
return Number.isFinite(next) ? next : def;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function toBoolean(val: unknown, def: boolean): boolean {
|
|
42
|
+
if (typeof val === "boolean") return val;
|
|
43
|
+
if (val === 0 || val === "0") return false;
|
|
44
|
+
if (val === 1 || val === "1") return true;
|
|
45
|
+
return def;
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
export function useUtils() {
|
|
49
|
+
function copy(text: string, tip = true): Promise<boolean> {
|
|
50
|
+
return new Promise((ok) => {
|
|
49
51
|
uni.setClipboardData({
|
|
50
|
-
data,
|
|
52
|
+
data: text,
|
|
51
53
|
showToast: false,
|
|
52
54
|
success: () => {
|
|
53
|
-
if (
|
|
55
|
+
if (tip) {
|
|
54
56
|
uni.showToast({ title: "复制成功", icon: "none", duration: 1500 });
|
|
55
57
|
}
|
|
56
|
-
|
|
58
|
+
ok(true);
|
|
57
59
|
},
|
|
58
|
-
fail: () =>
|
|
60
|
+
fail: () => ok(false),
|
|
59
61
|
});
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
/**
|
|
64
|
-
* 从事件 dataset 的 data-copy 字段读取文本并复制。
|
|
65
|
-
*/
|
|
66
|
-
function copyFromEvent(event: TapEvent) {
|
|
67
|
-
return copy(event);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 读取当前剪贴板文本内容。
|
|
72
|
-
*/
|
|
73
65
|
function paste(): Promise<string> {
|
|
74
|
-
return new Promise((
|
|
66
|
+
return new Promise((ok) => {
|
|
75
67
|
uni.getClipboardData({
|
|
76
|
-
success: (res) =>
|
|
77
|
-
fail: () =>
|
|
68
|
+
success: (res) => ok(res.data),
|
|
69
|
+
fail: () => ok(""),
|
|
78
70
|
});
|
|
79
71
|
});
|
|
80
72
|
}
|
|
81
73
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
74
|
+
function auth() {
|
|
75
|
+
uni.showModal({
|
|
76
|
+
title: "提示",
|
|
77
|
+
content: "需要授权相册权限",
|
|
78
|
+
confirmText: "去设置",
|
|
79
|
+
success: (res) => {
|
|
80
|
+
if (res.confirm) uni.openSetting();
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function saveImage(path: string): Promise<boolean> {
|
|
86
|
+
return new Promise((ok) => {
|
|
87
87
|
uni.saveImageToPhotosAlbum({
|
|
88
|
-
filePath,
|
|
88
|
+
filePath: path,
|
|
89
89
|
success: () => {
|
|
90
90
|
uni.showToast({ title: "保存成功", icon: "success" });
|
|
91
|
-
|
|
91
|
+
ok(true);
|
|
92
92
|
},
|
|
93
93
|
fail: (err) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
content: "需要授权相册权限",
|
|
98
|
-
confirmText: "去设置",
|
|
99
|
-
success: (res) => {
|
|
100
|
-
if (res.confirm) uni.openSetting();
|
|
101
|
-
},
|
|
102
|
-
});
|
|
94
|
+
const msg = String(err.errMsg || "");
|
|
95
|
+
if (msg.includes("auth deny") || msg.includes("authorize")) {
|
|
96
|
+
auth();
|
|
103
97
|
} else {
|
|
104
98
|
uni.showToast({ title: "保存失败", icon: "none" });
|
|
105
99
|
}
|
|
106
|
-
|
|
100
|
+
ok(false);
|
|
107
101
|
},
|
|
108
102
|
});
|
|
109
103
|
});
|
|
110
104
|
}
|
|
111
105
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function download(opt: DownloadOpt): Promise<DownloadRes> {
|
|
128
|
+
return new Promise((ok) => {
|
|
117
129
|
const task = uni.downloadFile({
|
|
118
|
-
url:
|
|
119
|
-
filePath:
|
|
120
|
-
header:
|
|
130
|
+
url: opt.url,
|
|
131
|
+
filePath: opt.path,
|
|
132
|
+
header: opt.header,
|
|
121
133
|
success: (res) => {
|
|
122
134
|
if (res.statusCode === 200) {
|
|
123
|
-
|
|
135
|
+
ok({ ok: true, path: res.tempFilePath, code: res.statusCode });
|
|
124
136
|
} else {
|
|
125
|
-
|
|
137
|
+
ok({ ok: false, code: res.statusCode, msg: `下载失败,状态码:${res.statusCode}` });
|
|
126
138
|
}
|
|
127
139
|
},
|
|
128
|
-
fail: (err) =>
|
|
140
|
+
fail: (err) => ok({ ok: false, msg: err.errMsg }),
|
|
129
141
|
});
|
|
130
142
|
|
|
131
|
-
if (
|
|
143
|
+
if (opt.progress) {
|
|
132
144
|
task.onProgressUpdate((res) => {
|
|
133
|
-
|
|
145
|
+
opt.progress!(res.progress, res.totalBytesWritten, res.totalBytesExpectedToWrite);
|
|
134
146
|
});
|
|
135
147
|
}
|
|
136
148
|
});
|
|
137
149
|
}
|
|
138
150
|
|
|
139
|
-
|
|
140
|
-
* 下载远程图片并直接保存到相册。
|
|
141
|
-
*/
|
|
142
|
-
async function downloadAndSaveImage(url: string, onProgress?: (progress: number) => void): Promise<boolean> {
|
|
151
|
+
async function saveImageUrl(url: string, progress?: (value: number) => void): Promise<boolean> {
|
|
143
152
|
try {
|
|
144
153
|
uni.showLoading({ title: "下载中...", mask: true });
|
|
154
|
+
const res = await download({ url, progress: progress ? (value) => progress(value) : undefined });
|
|
155
|
+
uni.hideLoading();
|
|
145
156
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
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" });
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
150
169
|
|
|
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 });
|
|
151
174
|
uni.hideLoading();
|
|
152
175
|
|
|
153
|
-
if (!
|
|
154
|
-
uni.showToast({ title:
|
|
176
|
+
if (!res.ok || !res.path) {
|
|
177
|
+
uni.showToast({ title: res.msg || "下载失败", icon: "none" });
|
|
155
178
|
return false;
|
|
156
179
|
}
|
|
157
180
|
|
|
158
|
-
return await
|
|
181
|
+
return await saveVideo(res.path);
|
|
159
182
|
} catch {
|
|
160
183
|
uni.hideLoading();
|
|
161
184
|
uni.showToast({ title: "操作失败", icon: "none" });
|
|
@@ -163,5 +186,18 @@ export function useUtils() {
|
|
|
163
186
|
}
|
|
164
187
|
}
|
|
165
188
|
|
|
166
|
-
return {
|
|
189
|
+
return {
|
|
190
|
+
withQuery,
|
|
191
|
+
toQuery,
|
|
192
|
+
signText,
|
|
193
|
+
toNumber,
|
|
194
|
+
toBoolean,
|
|
195
|
+
copy,
|
|
196
|
+
paste,
|
|
197
|
+
saveImage,
|
|
198
|
+
saveVideo,
|
|
199
|
+
download,
|
|
200
|
+
saveImageUrl,
|
|
201
|
+
saveVideoUrl,
|
|
202
|
+
};
|
|
167
203
|
}
|
package/src/hlw.ts
CHANGED
|
@@ -1,37 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* hlw - 全局命名空间工厂
|
|
3
|
-
* 提供 $msg、$device、$
|
|
4
|
-
*
|
|
5
|
-
* 注意:广告能力请直接 `import { useAd } from "@hlw-uni/mp-vue"`,不再通过 hlw.$ad 暴露。
|
|
3
|
+
* 提供 $msg、$device、$request、$utils 的统一访问入口。
|
|
6
4
|
*/
|
|
7
5
|
import { useMsg } from '@/composables/msg';
|
|
8
6
|
import { useDevice, type DeviceInfo } from '@/composables/device';
|
|
9
|
-
import {
|
|
7
|
+
import { useRequest } from '@/composables/request';
|
|
10
8
|
import { useUtils } from '@/composables/utils';
|
|
11
|
-
import { useColor } from '@/composables/color';
|
|
12
9
|
|
|
13
10
|
export interface HlwInstance {
|
|
14
11
|
$msg: ReturnType<typeof useMsg>;
|
|
15
12
|
$device: DeviceInfo;
|
|
16
|
-
$
|
|
13
|
+
$request: ReturnType<typeof useRequest>;
|
|
17
14
|
$utils: ReturnType<typeof useUtils>;
|
|
18
|
-
$color: ReturnType<typeof useColor>;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
17
|
let _msg: ReturnType<typeof useMsg> | null = null;
|
|
22
|
-
let _device: ReturnType<typeof useDevice> | null = null;
|
|
23
18
|
let _utils: ReturnType<typeof useUtils> | null = null;
|
|
24
|
-
let _color: ReturnType<typeof useColor> | null = null;
|
|
25
19
|
|
|
26
20
|
export const hlw: HlwInstance = {
|
|
27
21
|
/** 延迟创建消息提示实例。 */
|
|
28
22
|
get $msg() { return (_msg ??= useMsg()); },
|
|
29
23
|
/** 延迟读取并缓存设备信息。 */
|
|
30
|
-
get $device() { return
|
|
31
|
-
/**
|
|
32
|
-
$
|
|
24
|
+
get $device() { return useDevice().info; },
|
|
25
|
+
/** 复用全局请求实例。 */
|
|
26
|
+
$request: useRequest(),
|
|
33
27
|
/** 延迟创建通用工具实例。 */
|
|
34
28
|
get $utils() { return (_utils ??= useUtils()); },
|
|
35
|
-
/** 延迟创建颜色工具实例。 */
|
|
36
|
-
get $color() { return (_color ??= useColor()); },
|
|
37
29
|
};
|