246 lines
6.7 KiB
TypeScript
246 lines
6.7 KiB
TypeScript
import { ComponentInternalInstance } from "vue";
|
||
|
||
/** 扫码 */
|
||
export async function scanCode() {
|
||
const res = await new Promise<UniApp.ScanCodeSuccessRes>(
|
||
(resolve, reject) => {
|
||
uni.scanCode({
|
||
success(res) {
|
||
resolve(res);
|
||
},
|
||
fail(res) {
|
||
reject(res);
|
||
},
|
||
});
|
||
},
|
||
);
|
||
console.log({ res });
|
||
return res.result;
|
||
}
|
||
|
||
/** 获取当前页面 */
|
||
export function getCurrentPage() {
|
||
const pages = getCurrentPages();
|
||
return pages[pages.length - 1];
|
||
}
|
||
|
||
|
||
/**
|
||
* 使用 TypeScript 工具类型实现自动类型推断的 toPromise
|
||
*
|
||
* 核心思路:
|
||
* 1. 使用类似 Parameters<T>[0] 的方式提取函数第一个参数类型
|
||
* 2. 从参数类型中提取 success 回调的参数类型
|
||
* 3. 使用条件类型和 infer 关键字实现类型推断
|
||
* 4. 虽然不能直接用 ReturnType(因为uni API返回void),但可以用类似思路提取回调参数类型
|
||
*/
|
||
|
||
// 方法1:直接使用 Parameters<T> 工具类型
|
||
// type ExtractFirstParameter<T> = Parameters<T>[0]; // 这样也可以,但需要T是函数类型
|
||
|
||
// 方法2:使用条件类型实现(更灵活,等价于 Parameters<T>[0])
|
||
type ExtractFirstParameter<T> = T extends (arg: infer P, ...args: unknown[]) => unknown ? P : never;
|
||
|
||
// 提取 success 回调的参数类型(类似 ReturnType 的思路,但提取回调参数)
|
||
type ExtractSuccessResult<T> = T extends { success?: (result: infer R) => void } ? R : never;
|
||
|
||
// 提取函数参数类型并排除回调函数(用于args参数)
|
||
type ExtractOptions<T> = T extends (options: infer P) => unknown
|
||
? Omit<P, "success" | "fail" | "complete">
|
||
: never;
|
||
|
||
// 类型测试示例(编译时验证)
|
||
// type TestDownloadOptions = ExtractOptions<typeof uni.downloadFile>; // UniNamespace.DownloadFileOption 去除回调
|
||
// type TestDownloadResult = ExtractSuccessResult<Parameters<typeof uni.downloadFile>[0]>; // UniNamespace.DownloadSuccessData
|
||
|
||
/**
|
||
* 将 uni-app 回调方法转换为 Promise(使用工具类型自动推断)
|
||
*
|
||
* 优势:
|
||
* - 无需手动指定泛型参数
|
||
* - 完全基于 TypeScript 工具类型实现
|
||
* - 支持所有 uni-app API,不需要为每个API单独写重载
|
||
* - 类型安全,有完整的智能提示
|
||
*
|
||
* @example
|
||
* // 自动推断为 Promise<UniNamespace.DownloadSuccessData>
|
||
* const downloadRes = await toPromise(uni.downloadFile, { url: 'https://example.com/file.jpg' });
|
||
* console.log(downloadRes.tempFilePath); // ✅ 类型安全,有智能提示
|
||
*
|
||
* // 自动推断为 Promise<UniNamespace.ScanCodeSuccessRes>
|
||
* const scanRes = await toPromise(uni.scanCode, {});
|
||
* console.log(scanRes.result); // ✅ 类型安全,有智能提示
|
||
*
|
||
* // 自动推断为 Promise<UniNamespace.UploadFileSuccessCallbackResult>
|
||
* const uploadRes = await toPromise(uni.uploadFile, {
|
||
* url: '/upload',
|
||
* filePath: 'temp://file.jpg',
|
||
* name: 'file'
|
||
* });
|
||
* console.log(uploadRes.statusCode); // ✅ 类型安全,有智能提示
|
||
*
|
||
* // 自动推断为 Promise<UniNamespace.RequestSuccessCallbackResult>
|
||
* const requestRes = await toPromise(uni.request, { url: '/api/data' });
|
||
* console.log(requestRes.data); // ✅ 类型安全,有智能提示
|
||
*/
|
||
export function toPromise<F extends (options: Record<string, unknown>) => void>(
|
||
uniFun: F,
|
||
args?: ExtractOptions<F>,
|
||
): Promise<ExtractSuccessResult<ExtractFirstParameter<F>>> {
|
||
type OptionsType = ExtractFirstParameter<F>;
|
||
type ResultType = ExtractSuccessResult<OptionsType>;
|
||
|
||
return new Promise<ResultType>((resolve, reject) => {
|
||
uniFun({
|
||
...args,
|
||
success(res: ResultType) {
|
||
resolve(res);
|
||
},
|
||
fail(err: unknown) {
|
||
reject(err);
|
||
},
|
||
} as OptionsType);
|
||
});
|
||
}
|
||
|
||
|
||
export function showToast(
|
||
title: string,
|
||
icon: "success" | "loading" | "error" | "none" = "none",
|
||
options?: UniNamespace.ShowToastOptions,
|
||
) {
|
||
uni.showToast({
|
||
...options,
|
||
title,
|
||
icon,
|
||
duration: options?.duration ?? 1000,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 通过URL生成canvas二维码
|
||
* @param canvasId
|
||
* @param url 图片URL
|
||
* @param x 定位于canvas左边宽度,单位px
|
||
* @param y 定位于canvas顶部高度,单位px
|
||
* @param width 二维码宽度,单位px
|
||
* @param height 二维码高度,单位px
|
||
* @param thisArg 页面上下文
|
||
* @returns 图像像素点数据
|
||
*/
|
||
export async function getCanvasImageData(
|
||
canvasId: string,
|
||
url: string,
|
||
x = 0,
|
||
y = 0,
|
||
width = 200,
|
||
height = 200,
|
||
thisArg: ComponentInternalInstance,
|
||
) {
|
||
const context = uni.createCanvasContext(canvasId, thisArg);
|
||
|
||
const imgRes = await toPromise(
|
||
uni.downloadFile,
|
||
{
|
||
url,
|
||
},
|
||
);
|
||
|
||
console.log(imgRes);
|
||
context.drawImage(imgRes.tempFilePath, x, y, width, height);
|
||
await new Promise((resolve) => {
|
||
context.draw(false, (res) => {
|
||
console.log(res);
|
||
resolve(res);
|
||
});
|
||
});
|
||
const imageData: UniNamespace.CanvasGetImageDataRes =
|
||
await uni.canvasGetImageData({
|
||
canvasId: canvasId,
|
||
x,
|
||
y,
|
||
width: width,
|
||
height: height,
|
||
});
|
||
|
||
return imageData.data;
|
||
}
|
||
|
||
export async function getCanvasImageDataSimplify(
|
||
canvasId: string,
|
||
url: string,
|
||
width = 200,
|
||
height = 200,
|
||
thisArg: ComponentInternalInstance,
|
||
) {
|
||
return await getCanvasImageData(canvasId, url, 0, 0, width, height, thisArg);
|
||
}
|
||
|
||
export async function getCanvasImageDataTransObj({
|
||
canvasId,
|
||
url,
|
||
x = 0,
|
||
y = 0,
|
||
width = 200,
|
||
height = 200,
|
||
thisArg,
|
||
}: {
|
||
canvasId: string;
|
||
url: string;
|
||
x: number;
|
||
y: number;
|
||
width: number;
|
||
height: number;
|
||
thisArg: ComponentInternalInstance;
|
||
}) {
|
||
return await getCanvasImageData(canvasId, url, x, y, width, height, thisArg);
|
||
}
|
||
|
||
export function rpxToPx(rpx: number | string) {
|
||
if (typeof rpx === "string") {
|
||
rpx = Number.parseInt(rpx);
|
||
}
|
||
const screenWidth = uni.getSystemInfoSync().screenWidth;
|
||
return (screenWidth * rpx) / 750;
|
||
}
|
||
|
||
export function pxToRpx(px: number | string) {
|
||
if (typeof px === "string") {
|
||
px = Number.parseInt(px);
|
||
}
|
||
const screenWidth = uni.getSystemInfoSync().screenWidth;
|
||
return (750 * px) / screenWidth;
|
||
}
|
||
|
||
/**
|
||
* 退出程序
|
||
*/
|
||
export function exitApp() {
|
||
// #ifdef APP-PLUS
|
||
if (plus.os.name?.toLowerCase() === "android") {
|
||
plus.runtime.quit();
|
||
} else {
|
||
const threadClass = plus.ios.importClass("NSThread");
|
||
const mainThread = plus.ios.invoke(threadClass, "mainThread");
|
||
plus.ios.invoke(mainThread, "exit");
|
||
}
|
||
// #endif
|
||
}
|
||
|
||
/**
|
||
* 检查是否有安卓权限
|
||
*/
|
||
export function checkAndroidPermission(permissionList: string[]) {
|
||
// #ifdef APP-PLUS
|
||
const ActivityCompat = plus.android.importClass(
|
||
"androidx.core.app.ActivityCompat",
|
||
) as Android.ActivityCompat;
|
||
const activity = plus.android.runtimeMainActivity();
|
||
|
||
return permissionList.every((v) => {
|
||
const p = ActivityCompat.checkSelfPermission(activity, v);
|
||
return p === 0;
|
||
});
|
||
// #endif
|
||
}
|