r-util-js/packages/uni-app/src/uni-helper.ts

246 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}