import promptAction from '@ohos.promptAction'; import { BusinessError, deviceInfo } from '@kit.BasicServicesKit'; import { buffer, HashMap, util } from '@kit.ArkTS'; import { DeviceInfo } from '../bean/DeviceInfo'; import { common } from '@kit.AbilityKit'; import { md5_hex } from '../util/md5'; import { LogHelper } from '../../../../Index'; export interface Btn { text?: string | Resource; color?: string | Resource; onClick?: () => void } export interface AlertOptions { title?: string, msg?: string, action?: Btn } export interface ConfirmOptions { title?: string msg?: string confirm?: Btn cancel?: Btn } export interface ListOptions { title?: string cancel?: Btn autoCancel?: boolean index?: number alignment?: DialogAlignment values: Array onSelected: (index: number, value: T) => void onError?: (msg: string) => void } interface ListItem { content: string } @Builder function customDialogBuilder(option: ListOptions, dialogTag: string) { Column() { Text(option.title) .fontSize(16) .fontColor('#00BE87') .textAlign(TextAlign.Center) .width('100%') .height(60) .backgroundColor('#F0FFFA') .borderRadius({ topLeft: 10, topRight: 10 }) .visibility(option.title ? Visibility.Visible : Visibility.None) Column() { List() { ForEach(option.values, (item: T, index: number) => { ListItem() { Row() { Text(typeof item === "string" ? item : (item as ListItem).content) .fontSize(option.index === index ? 16 : 14) .fontWeight(option.index === index ? FontWeight.Bold : FontWeight.Normal) .maxLines(1) .width('70%') .ellipsisMode(EllipsisMode.CENTER) .textOverflow({ overflow: TextOverflow.Ellipsis }) .textAlign(TextAlign.Center) .align(Alignment.Center) .fontColor('#11102C') } .alignItems(VerticalAlign.Center) .justifyContent(FlexAlign.Center) .height(60) .width('100%') .onClick(() => { if (ToolsHelper.mapDialog.get(dialogTag)) { promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag)) ToolsHelper.mapDialog.remove(dialogTag) } option.onSelected(index, item) }) } }, (item: T) => typeof item === "string" ? item : (item as ListItem).content) } .listDirection(Axis.Vertical) // 排列方向 .scrollBar(BarState.Off) .friction(0.6) .divider({ strokeWidth: 1, color: '#F6F6F6', }) // 每行之间的分界线 .edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring .width('100%') .height(option.values === undefined ? '20%' : option.values.length < 5 ? option.values.length * 60 : 300) if (option.cancel) { Column() { Text(option.cancel.text ?? '取消') .fontSize(16) .fontColor(option.cancel.color ?? '#777777') .textAlign(TextAlign.Center) .width(325) .height(44) .backgroundColor('white') .borderRadius(22) .borderWidth(1) .borderColor('#D7D7D7') } .onClick(() => { if (ToolsHelper.mapDialog.get(dialogTag)) { promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag)) ToolsHelper.mapDialog.remove(dialogTag) } option.cancel?.onClick && option.cancel.onClick() }) .height(71) .width('100%') .alignItems(HorizontalAlign.Center) .justifyContent(FlexAlign.Center) } }.justifyContent(FlexAlign.End) }.justifyContent(FlexAlign.Start) } interface ThrottleInterface { startTime: number; //调用的时间 timeoutNumber: number; //setTimeout的句柄 } /** * 常用方法 */ export class ToolsHelper { private constructor() { } /** * 弹出Toast * @param msg */ static log(...args: ESObject[]) { const k = ToolsHelper.getStackKey()?.split('/') LogHelper.info(`${k ? k[k.length-1].split('.')[0] : ''}::\n`, ...args) } /** * 弹出Toast * @param msg */ static showMessage(msg: string) { console.info(msg); promptAction.showToast({ message: msg, duration: 1500 }); } private static oTime: number = 0 /** * 双击退出 * @param msg */ static doubleAndExit(event?: () => void) { const cTime = new Date().getTime() console.log('=====>', cTime) console.log('=====>', cTime - ToolsHelper.oTime) if (cTime - ToolsHelper.oTime > 800) { ToolsHelper.oTime = cTime ToolsHelper.showMessage('双击退出') } else { if (event) { event() } else { (getContext() as common.UIAbilityContext).terminateSelf() // getContext().getApplicationContext().killAllProcesses(); } } return true } /**kio9 * 弹出Alert弹窗 * @param options */ static showAlertDialog(options: AlertOptions) { try { promptAction.showDialog({ alignment: DialogAlignment.Center, title: options.title, message: options.msg, buttons: [{ text: options.action?.text ?? "确定", color: options.action?.color ?? "#000000", }] }) .then(() => { options.action?.onClick && options.action?.onClick() }) .catch((err: Error) => { // ToolsHelper.showMessage(err.message) }) } catch (error) { let message = (error as BusinessError).message // ToolsHelper.showMessage(message) } } /** * 弹出Confirm弹窗 * @param options */ static showConfirmDialog(options: ConfirmOptions): Promise { return new Promise((reject) => { try { promptAction.showDialog({ alignment: 1, title: options.title, message: options.msg, buttons: [{ text: options.cancel?.text ?? "取消", color: options.cancel?.color ?? "#666666", }, { text: options.confirm?.text ?? "确定", color: options.confirm?.color ?? "#000000", },] }) .then((data) => { if (data.index === 0) { options.cancel?.onClick && options.cancel.onClick() } else { options.confirm?.onClick && options.confirm.onClick() } }) .catch((err: Error) => { // ToolsHelper.showMessage(err.message) }) } catch (error) { reject() // let message = (error as BusinessError).message // ToolsHelper.showMessage(message) } }) } public static mapDialog = new HashMap() /** * 弹出List弹窗 * @param options values 如果是非string列表的话,需要存在content字段 * @param p 调用页面,直接this 即可 */ static showListDialog(options: ListOptions, p: object) { let isSuccess: Array = [] options.values.forEach((item, index) => { if (typeof item !== 'string') { if (!(item as ListItem).content) { isSuccess.push(index) } } }) if (isSuccess.length > 0) { options.onError && options.onError(`第(${isSuccess.join("、")})个数据中,没有content字段。`) } else { const dialogTag = ToolsHelper.getUuid() promptAction.openCustomDialog({ cornerRadius: 0, autoCancel: options.autoCancel ?? false, width: '100%', // height: options.values === undefined ? '20%' : // options.values?.length < 8 ? `${options.values?.length / 16 * 100+10}%` : '50%', alignment: options.alignment ?? DialogAlignment.Bottom, builder: customDialogBuilder.bind(p, options, dialogTag), backgroundColor: 'white' }).then((dialogId: number) => { ToolsHelper.mapDialog.set(dialogTag, dialogId) }) } } /** * 弹出自定义弹窗 * @param alignment 弹窗在竖直方向上的对齐方式 */ static showCustomDialog(dialogTag: string, b: CustomBuilder, alignment?: DialogAlignment) { promptAction.openCustomDialog({ alignment: alignment ?? DialogAlignment.Center, builder: b, cornerRadius: 3, width: '100%', }).then((dialogId: number) => { ToolsHelper.mapDialog.set(dialogTag, dialogId) }).catch((error: Error) => { console.log('>>>>>', JSON.stringify(error)) }) } /** * 关闭自定义弹窗 * @param dialogTag 开启时的tag */ static closeCustomDialog(dialogTag: string) { if (ToolsHelper.mapDialog.get(dialogTag)) { promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag)) ToolsHelper.mapDialog.remove(dialogTag) } } static encryptStringToNumber(str: string): number { let result = 0; for (let i = 0; i < str.length; i++) { result = result * 256 + str.charCodeAt(i); } return result; } static decryptNumberToString(num: number): string { let result = ''; while (num > 0) { result = String.fromCharCode(num & 255) + result; num >>>= 8; } return result.replace(/[\s\0]+$/, ''); // 移除结尾的空白字符 } /** * 获取调用栈第一个类 */ static getStackKey() { let stack = new Error().stack if (stack) { let list = JSON.stringify(stack).split('\\n') let a = list[list.length-2].split(':')[0].split('(')[1] return a } return undefined } private static deviceInfo: DeviceInfo /** * 获取设备信息 * @returns {@link DeviceInfo} */ static getDeviceInfo() { if (!ToolsHelper.deviceInfo) { ToolsHelper.deviceInfo = new DeviceInfo() ToolsHelper.deviceInfo.ODID = deviceInfo.ODID ToolsHelper.deviceInfo.manufacture = deviceInfo.manufacture ToolsHelper.deviceInfo.productModel = deviceInfo.productModel ToolsHelper.deviceInfo.brand = deviceInfo.brand ToolsHelper.deviceInfo.osFullName = deviceInfo.osFullName ToolsHelper.logInfo() } return ToolsHelper.deviceInfo } private static logInfo() { // ToolsHelper.log('deviceType-----', deviceInfo.deviceType) // ToolsHelper.log('manufacture-----', deviceInfo.manufacture) // ToolsHelper.log('brand-----', deviceInfo.brand) // ToolsHelper.log('marketName-----', deviceInfo.marketName) // ToolsHelper.log('productSeries-----', deviceInfo.productSeries) // ToolsHelper.log('productModel-----', deviceInfo.productModel) // ToolsHelper.log('softwareModel-----', deviceInfo.softwareModel) // ToolsHelper.log('hardwareModel-----', deviceInfo.hardwareModel) // ToolsHelper.log('hardwareProfile-----', deviceInfo.hardwareProfile) // ToolsHelper.log('serial-----', deviceInfo.serial) // ToolsHelper.log('bootloaderVersion-----', deviceInfo.bootloaderVersion) // ToolsHelper.log('abiList-----', deviceInfo.abiList) // ToolsHelper.log('securityPatchTag-----', deviceInfo.securityPatchTag) // ToolsHelper.log('displayVersion-----', deviceInfo.displayVersion) // ToolsHelper.log('incrementalVersion-----', deviceInfo.incrementalVersion) // ToolsHelper.log('osReleaseType-----', deviceInfo.osReleaseType) // ToolsHelper.log('osFullName-----', deviceInfo.osFullName) // ToolsHelper.log('majorVersion-----', deviceInfo.majorVersion) // ToolsHelper.log('seniorVersion-----', deviceInfo.seniorVersion) // ToolsHelper.log('featureVersion-----', deviceInfo.featureVersion) // ToolsHelper.log('buildVersion-----', deviceInfo.buildVersion) // ToolsHelper.log('sdkApiVersion-----', deviceInfo.sdkApiVersion) // ToolsHelper.log('firstApiVersion-----', deviceInfo.firstApiVersion) // ToolsHelper.log('versionId-----', deviceInfo.versionId) // ToolsHelper.log('buildType-----', deviceInfo.buildType) // ToolsHelper.log('buildUser-----', deviceInfo.buildUser) // ToolsHelper.log('buildHost-----', deviceInfo.buildHost) // ToolsHelper.log('buildTime-----', deviceInfo.buildTime) // ToolsHelper.log('buildRootHash-----', deviceInfo.buildRootHash) // ToolsHelper.log('distributionOSName-----', deviceInfo.distributionOSName) // ToolsHelper.log('distributionOSVersion-----', deviceInfo.distributionOSVersion) // ToolsHelper.log('distributionOSApiVersion-----', deviceInfo.distributionOSApiVersion) // ToolsHelper.log('distributionOSApiName-----', deviceInfo.distributionOSApiName) // ToolsHelper.log('distributionOSReleaseType-----', deviceInfo.distributionOSReleaseType) } /** * 防抖动函数,调用后会延迟wait时间执行,当在wait时间内再次对同一函数调用,则会取消之前的定时器,重新定时 * @param fun * @param wait */ static debounceHold(fun: Function, wait: number = 1500) { let funcValue1 = ToolsHelper.getUniqueId(fun) let hash = md5_hex(funcValue1) if (ToolsHelper.setTimeOutMap.get(hash)) { clearTimeout(ToolsHelper.setTimeOutMap.get(hash)?.timeoutNumber) ToolsHelper.setTimeOutMap.delete(hash) } // ToolsHelper.checkTimeOutNumber() let timeoutNumber = setTimeout(() => { ToolsHelper.setTimeOutMap.get(hash) && clearTimeout(ToolsHelper.setTimeOutMap.get(hash)?.timeoutNumber) ToolsHelper.setTimeOutMap.delete(hash) // 执行函数调用 fun() }, wait) ToolsHelper.setTimeOutMap.set(hash, { timeoutNumber: timeoutNumber, startTime: new Date().getTime(), }) } static toString(arrayBuffer: ArrayBuffer) { let decoder = util.TextDecoder.create('utf-8'); return decoder.decodeToString(new Uint8Array(arrayBuffer)) } static stringToArrayBuffer(str: string): ArrayBuffer { let buf = buffer.from(str, 'utf-8'); // 使用 UTF-8 编码 return buf.buffer; } private static setTimeOutMap: Map = new Map() private static uniqueIdMap = new WeakMap(); public static getUuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { let r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } private static getUniqueId(fun: Function): string { // ToolsHelper.log('', ToolsHelper.getStackKey()) if (!ToolsHelper.uniqueIdMap.has(fun)) { ToolsHelper.uniqueIdMap.set(fun, ToolsHelper.getUuid()); } return ToolsHelper.uniqueIdMap.get(fun)!; } }