import { ComponentInternalInstance } from "vue"; /** 扫码 */ export async function scanCode() { const res = await new Promise( (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[0] 的方式提取函数第一个参数类型 * 2. 从参数类型中提取 success 回调的参数类型 * 3. 使用条件类型和 infer 关键字实现类型推断 * 4. 虽然不能直接用 ReturnType(因为uni API返回void),但可以用类似思路提取回调参数类型 */ // 方法1:直接使用 Parameters 工具类型 // type ExtractFirstParameter = Parameters[0]; // 这样也可以,但需要T是函数类型 // 方法2:使用条件类型实现(更灵活,等价于 Parameters[0]) type ExtractFirstParameter = T extends (arg: infer P, ...args: unknown[]) => unknown ? P : never; // 提取 success 回调的参数类型(类似 ReturnType 的思路,但提取回调参数) type ExtractSuccessResult = T extends { success?: (result: infer R) => void } ? R : never; // 提取函数参数类型并排除回调函数(用于args参数) type ExtractOptions = T extends (options: infer P) => unknown ? Omit : never; // 类型测试示例(编译时验证) // type TestDownloadOptions = ExtractOptions; // UniNamespace.DownloadFileOption 去除回调 // type TestDownloadResult = ExtractSuccessResult[0]>; // UniNamespace.DownloadSuccessData /** * 将 uni-app 回调方法转换为 Promise(使用工具类型自动推断) * * 优势: * - 无需手动指定泛型参数 * - 完全基于 TypeScript 工具类型实现 * - 支持所有 uni-app API,不需要为每个API单独写重载 * - 类型安全,有完整的智能提示 * * @example * // 自动推断为 Promise * const downloadRes = await toPromise(uni.downloadFile, { url: 'https://example.com/file.jpg' }); * console.log(downloadRes.tempFilePath); // ✅ 类型安全,有智能提示 * * // 自动推断为 Promise * const scanRes = await toPromise(uni.scanCode, {}); * console.log(scanRes.result); // ✅ 类型安全,有智能提示 * * // 自动推断为 Promise * const uploadRes = await toPromise(uni.uploadFile, { * url: '/upload', * filePath: 'temp://file.jpg', * name: 'file' * }); * console.log(uploadRes.statusCode); // ✅ 类型安全,有智能提示 * * // 自动推断为 Promise * const requestRes = await toPromise(uni.request, { url: '/api/data' }); * console.log(requestRes.data); // ✅ 类型安全,有智能提示 */ export function toPromise) => void>( uniFun: F, args?: ExtractOptions, ): Promise>> { type OptionsType = ExtractFirstParameter; type ResultType = ExtractSuccessResult; return new Promise((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 }