ToolsHelper.ets 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. import promptAction from '@ohos.promptAction';
  2. import { BusinessError, deviceInfo } from '@kit.BasicServicesKit';
  3. import { HashMap } from '@kit.ArkTS';
  4. import { DeviceInfo } from '../bean/DeviceInfo';
  5. import { common } from '@kit.AbilityKit';
  6. import { md5_hex } from '../util/md5';
  7. import { LogHelper } from '../../../../Index';
  8. export interface Btn {
  9. text?: string | Resource;
  10. color?: string | Resource;
  11. onClick?: () => void
  12. }
  13. export interface AlertOptions {
  14. title?: string,
  15. msg?: string,
  16. action?: Btn
  17. }
  18. export interface ConfirmOptions {
  19. title?: string
  20. msg?: string
  21. confirm?: Btn
  22. cancel?: Btn
  23. }
  24. export interface ListOptions<T> {
  25. title?: string
  26. cancel?: Btn
  27. index?: number
  28. alignment?: DialogAlignment
  29. values: Array<T>
  30. onSelected: (index: number, value: T) => void
  31. onError?: (msg: string) => void
  32. }
  33. interface ListItem {
  34. content: string
  35. }
  36. @Builder
  37. function customDialogBuilder<T>(option: ListOptions<T>, dialogTag: string) {
  38. Column() {
  39. Text(option.title)
  40. .fontSize(16)
  41. .fontColor('#00BE87')
  42. .textAlign(TextAlign.Center)
  43. .width('100%')
  44. .height(60)
  45. .backgroundColor('#F0FFFA')
  46. .borderRadius({
  47. topLeft: 10,
  48. topRight: 10
  49. })
  50. .visibility(option.title ? Visibility.Visible : Visibility.None)
  51. Column(){
  52. List() {
  53. ForEach(option.values, (item: T, index: number) => {
  54. ListItem() {
  55. Row() {
  56. Text(typeof item === "string" ? item : (item as ListItem).content)
  57. .fontSize(option.index === index ? 16 : 14)
  58. .fontWeight(option.index === index ? FontWeight.Bold : FontWeight.Normal)
  59. .maxLines(1)
  60. .width('70%')
  61. .ellipsisMode(EllipsisMode.CENTER)
  62. .textOverflow({
  63. overflow: TextOverflow.Ellipsis
  64. })
  65. .textAlign(TextAlign.Center)
  66. .align(Alignment.Center)
  67. .fontColor('#11102C')
  68. }
  69. .alignItems(VerticalAlign.Center)
  70. .justifyContent(FlexAlign.Center)
  71. .height(60)
  72. .width('100%')
  73. .onClick(() => {
  74. if (ToolsHelper.mapDialog.get(dialogTag)) {
  75. promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag))
  76. ToolsHelper.mapDialog.remove(dialogTag)
  77. }
  78. option.onSelected(index, item)
  79. })
  80. }
  81. }, (item: T) => typeof item === "string" ? item : (item as ListItem).content)
  82. }
  83. .listDirection(Axis.Vertical) // 排列方向
  84. .scrollBar(BarState.Off)
  85. .friction(0.6)
  86. .divider({
  87. strokeWidth: 1,
  88. color: '#F6F6F6',
  89. }) // 每行之间的分界线
  90. .edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring
  91. .width('100%')
  92. .height(option.values === undefined ? '20%' :option.values.length < 5 ? option.values.length*60 : 300)
  93. if (option.cancel) {
  94. Column() {
  95. Text(option.cancel.text ?? '取消')
  96. .fontSize(16)
  97. .fontColor(option.cancel.color ?? '#777777')
  98. .textAlign(TextAlign.Center)
  99. .width(325)
  100. .height(44)
  101. .backgroundColor('white')
  102. .borderRadius(22)
  103. .borderWidth(1)
  104. .borderColor('#D7D7D7')
  105. }
  106. .onClick(() => {
  107. if (ToolsHelper.mapDialog.get(dialogTag)) {
  108. promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag))
  109. ToolsHelper.mapDialog.remove(dialogTag)
  110. }
  111. option.cancel?.onClick && option.cancel.onClick()
  112. })
  113. .height(71)
  114. .width('100%')
  115. .alignItems(HorizontalAlign.Center)
  116. .justifyContent(FlexAlign.Center)
  117. }
  118. }.justifyContent(FlexAlign.End)
  119. }.justifyContent(FlexAlign.Start)
  120. }
  121. interface ThrottleInterface {
  122. startTime: number; //调用的时间
  123. timeoutNumber: number; //setTimeout的句柄
  124. }
  125. /**
  126. * 常用方法
  127. */
  128. export class ToolsHelper {
  129. private constructor() {
  130. }
  131. /**
  132. * 弹出Toast
  133. * @param msg
  134. */
  135. static log(...args: ESObject[]) {
  136. const k = ToolsHelper.getStackKey()?.split('/')
  137. LogHelper.info(`${k ? k[k.length-1].split('.')[0] : ''}::\n`, ...args)
  138. }
  139. /**
  140. * 弹出Toast
  141. * @param msg
  142. */
  143. static showMessage(msg: string) {
  144. console.info(msg);
  145. promptAction.showToast({
  146. message: msg,
  147. duration: 1500
  148. });
  149. }
  150. private static oTime: number = 0
  151. /**
  152. * 双击退出
  153. * @param msg
  154. */
  155. static doubleAndExit(event?: () => void) {
  156. const cTime = new Date().getTime()
  157. console.log('=====>', cTime)
  158. console.log('=====>', cTime - ToolsHelper.oTime)
  159. if (cTime - ToolsHelper.oTime > 800) {
  160. ToolsHelper.oTime = cTime
  161. ToolsHelper.showMessage('双击退出')
  162. } else {
  163. if (event) {
  164. event()
  165. } else {
  166. (getContext() as common.UIAbilityContext).terminateSelf()
  167. // getContext().getApplicationContext().killAllProcesses();
  168. }
  169. }
  170. return true
  171. }
  172. /**kio9
  173. * 弹出Alert弹窗
  174. * @param options
  175. */
  176. static showAlertDialog(options: AlertOptions) {
  177. try {
  178. promptAction.showDialog({
  179. alignment: DialogAlignment.Center,
  180. title: options.title,
  181. message: options.msg,
  182. buttons: [{
  183. text: options.action?.text ?? "确定",
  184. color: options.action?.color ?? "#000000",
  185. }]
  186. })
  187. .then(() => {
  188. options.action?.onClick && options.action?.onClick()
  189. })
  190. .catch((err: Error) => {
  191. ToolsHelper.showMessage(err.message)
  192. })
  193. } catch (error) {
  194. let message = (error as BusinessError).message
  195. ToolsHelper.showMessage(message)
  196. }
  197. }
  198. /**
  199. * 弹出Confirm弹窗
  200. * @param options
  201. */
  202. static showConfirmDialog(options: ConfirmOptions): Promise<void> {
  203. return new Promise((reject) => {
  204. try {
  205. promptAction.showDialog({
  206. alignment: 1,
  207. title: options.title,
  208. message: options.msg,
  209. buttons: [{
  210. text: options.confirm?.text ?? "确定",
  211. color: options.confirm?.color ?? "#000000",
  212. }, {
  213. text: options.cancel?.text ?? "取消",
  214. color: options.cancel?.color ?? "#666666",
  215. }]
  216. })
  217. .then((data) => {
  218. if (data.index === 0) {
  219. options.confirm?.onClick && options.confirm.onClick()
  220. } else {
  221. options.cancel?.onClick && options.cancel.onClick()
  222. }
  223. })
  224. .catch((err: Error) => {
  225. ToolsHelper.showMessage(err.message)
  226. })
  227. } catch (error) {
  228. reject()
  229. let message = (error as BusinessError).message
  230. ToolsHelper.showMessage(message)
  231. }
  232. })
  233. }
  234. public static mapDialog = new HashMap<string, number>()
  235. /**
  236. * 弹出List弹窗
  237. * @param options values 如果是非string列表的话,需要存在content字段
  238. * @param p 调用页面,直接this 即可
  239. */
  240. static showListDialog<T = string>(options: ListOptions<T>, p: object) {
  241. let isSuccess: Array<number> = []
  242. options.values.forEach((item, index) => {
  243. if (typeof item !== 'string') {
  244. if (!(item as ListItem).content) {
  245. isSuccess.push(index)
  246. }
  247. }
  248. })
  249. if (isSuccess.length > 0) {
  250. options.onError && options.onError(`第(${isSuccess.join("、")})个数据中,没有content字段。`)
  251. } else {
  252. const dialogTag = ToolsHelper.getUuid()
  253. promptAction.openCustomDialog({
  254. cornerRadius: 0,
  255. autoCancel: false,
  256. width: '100%',
  257. // height: options.values === undefined ? '20%' :
  258. // options.values?.length < 8 ? `${options.values?.length / 16 * 100+10}%` : '50%',
  259. alignment: options.alignment ?? DialogAlignment.Bottom,
  260. builder: customDialogBuilder.bind(p, options, dialogTag),
  261. backgroundColor: 'white'
  262. }).then((dialogId: number) => {
  263. ToolsHelper.mapDialog.set(dialogTag, dialogId)
  264. })
  265. }
  266. }
  267. /**
  268. * 弹出自定义弹窗
  269. * @param alignment 弹窗在竖直方向上的对齐方式
  270. */
  271. static showCustomDialog(dialogTag: string, b: CustomBuilder, alignment?: DialogAlignment) {
  272. promptAction.openCustomDialog({
  273. alignment: alignment ?? DialogAlignment.Center,
  274. builder: b,
  275. cornerRadius: 3,
  276. width: '100%',
  277. }).then((dialogId: number) => {
  278. ToolsHelper.mapDialog.set(dialogTag, dialogId)
  279. }).catch((error: Error) => {
  280. console.log('>>>>>', JSON.stringify(error))
  281. })
  282. }
  283. /**
  284. * 关闭自定义弹窗
  285. * @param dialogTag 开启时的tag
  286. */
  287. static closeCustomDialog(dialogTag: string) {
  288. if (ToolsHelper.mapDialog.get(dialogTag)) {
  289. promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogTag))
  290. ToolsHelper.mapDialog.remove(dialogTag)
  291. }
  292. }
  293. static encryptStringToNumber(str: string): number {
  294. let result = 0;
  295. for (let i = 0; i < str.length; i++) {
  296. result = result * 256 + str.charCodeAt(i);
  297. }
  298. return result;
  299. }
  300. static decryptNumberToString(num: number): string {
  301. let result = '';
  302. while (num > 0) {
  303. result = String.fromCharCode(num & 255) + result;
  304. num >>>= 8;
  305. }
  306. return result.replace(/[\s\0]+$/, ''); // 移除结尾的空白字符
  307. }
  308. /**
  309. * 获取调用栈第一个类
  310. */
  311. static getStackKey() {
  312. let stack = new Error().stack
  313. if (stack) {
  314. let list = JSON.stringify(stack).split('\\n')
  315. let a = list[list.length-2].split(':')[0].split('(')[1]
  316. return a
  317. }
  318. return undefined
  319. }
  320. private static deviceInfo: DeviceInfo
  321. /**
  322. * 获取设备信息
  323. * @returns {@link DeviceInfo}
  324. */
  325. static getDeviceInfo() {
  326. if (!ToolsHelper.deviceInfo) {
  327. ToolsHelper.deviceInfo = new DeviceInfo()
  328. ToolsHelper.deviceInfo.ODID = deviceInfo.ODID
  329. ToolsHelper.deviceInfo.manufacture = deviceInfo.manufacture
  330. ToolsHelper.deviceInfo.brand = deviceInfo.brand
  331. ToolsHelper.deviceInfo.osFullName = deviceInfo.osFullName
  332. }
  333. return ToolsHelper.deviceInfo
  334. }
  335. /**
  336. * 防抖动函数,调用后会延迟wait时间执行,当在wait时间内再次对同一函数调用,则会取消之前的定时器,重新定时
  337. * @param fun
  338. * @param wait
  339. */
  340. static debounceHold(fun: Function, wait: number = 1500) {
  341. let funcValue1 = ToolsHelper.getUniqueId(fun)
  342. let hash = md5_hex(funcValue1)
  343. if (ToolsHelper.setTimeOutMap.get(hash)) {
  344. clearTimeout(ToolsHelper.setTimeOutMap.get(hash)?.timeoutNumber)
  345. ToolsHelper.setTimeOutMap.delete(hash)
  346. }
  347. // ToolsHelper.checkTimeOutNumber()
  348. let timeoutNumber = setTimeout(() => {
  349. ToolsHelper.setTimeOutMap.get(hash) && clearTimeout(ToolsHelper.setTimeOutMap.get(hash)?.timeoutNumber)
  350. ToolsHelper.setTimeOutMap.delete(hash)
  351. // 执行函数调用
  352. fun()
  353. }, wait)
  354. ToolsHelper.setTimeOutMap.set(hash, {
  355. timeoutNumber: timeoutNumber,
  356. startTime: new Date().getTime(),
  357. })
  358. }
  359. private static setTimeOutMap: Map<string, ThrottleInterface> = new Map()
  360. private static uniqueIdMap = new WeakMap<Function, string>();
  361. public static getUuid() {
  362. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
  363. let r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
  364. return v.toString(16);
  365. });
  366. }
  367. private static getUniqueId(fun: Function): string {
  368. // ToolsHelper.log(ToolsHelper.getStackKey())
  369. if (!ToolsHelper.uniqueIdMap.has(fun)) {
  370. ToolsHelper.uniqueIdMap.set(fun, ToolsHelper.getUuid());
  371. }
  372. return ToolsHelper.uniqueIdMap.get(fun)!;
  373. }
  374. }