fanmingyou3_wd

工程级一多开发:三层架构规范

工程级一多开发:多设备部署设计
Showing 100 changed files with 4082 additions and 34 deletions

Too many changes to show.

To preserve performance only 100 of 100+ files are displayed.

@@ -5,13 +5,13 @@ @@ -5,13 +5,13 @@
5 "name": "default", 5 "name": "default",
6 "type": "HarmonyOS", 6 "type": "HarmonyOS",
7 "material": { 7 "material": {
8 - "certpath": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.cer",  
9 - "storePassword": "0000001A76406594DCD45689E6888037F1FA52EA0D15317DD495835E1F8D18BEF743FCEB51D881F01BA2", 8 + "certpath": "C:\\Users\\PC\\.ohos\\config\\default_sight_harmony_vDjosN2opayZneQDLiBs3Lc0sT1uPzRAYaDxxv2LWEQ=.cer",
  9 + "storePassword": "0000001A87430EDC6C0D1CD6A6473A6D385177DBFF70325BBD48C7E491A0D9B37F91920FD46D82B2436B",
10 "keyAlias": "debugKey", 10 "keyAlias": "debugKey",
11 - "keyPassword": "0000001ACD3163BEAB09744DEA803A35F7B90073531926D60C13C3BA4F277C41CDB92A46819ED663CD31",  
12 - "profile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.p7b", 11 + "keyPassword": "0000001A8E615E1CB276AA122C291AFBCDFF792FEE8AA618B186EE7C99220C7F549B8C2ED6A209703D75",
  12 + "profile": "C:\\Users\\PC\\.ohos\\config\\default_sight_harmony_vDjosN2opayZneQDLiBs3Lc0sT1uPzRAYaDxxv2LWEQ=.p7b",
13 "signAlg": "SHA256withECDSA", 13 "signAlg": "SHA256withECDSA",
14 - "storeFile": "C:\\Users\\PC\\.ohos\\config\\auto_debug_sight_harmony_com.wondertek.sight_70086000327424393.p12" 14 + "storeFile": "C:\\Users\\PC\\.ohos\\config\\default_sight_harmony_vDjosN2opayZneQDLiBs3Lc0sT1uPzRAYaDxxv2LWEQ=.p12"
15 } 15 }
16 } 16 }
17 ], 17 ],
@@ -35,8 +35,8 @@ @@ -35,8 +35,8 @@
35 }, 35 },
36 "modules": [ 36 "modules": [
37 { 37 {
38 - "name": "entry",  
39 - "srcPath": "./entry", 38 + "name": "phone",
  39 + "srcPath": "./products/phone",
40 "targets": [ 40 "targets": [
41 { 41 {
42 "name": "default", 42 "name": "default",
@@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
48 }, 48 },
49 { 49 {
50 "name": "wdConstant", 50 "name": "wdConstant",
51 - "srcPath": "./wdConstant", 51 + "srcPath": "./commons/wdConstant",
52 "targets": [ 52 "targets": [
53 { 53 {
54 "name": "default", 54 "name": "default",
@@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
60 }, 60 },
61 { 61 {
62 "name": "wdKit", 62 "name": "wdKit",
63 - "srcPath": "./wdKit", 63 + "srcPath": "./commons/wdKit",
64 "targets": [ 64 "targets": [
65 { 65 {
66 "name": "default", 66 "name": "default",
@@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
72 }, 72 },
73 { 73 {
74 "name": "wdBean", 74 "name": "wdBean",
75 - "srcPath": "./wdBean", 75 + "srcPath": "./features/wdBean",
76 "targets": [ 76 "targets": [
77 { 77 {
78 "name": "default", 78 "name": "default",
@@ -84,7 +84,7 @@ @@ -84,7 +84,7 @@
84 }, 84 },
85 { 85 {
86 "name": "wdComponent", 86 "name": "wdComponent",
87 - "srcPath": "./wdComponent", 87 + "srcPath": "./features/wdComponent",
88 "targets": [ 88 "targets": [
89 { 89 {
90 "name": "default", 90 "name": "default",
@@ -96,7 +96,7 @@ @@ -96,7 +96,7 @@
96 }, 96 },
97 { 97 {
98 "name": "wdNetwork", 98 "name": "wdNetwork",
99 - "srcPath": "./wdNetwork", 99 + "srcPath": "./commons/wdNetwork",
100 "targets": [ 100 "targets": [
101 { 101 {
102 "name": "default", 102 "name": "default",
@@ -108,7 +108,7 @@ @@ -108,7 +108,7 @@
108 }, 108 },
109 { 109 {
110 "name": "wdRouter", 110 "name": "wdRouter",
111 - "srcPath": "./wdRouter", 111 + "srcPath": "./commons/wdRouter",
112 "targets": [ 112 "targets": [
113 { 113 {
114 "name": "default", 114 "name": "default",
@@ -120,11 +120,11 @@ @@ -120,11 +120,11 @@
120 }, 120 },
121 { 121 {
122 "name": "wdJsBridge", 122 "name": "wdJsBridge",
123 - "srcPath": "./wdJsBridge" 123 + "srcPath": "./commons/wdJsBridge"
124 }, 124 },
125 { 125 {
126 "name": "wdWebComponent", 126 "name": "wdWebComponent",
127 - "srcPath": "./wdWebComponent", 127 + "srcPath": "./commons/wdWebComponent",
128 "targets": [ 128 "targets": [
129 { 129 {
130 "name": "default", 130 "name": "default",
@@ -148,7 +148,7 @@ @@ -148,7 +148,7 @@
148 // }, 148 // },
149 { 149 {
150 "name": "wdPlayer", 150 "name": "wdPlayer",
151 - "srcPath": "./wdPlayer", 151 + "srcPath": "./features/wdPlayer",
152 "targets": [ 152 "targets": [
153 { 153 {
154 "name": "default", 154 "name": "default",
@@ -160,7 +160,7 @@ @@ -160,7 +160,7 @@
160 }, 160 },
161 { 161 {
162 "name": "wdDetailPlayApi", 162 "name": "wdDetailPlayApi",
163 - "srcPath": "./wdDetailPlayApi", 163 + "srcPath": "./features/wdDetailPlayApi",
164 "targets": [ 164 "targets": [
165 { 165 {
166 "name": "default", 166 "name": "default",
@@ -172,7 +172,7 @@ @@ -172,7 +172,7 @@
172 }, 172 },
173 { 173 {
174 "name": "wdDetailPlayShortVideo", 174 "name": "wdDetailPlayShortVideo",
175 - "srcPath": "./wdDetailPlayShortVideo", 175 + "srcPath": "./features/wdDetailPlayShortVideo",
176 "targets": [ 176 "targets": [
177 { 177 {
178 "name": "default", 178 "name": "default",
@@ -184,7 +184,7 @@ @@ -184,7 +184,7 @@
184 }, 184 },
185 { 185 {
186 "name": "wdDetailPlayVod", 186 "name": "wdDetailPlayVod",
187 - "srcPath": "./wdDetailPlayVod", 187 + "srcPath": "./features/wdDetailPlayVod",
188 "targets": [ 188 "targets": [
189 { 189 {
190 "name": "default", 190 "name": "default",
@@ -196,7 +196,7 @@ @@ -196,7 +196,7 @@
196 }, 196 },
197 { 197 {
198 "name": "wdDetailPlayLive", 198 "name": "wdDetailPlayLive",
199 - "srcPath": "./wdDetailPlayLive", 199 + "srcPath": "./features/wdDetailPlayLive",
200 "targets": [ 200 "targets": [
201 { 201 {
202 "name": "default", 202 "name": "default",
  1 +export { CommonConstants } from './src/main/ets/constants/CommonConstants';
  2 +
  3 +export { BreakpointConstants } from './src/main/ets/constants/BreakpointConstants';
  4 +
  5 +export { ConfigConstants } from './src/main/ets/constants/ConfigConstants';
  6 +
  7 +// enum
  8 +export { BottomNavi } from './src/main/ets/enum/BottomNavi';
  9 +
  10 +export { CompStyle } from './src/main/ets/enum/CompStyle';
  11 +
  12 +export { CompType } from './src/main/ets/enum/CompType';
  13 +
  14 +export { NetDataStatusType } from './src/main/ets/enum/NetDataStatusType';
  15 +
  16 +export { ViewType } from './src/main/ets/enum/ViewType';
  17 +
  18 +export { DurationEnum } from './src/main/ets/enum/DurationEnum';
  19 +
  20 +export { ScreenType } from './src/main/ets/enum/ScreenType';
  1 +{
  2 + "license": "Apache-2.0",
  3 + "devDependencies": {},
  4 + "author": "",
  5 + "name": "wdconstant",
  6 + "description": "Please describe the basic information.",
  7 + "main": "Index.ets",
  8 + "version": "1.0.0",
  9 + "dependencies": {}
  10 +}
  1 +/**
  2 + * Constants for breakpoint.
  3 + */
  4 +export class BreakpointConstants {
  5 + /**
  6 + * Breakpoints that represent smaller device types.
  7 + * 最小宽度
  8 + */
  9 + static readonly BREAKPOINT_XS: string = 'xs';
  10 + /**
  11 + * Breakpoints that represent small device types.
  12 + * 小宽度
  13 + */
  14 + static readonly BREAKPOINT_SM: string = 'sm';
  15 + /**
  16 + * Breakpoints that represent middle device types.
  17 + * 中等宽度
  18 + */
  19 + static readonly BREAKPOINT_MD: string = 'md';
  20 + /**
  21 + * Breakpoints that represent large device types.
  22 + * 大宽度
  23 + */
  24 + static readonly BREAKPOINT_LG: string = 'lg';
  25 + /**
  26 + * Breakpoints that represent extra-large device types.
  27 + * 特大宽度
  28 + */
  29 + // static readonly BREAKPOINT_XL: string = 'xl';
  30 + /**
  31 + * Breakpoints that represent extra-extra-large device types.
  32 + * 超大宽度
  33 + */
  34 + // static readonly BREAKPOINT_XXL: string = 'xxl';
  35 +}
  1 +/**
  2 + * The constant of Common.
  3 + */
  4 +export class CommonConstants {
  5 + // offset
  6 + static readonly ZERO: number = 0;
  7 + static readonly NO_INDEX: number = -1;
  8 + static readonly NO_LENGTH: number = 0;
  9 +
  10 + /**
  11 + * 布局相关.
  12 + */
  13 + static readonly FULL_PARENT: string = '100%';
  14 + static readonly FULL_WIDTH: string = '100%';
  15 + static readonly FULL_HEIGHT: string = '100%';
  16 +
  17 + /**
  18 + * Component opacity value: 1.
  19 + */
  20 + static readonly FULL_OPACITY: number = 1;
  21 +}
  22 +;
  23 +
  1 +/**
  2 + * Config Constants.
  3 + */
  4 +export class ConfigConstants {
  5 + /**
  6 + * 应用id/appId
  7 + *
  8 + */
  9 + static readonly appId: string = "";
  10 + /**
  11 + * 终端id/terminalId
  12 + *
  13 + */
  14 + static readonly terminalId: string = "android";
  15 + /**
  16 + * 36_渠道编码(sappType)
  17 + *
  18 + */
  19 + // static readonly appType: string = "2"; // wap
  20 + static readonly appType: string = "3"; // 手机客户端App(安卓)
  21 +
  22 + static readonly clientType: string = "";
  23 + /**
  24 + * SourceID
  25 + *
  26 + */
  27 + static readonly sourceId: string = "";
  28 + /**
  29 + * 产品渠道应用对照关系:
  30 + */
  31 + static readonly appCode: string = "83092caa603a421aa0222308b3f6b27a";
  32 + /**
  33 + * 基线代码和客户端应用版本号规范
  34 + */
  35 + static readonly ptvCode: string = "";
  36 + /**
  37 + * 省份code/province(02->上海)
  38 + */
  39 + static readonly province: string = "02";
  40 + /**
  41 + * 正在播放的节目ID
  42 + */
  43 + static playingContentId?: string = undefined
  44 + /**
  45 + * 设备Id/deviceId
  46 + * 设备Id或者能标识请求端的唯一标识
  47 + */
  48 + static readonly DEVICE_ID: string = "5bfed7be-0497-487f-990b-991e5b828a6e";
  49 + /**
  50 + * base url VOD
  51 + */
  52 + static readonly BASE_URL_VOD: string = "";
  53 + /**
  54 + * base url Live
  55 + */
  56 + static readonly BASE_URL_LIVE: string = "";
  57 + /**
  58 + * 获取用户信息的服务器
  59 + */
  60 + static readonly BASE_URL: string = "";
  61 + /**
  62 + * 内容列表路径
  63 + */
  64 + static readonly CONTENT_LIST_PATH: string = "/display/v4/static";
  65 + /**
  66 + * 电视台(直播)列表路径
  67 + */
  68 + static readonly LIVE_TV_PATH: string = "/live/v2/tv-data";
  69 + /**
  70 + * 临时的详情URL
  71 + */
  72 + static readonly DETAIL_URL = "https://pd-people-uat.pdnews.cn/h/contentTop/110?hiddenTopNavigation=true"
  73 +}
  1 +/**
  2 + * 常见图片宽/高比
  3 + */
  4 +export const enum AspectRatioEnum {
  5 + // 常见纵向比例
  6 + ASPECT_RATIO_2_3 = 2 / 3,
  7 + ASPECT_RATIO_3_4 = 3 / 4,
  8 + ASPECT_RATIO_4_5 = 4 / 5,
  9 + ASPECT_RATIO_5_7 = 5 / 7,
  10 + ASPECT_RATIO_9_16 = 9 / 16,
  11 + ASPECT_RATIO_10_16 = 10 / 16,
  12 + ASPECT_RATIO_9_21 = 9 / 21,
  13 +
  14 + // 正方形
  15 + ASPECT_RATIO_1_1 = 1 / 1,
  16 +
  17 + // 常见横向比例
  18 + ASPECT_RATIO_7_5 = 7 / 5,
  19 + ASPECT_RATIO_5_4 = 5 / 4,
  20 + ASPECT_RATIO_4_3 = 4 / 3,
  21 + ASPECT_RATIO_3_2 = 3 / 2,
  22 + ASPECT_RATIO_16_10 = 16 / 10,
  23 + ASPECT_RATIO_16_9 = 16 / 9,
  24 + ASPECT_RATIO_21_9 = 21 / 9,
  25 +
  26 + // 其他常见比例
  27 + ASPECT_RATIO_2_1 = 2 / 1, // banner图宽高比
  28 + ASPECT_RATIO_1_2 = 1 / 2,
  29 + ASPECT_RATIO_75_45 = 75 / 45, // 角标宽高比
  30 +}
  1 +/**
  2 + * 底部导航 Bottom navigation item的下标/索引
  3 + * 新闻 人民号、 视频、 服务、 我的
  4 + */
  5 +export const enum BottomNavi {
  6 + NEWS = 0,
  7 + PEOPLE,
  8 + VIDEO,
  9 + SERVICE,
  10 + MINE,
  11 +}
  1 +/**
  2 + * 组件Style/展示样式
  3 + */
  4 +export const enum CompStyle {
  5 + Label_03 = 'Label-03', // 标题卡:icon+文字
  6 + Carousel_Layout_01 = 'Zh_Carousel_Layout-01', // 通用轮播卡:视频、直播、活动、专题、榜单、外链
  7 + Carousel_Layout_02 = 'Carousel_Layout-02', // 直播轮播卡:直播
  8 + Single_Row_01 = 'Zh_Single_Row-01', // 三格方形小卡(排名):专题、活动
  9 + Zh_Single_Row_01 = 'Zh_Single_Row-01', // 横划卡
  10 + Single_Row_02 = 'Zh_Single_Row-02', // 通用横划卡:视频、直播、专题
  11 + Single_Row_03 = 'Single_Row-03', // 直播横划卡:直播
  12 + Single_Row_04 = 'Single_Row-04', // 三格方形小卡:专题、活动
  13 + Single_Row_05 = 'Single_Row-05', // 专题横划卡:视频、直播、专题、活动、榜单、外链
  14 + Single_Column_01 = 'Single_Column-01', // 大卡横屏视频:视频、直播
  15 + Single_Column_02 = 'Single_Column-02', // 活动卡:活动
  16 + Single_Column_03 = 'Single_Column-03', // 地域榜单:榜单
  17 + Single_Column_04 = 'Single_Column-04', // 大卡横屏(带背景):视频、直播
  18 + Single_Column_05 = 'Single_Column-05', // 海报图卡:/
  19 + Single_Column_06 = 'Single_Column-06', // 留言板卡:/
  20 + Zh_Single_Column_02 = 'Zh_Single_Column-02', // 头图卡
  21 + Grid_Layout_01 = 'Grid_Layout-01', // 横屏宫格卡:视频、直播
  22 + Grid_Layout_02 = 'Grid_Layout-02', // 竖屏宫格卡:视频、直播、榜单
  23 + Masonry_Layout_01 = 'Masonry_Layout-01', // 双列瀑布流/瀑布流卡:视频、直播、专题、活动
  24 + Title_Abbr_01 = '11', // 标题缩略
  25 + Title_All_01 = '3', // 全标题
  26 + Single_ImageCard_03 = '13',//单图卡:3行标题
  27 + Single_ImageCard_01 = '6',//单图卡,竖图
  28 + ZhGrid_Layout_03 = 'Zh_Grid_Layout-03', //金刚位卡
  29 + Album_Card_01 = '17' //图卡集
  30 +}
  1 +/**
  2 + * 组件Type/展示类型
  3 + */
  4 +export const enum CompType {
  5 + LABEL = 'LABEL', // 标题组件
  6 + CAROUSEL_LAYOUT = 'CAROUSEL_LAYOUT', // 轮播组件,即Banner/焦点图
  7 + SINGLE_ROW = 'SINGLE_ROW', // 单行组件
  8 + SINGLE_COLUMN = 'SINGLE_COLUMN', // 单列组件
  9 + GRID_LAYOUT = 'GRID_LAYOUT', // 网格组件
  10 + MASONRY_LAYOUT = 'MASONRY_LAYOUT', // 瀑布流组件
  11 +}
  1 +/**
  2 + * 时间跨度/延时枚举常量值
  3 + * duration/delay/interval
  4 + */
  5 +export const enum DurationEnum {
  6 + DURATION_MS_50 = 50, // 50毫秒
  7 + DURATION_MS_100 = 100, // 50毫秒
  8 + DURATION_MS_1000 = 1000, // 1秒/1000ms
  9 + DURATION_MS_1500 = 1500, // 1.5秒
  10 + DURATION_1 = 1000, // 1秒
  11 + DURATION_2 = 2000, // 2秒
  12 + DURATION_3 = 3000, // 3秒
  13 + DURATION_4 = 4000, // 4秒
  14 +}
  1 +/**
  2 + * 请求网络数据状态类型
  3 + */
  4 +export const enum NetDataStatusType {
  5 + // 已初始化,但尚未发出请求
  6 + INITIAL = 0,
  7 + // 请求已取消/或无需网络请求/或不关心请求结果/或调用方没有设置回调callback
  8 + CANCEL,
  9 + // 已发出请求,但请求尚未返回;
  10 + LOADING,
  11 + // 请求已失败返回;
  12 + FAILED,
  13 + // 请求已成功返回;
  14 + LOADED
  15 +}
  1 +/**
  2 + * view展示种类
  3 + */
  4 +export const enum ViewType {
  5 + // 已初始化,但尚未发出请求
  6 + INITIAL = 0,
  7 + // 加载中:已发出请求,但请求尚未返回;
  8 + LOADING,
  9 + // 数据加载失败(重试)/failure
  10 + ERROR,
  11 + // 结果为空/无数据/空view
  12 + EMPTY,
  13 + // 已加载结束/非失败/非空/可以是仅一页数据,即楼层数据列表PAGE_GROUP_LIST/
  14 + LOADED,
  15 + // 已加载的是tab导航数据(可嵌套page的楼层列表)
  16 + NAV_BAR
  17 +}
  1 +{
  2 + "module": {
  3 + "name": "wdConstant",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true
  12 + }
  13 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "全局常量"
  6 + }
  7 + ]
  8 +}
  1 +export default class BuildProfile {
  2 + static readonly HAR_VERSION = '1.0.0';
  3 + static readonly BUILD_MODE_NAME = 'debug';
  4 + static readonly DEBUG = true;
  5 +}
  1 +export { BridgeWebViewControl } from './src/main/ets/core/BridgeWebViewControl'
  2 +
  3 +export { BridgeHandler } from './src/main/ets/core/BridgeHandler'
  4 +
  5 +export { Callback } from './src/main/ets/core/BridgeHandler'
  6 +
  7 +export { BridgeUtil } from './src/main/ets/utils/BridgeUtil'
  1 +import { harTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +{
  2 + "license": "Apache-2.0",
  3 + "devDependencies": {},
  4 + "author": "",
  5 + "name": "wdjsbridge",
  6 + "description": "Please describe the basic information.",
  7 + "main": "Index.ets",
  8 + "version": "1.0.0",
  9 + "dependencies": {}
  10 +}
  1 +// 事件对象,同wdBean的Action
  2 +export interface Action {
  3 + type: string;
  4 + // params?: Params; // 参数集合
  5 +}
  1 +import { StringUtils } from '../utils/StringUtils';
  2 +
  3 +/**
  4 + * 消息Message
  5 + */
  6 +export class CallBackMessage {
  7 + callbackId: string = ""; //callbackId
  8 + responseId: string = ""; //responseId
  9 + responseData: string = ""; //responseData
  10 + data?: string; //data of message
  11 + handlerName: string = ""; //name of handler
  12 +
  13 + /**
  14 + * TODO 待验证
  15 + * @returns
  16 + */
  17 + toJson(): string {
  18 + let cloneObject:object = JSON.parse(JSON.stringify(this))
  19 + return StringUtils.escapeDoubleQuotes(JSON.stringify(cloneObject))
  20 + }
  21 +
  22 + /**
  23 + * TODO 待验证
  24 + * @param jsonStr
  25 + * @returns
  26 + */
  27 + toList(jsonStr: string): Array<CallBackMessage> {
  28 + return JSON.parse(jsonStr)
  29 + }
  30 +}
  1 +import { Action } from './Action';
  2 +
  3 +/**
  4 + * 消息Message
  5 + */
  6 +export class Message {
  7 + callbackId: string = ""; //callbackId
  8 + responseId: string = ""; //responseId
  9 + responseData: string = ""; //responseData
  10 + data?: Action; //data of message
  11 + handlerName: string = ""; //name of handler
  12 +
  13 + /**
  14 + * TODO 待验证
  15 + * @returns
  16 + */
  17 + toJson(): string {
  18 + let jsonString: string = JSON.stringify(this)
  19 + return jsonString
  20 + }
  21 +
  22 + /**
  23 + * TODO 待验证
  24 + * @param jsonStr
  25 + * @returns
  26 + */
  27 + toList(jsonStr: string): Array<Message> {
  28 + return JSON.parse(jsonStr)
  29 + }
  30 +}
  1 +import { Action } from '../bean/Action'
  2 +
  3 +/**
  4 + * 注册回调接口
  5 + */
  6 +export type Callback = (data: string) => void
  7 +
  8 +export interface BridgeHandler {
  9 + handle: (data: Action, f: Callback) => void
  10 +}
  11 +
  12 +export class DefaultBridgeHandler implements BridgeHandler {
  13 + handle(data: Action, f: Callback) {
  14 + //1,2.3
  15 + f("DefaultHandler response data")
  16 + }
  17 +}
  1 +import webview from '@ohos.web.webview';
  2 +import HashMap from '@ohos.util.HashMap';
  3 +import { BridgeHandler, DefaultBridgeHandler, Callback } from './BridgeHandler';
  4 +import { BridgeUtil } from '../utils/BridgeUtil';
  5 +import { Message } from '../bean/Message';
  6 +import { CallBackMessage } from '../bean/CallBackMessage';
  7 +import { StringUtils } from '../utils/StringUtils';
  8 +
  9 +const TAG = 'BridgeWebViewControl';
  10 +
  11 +export class BridgeWebViewControl extends webview.WebviewController {
  12 + /**
  13 + *
  14 + */
  15 + private responseCallbacks: HashMap<string, Callback> = new HashMap<string, Callback>()
  16 + /**
  17 + *
  18 + */
  19 + private messageHandlers: HashMap<string, BridgeHandler> = new HashMap()
  20 + /**
  21 + * 页面加载前,不能处理消息
  22 + */
  23 + private uniqueId = 0;
  24 +
  25 + registerHandler(handlerName: string, handler: BridgeHandler) {
  26 + if (handler != null) {
  27 + this.messageHandlers.set(handlerName, handler)
  28 + }
  29 + }
  30 +
  31 + unregisterHandler(handlerName: string) {
  32 + if (handlerName != null) {
  33 + this.messageHandlers.remove(handlerName);
  34 + }
  35 + }
  36 +
  37 + /**
  38 + * @param url
  39 + * @param returnCallback
  40 + */
  41 + loadUrlCustom(url: string, returnCallback: Callback) {
  42 + this.loadUrl(url)
  43 + this.responseCallbacks.set(BridgeUtil.parseFunctionName(url), returnCallback);
  44 + }
  45 +
  46 + /**
  47 + * 刷新消息
  48 + */
  49 + flushMessageQueue() {
  50 + this.loadUrlCustom(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, (data: string) => {
  51 + let list: Array<Message> = JSON.parse(data)
  52 + if (list == null || list.length == 0) {
  53 + return
  54 + }
  55 + list.forEach(value => {
  56 + let responseId: string = value.responseId
  57 + // 是否是response CallBackFunction,收到消息,
  58 + if (StringUtils.isNotEmpty(responseId)) {
  59 + let call: Callback = this.responseCallbacks.get(responseId)
  60 + let responseData: string = value.responseData;
  61 + if (StringUtils.isEmpty(responseData) || call === undefined) {
  62 + return
  63 + }
  64 + call(responseData)
  65 + this.responseCallbacks.remove(responseId)
  66 + } else {
  67 + let responseFunction: Callback;
  68 + let callbackId: string = value.callbackId
  69 + if (StringUtils.isNotEmpty(callbackId)) {
  70 + responseFunction = (data: string) => {
  71 + let msg: CallBackMessage = new CallBackMessage()
  72 + msg.responseId = callbackId
  73 + msg.responseData = data
  74 + this.queueMessage(msg)
  75 + }
  76 + } else {
  77 + responseFunction = (data: string) => {
  78 + //TODO 更换log输出方式
  79 + // Logger.info(TAG, `default response:: ${data}`);
  80 + }
  81 + }
  82 + let handle: BridgeHandler
  83 + if (StringUtils.isNotEmpty(value.handlerName)) {
  84 + handle = this.messageHandlers.get(value.handlerName)
  85 + } else {
  86 + handle = new DefaultBridgeHandler()
  87 + }
  88 + if (handle != undefined && value.data != undefined) {
  89 + handle.handle(value.data, responseFunction)
  90 + }
  91 + }
  92 + })
  93 +
  94 + })
  95 +
  96 + }
  97 +
  98 + private queueMessage(msg: CallBackMessage) {
  99 + // //TODO
  100 + this.dispatchMessage(msg);
  101 + // }
  102 + }
  103 +
  104 + /**
  105 + * 消息分发,最终执行js方法
  106 + * @param msg
  107 + */
  108 + private dispatchMessage(msg: CallBackMessage) {
  109 + let messageJson: string = msg.toJson()
  110 + // messageJson = messageJson.replace("%7B", encodeURIComponent("%7B"));
  111 + // messageJson = messageJson.replace("%7D", encodeURIComponent("%7D"));
  112 + // messageJson = messageJson.replace("%22", encodeURIComponent("%22"));
  113 +
  114 + let javascriptCommand: string = StringUtils.formatStringForJS(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
  115 + this.runJavaScript(javascriptCommand)
  116 + }
  117 +
  118 + /**
  119 + * native 主动调用JSBridge方法
  120 + * @param msg
  121 + */
  122 + callHandle(handlerName: string, data: string, callBack: Callback) {
  123 + this.doSend(handlerName, data, callBack)
  124 + }
  125 +
  126 + private doSend(handlerName: string, data: string, callBack: Callback) {
  127 + let msg: CallBackMessage = new CallBackMessage()
  128 + if (StringUtils.isNotEmpty(data)) {
  129 + msg.data = data
  130 + }
  131 + if (StringUtils.isNotEmpty(handlerName)) {
  132 + msg.handlerName = handlerName
  133 + }
  134 + if (callBack != undefined) {
  135 + let callbackId: string = StringUtils.formatStringForJS(BridgeUtil.CALLBACK_ID_FORMAT, ++this.uniqueId + (BridgeUtil.UNDERLINE_STR + new Date().getTime()));
  136 + this.responseCallbacks.set(callbackId, callBack)
  137 + msg.callbackId = callbackId
  138 + }
  139 + this.queueMessage(msg);
  140 + }
  141 +
  142 + handlerReturnData(url: string) {
  143 + let functionName: string = BridgeUtil.getFunctionFromReturnUrl(url);
  144 + let callback = this.responseCallbacks.get(functionName);
  145 + let data: string = BridgeUtil.getDataFromReturnUrl(url);
  146 + if (callback != undefined) {
  147 + callback(data)
  148 + this.responseCallbacks.remove(functionName)
  149 + }
  150 + }
  151 +
  152 + //TODO
  153 + clean() {
  154 + this.responseCallbacks.clear()
  155 + this.messageHandlers.clear()
  156 + this.uniqueId = 0
  157 + }
  158 +}
  159 +
  160 +
  161 +
  1 +import webview from '@ohos.web.webview';
  2 +import { ResourceManager } from './ResourceManager';
  3 +
  4 +export class BridgeUtil {
  5 + static readonly YY_OVERRIDE_SCHEMA = "yy://";
  6 + static readonly YY_RETURN_DATA = BridgeUtil.YY_OVERRIDE_SCHEMA + "return/";
  7 + static readonly YY_FETCH_QUEUE = BridgeUtil.YY_RETURN_DATA + "_fetchQueue/";
  8 + static readonly EMPTY_STR = "";
  9 + static readonly UNDERLINE_STR = "_";
  10 + static readonly SPLIT_MARK = "/";
  11 + static readonly CALLBACK_ID_FORMAT = "JAVA_CB_%s";
  12 + static readonly JS_HANDLE_MESSAGE_FROM_JAVA = "javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');";
  13 + static readonly JS_FETCH_QUEUE_FROM_JAVA = "javascript:WebViewJavascriptBridge._fetchQueue();";
  14 + static readonly JAVASCRIPT_STR = "javascript:";
  15 +
  16 + // 例子 javascript:WebViewJavascriptBridge._fetchQueue(); --> _fetchQueue
  17 + static parseFunctionName(jsUrl: string): string {
  18 + return jsUrl.replace("javascript:WebViewJavascriptBridge.", "").replace("();", "");
  19 + }
  20 +
  21 + static getDataFromReturnUrl(url: string): string {
  22 + if (url.startsWith(BridgeUtil.YY_FETCH_QUEUE)) {
  23 + // return = [{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
  24 + return url.replace(BridgeUtil.YY_FETCH_QUEUE, BridgeUtil.EMPTY_STR);
  25 + }
  26 + // temp = _fetchQueue/[{"responseId":"JAVA_CB_2_3957","responseData":"Javascript Says Right back aka!"}]
  27 + let temp = url.replace(BridgeUtil.YY_RETURN_DATA, BridgeUtil.EMPTY_STR);
  28 + let functionAndData = temp.split(BridgeUtil.SPLIT_MARK);
  29 + if (functionAndData.length < 2) {
  30 + return ""
  31 + }
  32 + let result = ""
  33 + functionAndData.forEach((value) => {
  34 + result = result + value
  35 + });
  36 + return result
  37 +
  38 + }
  39 +
  40 + /**
  41 + * TODO
  42 + */
  43 + static releaseWebView() {
  44 +
  45 + }
  46 +
  47 + // 获取到传递信息的方法
  48 + // url = yy://return/_fetchQueue/[{"responseId":"JAVA_CB_1_360","responseData":"Javascript Says Right back aka!"}]
  49 + static getFunctionFromReturnUrl(url: string): string {
  50 + let temp = url.replace(BridgeUtil.YY_RETURN_DATA, BridgeUtil.EMPTY_STR);
  51 + let functionAndData = temp.split(BridgeUtil.SPLIT_MARK);
  52 + if (functionAndData.length >= 1) {
  53 + // functionAndData[0] = _fetchQueue
  54 + return functionAndData[0];
  55 + }
  56 + return ""
  57 + }
  58 +
  59 + /**
  60 + * 这里只是加载lib包中assets中的 WebViewJavascriptBridge.js
  61 + * @param controller
  62 + * @param path 路径
  63 + */
  64 + static webViewLoadLocalJs(context: Context, controller: webview.WebviewController) {
  65 + ResourceManager.getResourcesText(context, "WebViewJavascriptBridge.js").then((str) => {
  66 + //执行js注入
  67 + controller.runJavaScriptExt('javascript:' + str).then((msg) => {
  68 + console.log("Js注入结果:" + msg.getString());
  69 + })
  70 + })
  71 + }
  72 +
  73 +}
  1 +import buffer from '@ohos.buffer';
  2 +
  3 +export class ResourceManager {
  4 + /**
  5 + * 获取资源字符串内容
  6 + * @param context
  7 + * @param filename
  8 + * @returns
  9 + */
  10 + static getResourcesText(context: Context, filename: string): Promise<string> {
  11 + return new Promise<string>((success, error) => {
  12 + context.resourceManager.getRawFileContent(filename).then((content: Uint8Array) => {
  13 + if (!content) {
  14 + error("file is empty");
  15 + return
  16 + }
  17 + let result = buffer.from(content).toString("utf8");
  18 + if (result) {
  19 + success(result);
  20 + } else {
  21 + error("parse resources file result is empty");
  22 + }
  23 + }).catch((err: Error) => {
  24 + error(err);
  25 + })
  26 + })
  27 + }
  28 +}
  1 +/**
  2 + * StringUtils class.
  3 + */
  4 +export class StringUtils {
  5 +
  6 + /**
  7 + * Check obj is not empty.
  8 + *
  9 + * @param {object} obj
  10 + * @return {boolean} true(not empty)
  11 + */
  12 + static isNotEmpty(obj: any): boolean {
  13 + // return (obj && obj !== '');
  14 + // 或
  15 + return (obj !== undefined && obj !== null && obj !== '');
  16 + }
  17 +
  18 +
  19 + static isEmpty(...params: any): boolean {
  20 + if (params.length === 0) {
  21 + return true;
  22 + }
  23 + for (const param of params) {
  24 + if (!param) { // param === undefined || param === null || param === '';
  25 + return true;
  26 + }
  27 + }
  28 + return false;
  29 + }
  30 +
  31 +
  32 + static formatStringForJS(template: string, ...args: any[]): string {
  33 + let formattedString = template;
  34 + for (const arg of args) {
  35 + formattedString = formattedString.replace(/%s/, arg.toString());
  36 + }
  37 + return formattedString;
  38 + }
  39 +
  40 +
  41 + static escapeDoubleQuotes(obj:any): any {
  42 + if (typeof obj === 'string') {
  43 + return obj.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
  44 + } else if (typeof obj === 'object') {
  45 + for (let key in obj) {
  46 + if (obj.hasOwnProperty(key)) {
  47 + obj[key] = StringUtils.escapeDoubleQuotes(obj[key]);
  48 + }
  49 + }
  50 + }
  51 + return obj;
  52 + }
  53 +
  54 +}
  55 +// export default new StringUtils();
  1 +{
  2 + "module": {
  3 + "name": "wdJsBridge",
  4 + "type": "har",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ]
  11 + }
  12 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "JSBridge是Native(Harmony/Android/iOS)和H5(JavaScript)之间的桥梁,它构建双向通信通道,可以让【Native调用H5】以及让【H5调用Native】,使两者可交互数据。"
  6 + }
  7 + ]
  8 +}
  1 +//notation: js file can only use this kind of comments
  2 +//since comments will cause error when use in webview.loadurl,
  3 +//comments will be remove by java use regexp
  4 +(function() {
  5 + if (window.WebViewJavascriptBridge) {
  6 + return;
  7 + }
  8 +
  9 + var messagingIframe;
  10 + var bizMessagingIframe;
  11 + var sendMessageQueue = [];
  12 + var receiveMessageQueue = [];
  13 + var messageHandlers = {};
  14 +
  15 + var CUSTOM_PROTOCOL_SCHEME = 'yy';
  16 + var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
  17 +
  18 + var responseCallbacks = {};
  19 + var uniqueId = 1;
  20 +
  21 + // 创建消息index队列iframe
  22 + function _createQueueReadyIframe(doc) {
  23 + messagingIframe = doc.createElement('iframe');
  24 + messagingIframe.style.display = 'none';
  25 + doc.documentElement.appendChild(messagingIframe);
  26 + }
  27 + //创建消息体队列iframe
  28 + function _createQueueReadyIframe4biz(doc) {
  29 + bizMessagingIframe = doc.createElement('iframe');
  30 + bizMessagingIframe.style.display = 'none';
  31 + doc.documentElement.appendChild(bizMessagingIframe);
  32 + }
  33 + //set default messageHandler 初始化默认的消息线程
  34 + function init(messageHandler) {
  35 + if (WebViewJavascriptBridge._messageHandler) {
  36 + throw new Error('WebViewJavascriptBridge.init called twice');
  37 + }
  38 + WebViewJavascriptBridge._messageHandler = messageHandler;
  39 + var receivedMessages = receiveMessageQueue;
  40 + receiveMessageQueue = null;
  41 + for (var i = 0; i < receivedMessages.length; i++) {
  42 + _dispatchMessageFromNative(receivedMessages[i]);
  43 + }
  44 + }
  45 +
  46 + // 发送
  47 + function send(data, responseCallback) {
  48 + _doSend({
  49 + data: data
  50 + }, responseCallback);
  51 + }
  52 +
  53 + // 注册线程 往数组里面添加值
  54 + function registerHandler(handlerName, handler) {
  55 + messageHandlers[handlerName] = handler;
  56 + }
  57 + // 调用线程
  58 + function callHandler(handlerName, data, responseCallback) {
  59 + _doSend({
  60 + handlerName: handlerName,
  61 + data: data
  62 + }, responseCallback);
  63 + }
  64 +
  65 + //sendMessage add message, 触发native处理 sendMessage
  66 + function _doSend(message, responseCallback) {
  67 + if (responseCallback) {
  68 + var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
  69 + responseCallbacks[callbackId] = responseCallback;
  70 + message.callbackId = callbackId;
  71 + }
  72 +
  73 + sendMessageQueue.push(message);
  74 + messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  75 + }
  76 +
  77 + // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
  78 + function _fetchQueue() {
  79 + var messageQueueString = JSON.stringify(sendMessageQueue);
  80 + sendMessageQueue = [];
  81 + //android can't read directly the return data, so we can reload iframe src to communicate with java
  82 + if (messageQueueString !== '[]') {
  83 + bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
  84 + }
  85 + }
  86 +
  87 + //提供给native使用,
  88 + function _dispatchMessageFromNative(messageJSON) {
  89 + setTimeout(function() {
  90 + var message = JSON.parse(messageJSON);
  91 + var responseCallback;
  92 + //java call finished, now need to call js callback function
  93 + if (message.responseId) {
  94 + responseCallback = responseCallbacks[message.responseId];
  95 + if (!responseCallback) {
  96 + return;
  97 + }
  98 + responseCallback(message.responseData);
  99 + delete responseCallbacks[message.responseId];
  100 + } else {
  101 + //直接发送
  102 + if (message.callbackId) {
  103 + var callbackResponseId = message.callbackId;
  104 + responseCallback = function(responseData) {
  105 + _doSend({
  106 + responseId: callbackResponseId,
  107 + responseData: responseData
  108 + });
  109 + };
  110 + }
  111 +
  112 + var handler = WebViewJavascriptBridge._messageHandler;
  113 + if (message.handlerName) {
  114 + handler = messageHandlers[message.handlerName];
  115 + }
  116 + //查找指定handler
  117 + try {
  118 + handler(message.data, responseCallback);
  119 + } catch (exception) {
  120 + if (typeof console != 'undefined') {
  121 + console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
  122 + }
  123 + }
  124 + }
  125 + });
  126 + }
  127 +
  128 + //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
  129 + function _handleMessageFromNative(messageJSON) {
  130 + //console.log(messageJSON);
  131 + if (receiveMessageQueue) {
  132 + receiveMessageQueue.push(messageJSON);
  133 + }
  134 + _dispatchMessageFromNative(messageJSON);
  135 +
  136 + }
  137 +
  138 + var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
  139 + init: init,
  140 + send: send,
  141 + registerHandler: registerHandler,
  142 + callHandler: callHandler,
  143 + _fetchQueue: _fetchQueue,
  144 + _handleMessageFromNative: _handleMessageFromNative
  145 + };
  146 +
  147 + var doc = document;
  148 + _createQueueReadyIframe(doc);
  149 + _createQueueReadyIframe4biz(doc);
  150 + var readyEvent = doc.createEvent('Events');
  151 + readyEvent.initEvent('WebViewJavascriptBridgeReady');
  152 + readyEvent.bridge = WebViewJavascriptBridge;
  153 + doc.dispatchEvent(readyEvent);
  154 +})();
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { Logger } from './src/main/ets/utils/Logger'
  2 +
  3 +export { ResourcesUtils } from './src/main/ets/utils/ResourcesUtils'
  4 +
  5 +export { StringUtils } from './src/main/ets/utils/StringUtils'
  6 +
  7 +export { AppUtils } from './src/main/ets/utils/AppUtils';
  8 +
  9 +export { BasicDataSource } from './src/main/ets/utils/BasicDataSource';
  10 +
  11 +export { LazyDataSource } from './src/main/ets/utils/LazyDataSource'
  12 +
  13 +export { BreakpointSystem, BreakPointType } from './src/main/ets/utils/BreakPointSystem';
  14 +
  15 +export { ToastUtils } from './src/main/ets/utils/ToastUtils';
  16 +
  17 +export { WindowModel } from './src/main/ets/utils/WindowModel'
  18 +
  19 +export { SPHelper } from './src/main/ets/utils/SPHelper'
  20 +
  21 +export { AccountManagerUtils } from './src/main/ets/utils/AccountManagerUtils'
  22 +
  23 +export { CollectionUtils } from './src/main/ets/utils/CollectionUtils'
  24 +
  25 +export { DateTimeUtils } from './src/main/ets/utils/DateTimeUtils'
  26 +
  27 +export { DeviceUtil } from './src/main/ets/utils/DeviceUtil'
  28 +
  29 +export { DisplayUtils } from './src/main/ets/utils/DisplayUtils'
  30 +
  31 +export { SystemUtils } from './src/main/ets/utils/SystemUtils'
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +{
  2 + "license": "Apache-2.0",
  3 + "devDependencies": {},
  4 + "author": "",
  5 + "name": "wdkit",
  6 + "description": "Please describe the basic information.",
  7 + "main": "Index.ets",
  8 + "version": "1.0.0",
  9 + "dependencies": {}
  10 +}
  1 +import { StringUtils } from './StringUtils';
  2 +import { SPHelper } from './SPHelper';
  3 +import { Logger } from './Logger';
  4 +
  5 +const KEY_USER_TOKEN = 'userToken';
  6 +
  7 +const TAG: string = 'AccountManagerUtils';
  8 +
  9 +// 是否已登录hadLogin
  10 +let hasLogin: boolean = false;
  11 +
  12 +export class AccountManagerUtils {
  13 + // 是否已登录hadLogin
  14 + // private static hasLogin: boolean = undefined;
  15 +
  16 + constructor() {
  17 + }
  18 +
  19 + static async getUserToken(): Promise<string> {
  20 + let userToken = await SPHelper.default.get(KEY_USER_TOKEN, '') as string;
  21 + // Logger.info(TAG, 'getUserToken UserToken.' + userToken);
  22 + return userToken;
  23 + }
  24 +
  25 + static getUserTokenSync(): string {
  26 + let userToken = SPHelper.default.getSync(KEY_USER_TOKEN, '') as string;
  27 + // Logger.info(TAG, 'getUserToken UserToken.' + userToken);
  28 + return userToken;
  29 + }
  30 +
  31 + static async putUserToken(value: string) {
  32 + await SPHelper.default.save(KEY_USER_TOKEN, value);
  33 + }
  34 +
  35 + static putUserTokenSync(value: string) {
  36 + SPHelper.default.saveSync(KEY_USER_TOKEN, value);
  37 + }
  38 +
  39 + static async deleteUserToken() {
  40 + await SPHelper.default.delete(KEY_USER_TOKEN);
  41 + }
  42 +
  43 + static deleteUserTokenSync() {
  44 + SPHelper.default.deleteSync(KEY_USER_TOKEN);
  45 + }
  46 +
  47 + /**
  48 + * 是否已登录
  49 + * @param folder
  50 + * @param files
  51 + */
  52 + static async isLogin() {
  53 + Logger.info(TAG, 'isLogin hasLogin1:' + hasLogin);
  54 + let lastUserToken = await AccountManagerUtils.getUserToken()
  55 + Logger.info(TAG, 'isLogin lastUserToken:' + lastUserToken);
  56 + if (StringUtils.isEmpty(lastUserToken)) {
  57 + hasLogin = false;
  58 + Logger.info(TAG, "isLogin lastUserToken is empty");
  59 + } else {
  60 + hasLogin = true;
  61 + Logger.info(TAG, "isLogin lastUserToken is not empty,lastUserToken:" + lastUserToken);
  62 + }
  63 + // hasLogin = true;
  64 + Logger.info(TAG, 'isLogin hasLogin2:' + hasLogin);
  65 + return hasLogin;
  66 + }
  67 +
  68 + /**
  69 + * 是否已登录
  70 + * @param folder
  71 + * @param files
  72 + */
  73 + static isLoginSync() {
  74 + Logger.info(TAG, 'isLogin hasLogin1:' + hasLogin);
  75 + let lastUserToken = AccountManagerUtils.getUserTokenSync()
  76 + Logger.info(TAG, 'isLogin lastUserToken:' + lastUserToken);
  77 + if (StringUtils.isEmpty(lastUserToken)) {
  78 + hasLogin = false;
  79 + Logger.info(TAG, "isLogin lastUserToken is empty");
  80 + } else {
  81 + hasLogin = true;
  82 + Logger.info(TAG, "isLogin lastUserToken is not empty,lastUserToken:" + lastUserToken);
  83 + }
  84 + // hasLogin = true;
  85 + Logger.info(TAG, 'isLogin hasLogin2:' + hasLogin);
  86 + return hasLogin;
  87 + }
  88 +}
  89 +
  90 +// export const accountManagerUtils = new AccountManagerUtils();
  1 +import bundleManager from '@ohos.bundle.bundleManager';
  2 +import common from '@ohos.app.ability.common';
  3 +import { Logger } from './Logger';
  4 +
  5 +const TAG: string = 'AppUtils';
  6 +
  7 +/**
  8 + * 与应用相关属性或操作
  9 + */
  10 +export class AppUtils {
  11 + /**
  12 + * 获取应用名称
  13 + * 即:咪咕视频
  14 + */
  15 + static getAppName(context: common.Context): string {
  16 + // todo:获取到的是 $string:app_name
  17 + // return context.applicationInfo?.label;
  18 + return context.resourceManager.getStringByNameSync("app_name");
  19 + }
  20 +
  21 + /**
  22 + * 获取应用的包名
  23 + * 即:com.cmcc.myapplication
  24 + */
  25 + static getPackageName(context: common.Context): string {
  26 + return context.applicationInfo?.name;
  27 + }
  28 +
  29 + /**
  30 + * 获取应用版本号,如:1.0.0.0
  31 + * @returns 版本号字符串
  32 + */
  33 + static getAppVersionName(): string {
  34 + try {
  35 + let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT)
  36 + return bundleInfo?.versionName
  37 + } catch (e) {
  38 + Logger.warn(TAG, 'get app version error:' + e?.message);
  39 + }
  40 + return "";
  41 + }
  42 +
  43 + /**
  44 + * 获取应用版本编码,如:1000000
  45 + * @returns 应用版本编码
  46 + */
  47 + static getAppVersionCode(): string {
  48 + try {
  49 + let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT)
  50 + return bundleInfo?.versionCode + ""
  51 + } catch (e) {
  52 + Logger.warn(TAG, 'get app version error:' + e?.message);
  53 + }
  54 + return '';
  55 + }
  56 +}
  57 +
  1 +/**
  2 + * IDataSource处理数据侦听器的基本实现
  3 + */
  4 +export abstract class BasicDataSource<T> implements IDataSource {
  5 + private listeners: DataChangeListener[] = [];
  6 +
  7 + constructor() {
  8 + }
  9 +
  10 + // 获取数据总个数
  11 + public abstract totalCount(): number;
  12 +
  13 + // 获取索引对应的Item数据
  14 + public abstract getData(index: number): T | undefined;
  15 +
  16 + // 注册改变数据的控制器
  17 + public registerDataChangeListener(listener: DataChangeListener): void {
  18 + if (this.listeners.indexOf(listener) < 0) {
  19 + console.info('add DataChangeListener');
  20 + this.listeners.push(listener);
  21 + }
  22 + }
  23 +
  24 + // 注销改变数据的控制器
  25 + public unregisterDataChangeListener(listener: DataChangeListener): void {
  26 + const pos = this.listeners.indexOf(listener);
  27 + if (pos >= 0) {
  28 + console.info('remove DataChangeListener');
  29 + this.listeners.splice(pos, 1);
  30 + }
  31 + }
  32 +
  33 + // 通知控制器数据重新加载数据
  34 + public notifyDataReload(): void {
  35 + this.listeners.forEach(listener => {
  36 + listener.onDataReloaded();
  37 + })
  38 + }
  39 +
  40 + // 通知控制器数据增加
  41 + public notifyDataAdd(index: number): void {
  42 + this.listeners.forEach(listener => {
  43 + listener.onDataAdd(index);
  44 + })
  45 + }
  46 +
  47 + // 通知控制器数据位置变化
  48 + public notifyDataMove(from: number, to: number): void {
  49 + this.listeners.forEach(listener => {
  50 + listener.onDataMove(from, to);
  51 + })
  52 + }
  53 +
  54 + // 通知控制器数据删除
  55 + public notifyDataDelete(index: number): void {
  56 + this.listeners.forEach(listener => {
  57 + listener.onDataDelete(index);
  58 + })
  59 + }
  60 +
  61 + // 通知控制器数据变化
  62 + public notifyDataChange(index: number): void {
  63 + this.listeners.forEach(listener => {
  64 + listener.onDataChange(index);
  65 + })
  66 + }
  67 +}
  1 +import mediaQuery from '@ohos.mediaquery';
  2 +import { Logger } from './Logger';
  3 +
  4 +const TAG = 'BreakPointSystem';
  5 +
  6 +interface Breakpoint {
  7 + name: string
  8 + size: number
  9 + mediaQueryListener?: mediaQuery.MediaQueryListener
  10 +}
  11 +
  12 +// 响应式布局:"phone","tablet","2in1"
  13 +// https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/key-features/multi-device-app-dev/responsive-layout.md/
  14 +declare interface BreakPointTypeOption<T> {
  15 + xs: T; // [0, 320) (最小宽度-类型设备) // Wearable/可穿戴设备
  16 + sm: T; // [320,600) (小宽度-类型设备) // Phone/折叠设备当前折叠状态为折叠FOLDED/手机(HUAWEI Mate 40 Pro & NOH-AN00)/折叠后345.6vp
  17 + md: T; // [600,840) (中等宽度-类型设备) // Fold/折叠设备当前折叠状态为完全展开EXPANDED/折叠手机(HUAWEI Mate X5 & ALT-AL10)/展开后711.68vp
  18 + lg: T; // [840,1080) (大宽度-类型设备) // Tablet/2in1(PC与Tablet二合一产品)/TV/Car/PC/laptop
  19 + // xl?: T; // [1080,1440) (特大宽度-类型设备) // TV/如4K电视
  20 + // xxl?:T; //[1440,+8) (超大宽度-类型设备) // TV/如8K以上(8K/16K)电视
  21 +}
  22 +
  23 +export class BreakpointSystem {
  24 + // 上次通知的断点
  25 + private lastBreakpoint: string = ''
  26 + private breakpoints: Breakpoint[] = [
  27 + { name: 'xs', size: 0 },
  28 + { name: 'sm', size: 320 },
  29 + { name: 'md', size: 600 }, // 520vp
  30 + { name: 'lg', size: 840 }
  31 + ]
  32 +
  33 + private updateCurrentBreakpoint(breakpoint: string): void {
  34 + Logger.info(TAG, 'updateCurrentBreakpoint lastBreakpoint: ' + this.lastBreakpoint + ", breakpoint:" + breakpoint)
  35 + if (this.lastBreakpoint !== breakpoint) {
  36 + this.lastBreakpoint = breakpoint
  37 + AppStorage.setOrCreate<string>('currentBreakpoint', breakpoint)
  38 + // AppStorage.setOrCreate<string>(BreakpointConstants.CURRENT_BREAKPOINT, this.currentBreakpoint);
  39 + Logger.info(TAG, 'on current Breakpoint: ' + this.lastBreakpoint)
  40 + }
  41 + }
  42 +
  43 + public register() {
  44 + this.breakpoints.forEach((breakpoint: Breakpoint, index) => {
  45 + let condition: string
  46 + if (index === this.breakpoints.length - 1) {
  47 + condition = '(' + breakpoint.size + 'vp<=width' + ')'
  48 + } else {
  49 + condition = '(' + breakpoint.size + 'vp<=width<' + this.breakpoints[index + 1].size + 'vp)'
  50 + }
  51 + Logger.info(TAG, "register matchMediaSync condition:" + condition);
  52 + breakpoint.mediaQueryListener = mediaQuery.matchMediaSync(condition)
  53 + breakpoint.mediaQueryListener.on('change', (mediaQueryResult) => {
  54 + // Logger.info(TAG, "register change mediaQueryResult:" + JSON.stringify(mediaQueryResult));
  55 + if (mediaQueryResult.matches) {
  56 + // Logger.info(TAG, "register breakpoint.name:" + JSON.stringify(breakpoint.name));
  57 + this.updateCurrentBreakpoint(breakpoint.name)
  58 + }
  59 + })
  60 + })
  61 + }
  62 +
  63 + public unregister() {
  64 + this.breakpoints.forEach((breakpoint: Breakpoint) => {
  65 + if (breakpoint.mediaQueryListener) {
  66 + breakpoint.mediaQueryListener.off('change')
  67 + }
  68 + })
  69 + }
  70 +}
  71 +
  72 +export class BreakPointType<T> {
  73 + options: BreakPointTypeOption<T>
  74 +
  75 + constructor(option: BreakPointTypeOption<T>) {
  76 + this.options = option
  77 + }
  78 +
  79 + getValue(currentBreakPoint: string): T {
  80 + // return this.options[currentBreakPoint] as T;
  81 + // Logger.info(TAG, "getValue this.options:" + JSON.stringify(this.options));
  82 + if (currentBreakPoint === 'xs') {
  83 + return this.options.xs
  84 + } else if (currentBreakPoint === 'sm') {
  85 + return this.options.sm
  86 + } else if (currentBreakPoint === 'md') {
  87 + return this.options.md
  88 + } else if (currentBreakPoint === 'lg') {
  89 + return this.options.lg
  90 + // } else if (currentBreakPoint === 'xl') {
  91 + // return this.options.xl
  92 + // } else if (currentBreakPoint === 'xxl') {
  93 + // return this.options.xxl
  94 + } else {
  95 + // return undefined
  96 + return this.options.sm
  97 + }
  98 + }
  99 +}
  100 +
  1 +import LinkList from '@ohos.util.List';
  2 +import HashMap from '@ohos.util.HashMap';
  3 +
  4 +/**
  5 + * ArrayUtils class.
  6 + */
  7 +
  8 +export class CollectionUtils {
  9 + /**
  10 + * The Array utils tag.
  11 + */
  12 + private static readonly TAG: string = 'ArrayUtils';
  13 +
  14 + static isArray(value: any): boolean {
  15 + if (typeof Array.isArray === 'function') {
  16 + return Array.isArray(value);
  17 + } else {
  18 + return Object.prototype.toString.call(value) === '[object Array]';
  19 + }
  20 + }
  21 +
  22 + /**
  23 + * Check collection is empty or not.
  24 + * @param collection any[]
  25 + * @returns {boolean} true(empty)
  26 + */
  27 + static isEmpty(collection?: any[]): boolean {
  28 + return !collection || collection.length === 0;
  29 + }
  30 +
  31 + static isEmptyList<T>(list1?: LinkList<T>): boolean {
  32 + return !list1 || list1.length === 0;
  33 + }
  34 +
  35 + static isEmptyHashMap(obj?: HashMap<any, any>): boolean {
  36 + if (!obj) {
  37 + return true;
  38 + }
  39 + return obj.isEmpty();
  40 + }
  41 +
  42 + static isEmptyMap(obj?: Map<any, any>): boolean {
  43 + if (!obj) {
  44 + return true;
  45 + }
  46 + return Object.keys(obj).length === 0 && obj.constructor === Object;
  47 + }
  48 +
  49 + static isEmptyRecord(obj?: Record<string, string>): boolean {
  50 + if (!obj) {
  51 + return true;
  52 + }
  53 + return Object.keys(obj).length === 0 && obj.constructor === Object;
  54 + }
  55 +
  56 + /**
  57 + * Check collection is empty or not.
  58 + * @param collection any[]
  59 + * @returns {boolean} true(not empty)
  60 + */
  61 + static isNotEmpty(collection?: any[]): boolean {
  62 + if (!collection) {
  63 + return false
  64 + }
  65 + return collection.length > 0;
  66 + }
  67 +
  68 + static getListSize(collection?: any[]): number {
  69 + return CollectionUtils.isEmpty(collection) ? 0 : collection.length;
  70 + }
  71 +
  72 + static getListElement(collection?: any[], index?: number): any {
  73 + if (CollectionUtils.isEmpty(collection) || index === undefined) {
  74 + return null;
  75 + }
  76 + return index >= 0 && index < collection.length ? collection[index] : null;
  77 + }
  78 +
  79 + static convertArray<T>(objectList: T[] | T): T[] {
  80 + if (CollectionUtils.isArray(objectList)) {
  81 + return objectList as T[];
  82 + } else {
  83 + return [objectList as T];
  84 + }
  85 + }
  86 +
  87 + /**
  88 + * 把list2合入list1后
  89 + * @param list1
  90 + * @param list2
  91 + * @returns
  92 + */
  93 + static addAll<T>(list1: LinkList<T>, list2: LinkList<T>): LinkList<T> {
  94 + if (!list1) {
  95 + list1 = new LinkList<T>();
  96 + }
  97 + if (!list2) {
  98 + return list1;
  99 + }
  100 +
  101 + for (let index = 0; index < list2.length; index++) {
  102 + list1.add(list2[index])
  103 + }
  104 + return list1
  105 + }
  106 +
  107 + static deepCopy(objectList: any[]): any[] {
  108 + const list: any[] = [];
  109 + for (const objectItem of objectList) {
  110 + if (typeof objectItem !== 'object') {
  111 + list.push(objectItem);
  112 + continue;
  113 + }
  114 + if (objectItem.constructor === Date) {
  115 + list.push(new Date(objectItem));
  116 + continue;
  117 + }
  118 + if (objectItem.constructor === RegExp) {
  119 + list.push(new RegExp(objectItem));
  120 + continue;
  121 + }
  122 + if (objectItem.clone) {
  123 + list.push(objectItem.clone());
  124 + continue;
  125 + }
  126 + const newObj = new objectItem.constructor();
  127 + for (const key in objectItem) {
  128 + if (Object.hasOwnProperty.call(objectItem, key)) {
  129 + const val = objectItem[key];
  130 + newObj[key] = val;
  131 + }
  132 + }
  133 + list.push(newObj);
  134 + }
  135 + return list;
  136 + }
  137 +
  138 + static deepCopyNumber(values: Set<number>): Set<number> {
  139 + const newSet: Set<number> = new Set();
  140 + values.forEach((value => {
  141 + newSet.add(value);
  142 + }));
  143 + return newSet;
  144 + }
  145 +
  146 + /**
  147 + * Uint8Array to string
  148 + * @param fileData Uint8Array
  149 + */
  150 + static uint8ArrayToString(fileData: Uint8Array): string {
  151 + return decodeURIComponent(escape(String.fromCharCode(...fileData)));
  152 + }
  153 +
  154 + /**
  155 + * string to Uint8Array
  156 + * @param str string
  157 + */
  158 + static stringToUint8Array(str: string): Uint8Array {
  159 + // spilt('') Each character is divided between them
  160 + const arr = unescape(encodeURIComponent(str)).split('').map(val => val.charCodeAt(0));
  161 + return new Uint8Array(arr);
  162 + }
  163 +
  164 + /**
  165 + * 截取集合部分数据。
  166 + * start:0 - end:20 截取0-19,共20个数据
  167 + */
  168 + static getSubElements(collection?: any[], start?: number, end?: number): any[] {
  169 + if (CollectionUtils.isEmpty(collection) || start === undefined || end === undefined) {
  170 + return null;
  171 + }
  172 + if (start < 0 || end < start) {
  173 + return null;
  174 + }
  175 + if (end > collection.length) {
  176 + return null;
  177 + }
  178 + let ss = collection.slice(start, end);
  179 + ss;
  180 + return collection.slice(start, end);
  181 + }
  182 +}
  183 +
  1 +import { StringUtils } from './StringUtils';
  2 +
  3 +/**
  4 + * 日期/时间工具
  5 + */
  6 +// let myDate = new Date();
  7 +// myDate.getTime() // 获取以毫秒为单位的时间值。
  8 +// myDate.getFullYear(); // 获取完整的年份(4位,1970-????)
  9 +// myDate.getMonth(); // 获取myDate月份(0-11,0代表1月)
  10 +// myDate.getDate(); // 获取myDate日期(1-31)
  11 +// myDate.getDay(); // 获取myDate星期X(0-6,0代表星期天)
  12 +// myDate.getTime(); // 获取myDate时间(从1970.1.1开始的毫秒数)
  13 +// myDate.getHours(); // 获取myDate小时数(0-23)
  14 +// myDate.getMinutes(); // 获取myDate分钟数(0-59)
  15 +// myDate.getSeconds(); // 获取myDate秒数(0-59)
  16 +// myDate.getMilliseconds(); // 获取myDate毫秒数(0-999)
  17 +// myDate.toLocaleDateString(); // 获取当前日期:使用当前或指定的区域设置将日期转换为字符串。
  18 +// myDate.toLocaleTimeString(); // 获取当前时间:使用当前或指定的区域设置将时间转换为字符串。
  19 +// myDate.toLocaleString(); // 获取日期与时间:使用当前或指定的区域设置将日期和时间转换为字符串
  20 +
  21 +// 日期时间的年月日时分秒以及毫秒的各个字段
  22 +// export class DateOpt {
  23 +// year: string = ''; // 年 小写y
  24 +// month: string = ''; // 月 大写M
  25 +// day: string = ''; // 日 小写d
  26 +// hour: string = ''; // 时 大写H(24小时制)
  27 +// // hh: string = ''; // 时 小写H(12小时制)
  28 +// minute: string = ''; // 分 小写m
  29 +// second: string = ''; // 秒 小写s
  30 +// millisecond: string = ''; // 毫秒 大写S
  31 +// }
  32 +
  33 +export class DateTimeUtils {
  34 + static readonly MAX_TIME = Math.pow(10, 8) * 24 * 60 * 60 * 1000; // 8640000000000000 毫秒
  35 + static readonly MIN_TIME = -DateTimeUtils.MAX_TIME; // -8640000000000000 毫秒
  36 +
  37 + // 日期+时间格式
  38 + static readonly PATTERN_DATE_TIME_DEFAULT: string = 'yyyyMMddHHmmss'; // 年月日时分秒
  39 + static readonly PATTERN_DATE_TIME_HYPHEN: string = 'yyyy-MM-dd HH:mm:ss'; // 日期中包含连字符/中划线(HYPHEN),时间是以冒号(Colon)分割
  40 + static readonly PATTERN_DATE_TIME_HYPHEN_MS: string = 'yyyy-MM-dd HH:mm:ss.SSS'; // 日期中包含连字符/中划线(HYPHEN),时间是以冒号(Colon)分割
  41 + static readonly PATTERN_DATE_TIME_SLASH: string = 'yyyy/MM/dd HH:mm:ss'; // 日期中包含正斜杠(forward slash '/')
  42 + static readonly PATTERN_DATE_TIME_BACK_SLASH: string = 'yyyy\\MM\\dd HH:mm:ss'; // 日期中包含反斜杠(back slash '\')
  43 + static readonly PATTERN_DATE_TIME_DOT: string = 'yyyy.MM.dd HH:mm:ss'; // 日期中包含英文小圆点(DOT '.')
  44 + static readonly PATTERN_DATE_TIME_CN: string = 'yyyy年MM月dd日 HH:mm:ss'; // 日期中包含包含中文年月日
  45 + static readonly PATTERN_DATE_TIME_MS: string = 'yyyyMMddHHmmssSSS'; // 时间中包含毫秒
  46 + static readonly PATTERN_DATE_TIME_WITHOUT_SECOND: string = 'yyyyMMddHHmm'; // 时间中不包含秒
  47 + static readonly PATTERN_DATE_TIME_SIMPLIFY: string = 'MM/dd HH:mm'; // 精简的日期+时间(不包含年份和秒), 月/日 时:分
  48 + static readonly PATTERN_DATE_SLASH_WITHOUT_YEAR2: string = 'MM-dd'; // 日期中不包含年份
  49 +
  50 + // 仅日期格式(不包含时间)
  51 + static readonly PATTERN_DATE_DEFAULT: string = 'yyyyMMdd'; // 年月日
  52 + static readonly PATTERN_DATE_HYPHEN: string = 'yyyy-MM-dd'; // 日期中包含连字符/中划线(HYPHEN)
  53 + static readonly PATTERN_DATE_SLASH: string = 'yyyy/MM/dd'; // 日期中包含正斜杠(forward slash '/')
  54 + static readonly PATTERN_DATE_BACK_SLASH: string = 'yyyy\\MM\\dd'; // 日期中包含反斜杠(back slash '\')
  55 + static readonly PATTERN_DATE_DOT: string = 'yyyy.MM.dd'; // 日期中包含英文小圆点(DOT '.')
  56 + static readonly PATTERN_DATE_CN: string = 'yyyy年MM月dd日'; // 日期中包含包含中文年月日
  57 + static readonly PATTERN_DATE_SLASH_WITHOUT_YEAR: string = 'MM/dd'; // 日期中不包含年份
  58 + static readonly PATTERN_DATE_CN_WITHOUT_YEAR: string = 'MM月dd日'; // 日期中不包含年份,且month与day是中文
  59 +
  60 + // 仅时间格式(不包含日期)
  61 + static readonly PATTERN_TIME_DEFAULT: string = 'HHmmss'; // 时分秒
  62 + static readonly PATTERN_TIME_COLON: string = 'HH:mm:ss'; // 时间是以冒号(Colon)分割
  63 + static readonly PATTERN_TIME_MS: string = 'HHmmssSSS'; // 时间中包含毫秒
  64 + static readonly PATTERN_TIME_COLON_DOT_MS: string = 'HH:mm:ss.SSS'; // 时间是以冒号(Colon)与点号(DOT)分割,且包含毫秒
  65 + static readonly PATTERN_TIME_WITHOUT_SECOND: string = 'HHmm'; // 时间中不包含秒
  66 + static readonly PATTERN_TIME_COLON_WITHOUT_SECOND: string = 'HH:mm'; // 时间是以冒号(Colon)分割,且不包含秒
  67 + static readonly PATTERN_TIME_COLON_WITHOUT_HOUR: string = 'mm:ss'; // 时间是以冒号(Colon)分割,且不包含小时(分钟长度至少2位,可能是3位)
  68 +
  69 + static readonly PATTERN_WEEK: string = 'E'; // 周几/星期几
  70 +
  71 + static readonly MONTHS_IN_YEAR: number = 12; // 1年12个月
  72 + static readonly QUARTERS_IN_YEAR: number = 4; // 1年4个季度
  73 + static readonly MONTHS_IN_QUARTER: number = 3; // 1季度3个月
  74 + static readonly DAYS_MONTH: number[] = [31, 30, 29, 28]; // 每月多少天:大月(1/3/5/7/8/10/12月);小月(4/6/9/11月);2月(闰年29天/非闰月28天)
  75 + static readonly DAYS_IN_WEEK: number = 7; // 1周7天
  76 + static readonly HOURS_IN_DAY = 24; // 1天24小时
  77 + // 分钟
  78 + static readonly MINUTES_IN_HOUR = 60; // 1小时60分(时分秒之间的进制RADIX)
  79 + static readonly MINUTES_IN_DAY = 1440 // 60 * 24 // 1天1440分钟
  80 + // 秒
  81 + static readonly SECONDS_IN_MINUTE = 60; // 1分60秒(时分秒之间的进制RADIX)
  82 + static readonly SECONDS_IN_HOUR = 3600 // 60 * 60 // 1小时3600秒
  83 + static readonly SECONDS_IN_DAY = 86400 // 60 * 60 * 24 // 1天86400秒
  84 + // 毫秒
  85 + static readonly MILLISECONDS_IN_SECOND: number = 1000; // 1秒是1000毫秒,秒与毫秒之间的转换进制RADIX
  86 + static readonly MILLISECONDS_IN_MINUTE: number = 60000; // 1000 * 60 // 1分钟是60000毫秒
  87 + static readonly MILLISECONDS_IN_HOUR: number = 3600000; // 1000 * 60 * 60 // 1小时是3600000毫秒
  88 + static readonly MILLISECONDS_IN_DAY: number = 86400000; // 1000 * 60 * 60 * 24 // 1天是86400000毫秒
  89 +
  90 + static readonly CHINESE_YEAR: string = '年';
  91 + static readonly CHINESE_MONTH: string = '月';
  92 + static readonly CHINESE_DAY: string = '日';
  93 + static readonly CHINESE_LAST_WEEK: string = '上周';
  94 + static readonly CHINESE_YESTERDAY: string = '昨天';
  95 + static readonly CHINESE_TODAY: string = '今天';
  96 + static readonly CHINESE_TOMORROW: string = '明天';
  97 + static readonly CHINESE_NEXT_WEEK: string = '下周';
  98 + // 周x/星期X
  99 + static readonly CHINESE_WEEK1: string[] = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
  100 + static readonly CHINESE_WEEK2: string[] = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
  101 + static readonly ENGLISH_WEEK1 = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
  102 + static readonly ENGLISH_WEEK2 = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  103 + static readonly ENGLISH_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  104 +
  105 + /**
  106 + * 获取当前日期:年月日
  107 + * 返回格式,默认如:yyyyMMdd,20230926
  108 + */
  109 + static getCurDate(format = DateTimeUtils.PATTERN_DATE_DEFAULT): string {
  110 + const DATETIME = new Date()
  111 + return DateTimeUtils.formatDate(DATETIME.getTime(), format)
  112 + }
  113 +
  114 + /**
  115 + * 获取当前时间:时分秒
  116 + * 返回格式,默认如:HHmmss,230559
  117 + */
  118 + static getCurTime(format = DateTimeUtils.PATTERN_TIME_DEFAULT): string {
  119 + const DATETIME = new Date()
  120 + return DateTimeUtils.formatDate(DATETIME.getTime(), format)
  121 + }
  122 +
  123 + /**
  124 + * 获取当前时间戳,毫秒
  125 + * */
  126 + public static getTimeStamp(): number {
  127 + return new Date().getTime();
  128 + }
  129 +
  130 + /**
  131 + * 当数字转字符串不足maxLength位时,用前导0补足maxLength位
  132 + * @param value-数据值
  133 + * @returns
  134 + * 年份是4位
  135 + * 月/日/时/分/秒,都是2位
  136 + * 毫秒是3位
  137 + */
  138 + static fill(num: number, maxLength: number = 2): string {
  139 + let value: string = '' + num
  140 + return value.padStart(maxLength, '0')
  141 + }
  142 +
  143 + /**
  144 + * 获取两个时间点的时间差
  145 + * @param milliseconds1
  146 + * @param milliseconds2
  147 + * @returns Duration
  148 + */
  149 + static getDuration(milliseconds1: number, milliseconds2: number): number {
  150 + let date1 = new Date(milliseconds1)
  151 + let date2 = new Date(milliseconds2)
  152 + let timeDiff = Math.abs(date2.getTime() - date1.getTime());
  153 + return timeDiff;
  154 + }
  155 +
  156 + /**
  157 + * 格式化Duration时间
  158 + * @param duration 毫秒
  159 + * @returns 返回格式化后的Duration时间
  160 + */
  161 + static getFormattedDuration(duration: number): string {
  162 + // 也可以直接先除以1000转换为秒后再计算 // duration = duration/1000
  163 + // Math.floor 用于向下取整
  164 + let hour = Math.floor(duration / (DateTimeUtils.MILLISECONDS_IN_HOUR))
  165 + // 用总毫秒数duration减去hour所对应的毫秒数差,再除以每分钟所占用的毫秒数
  166 + let minute = Math.floor((duration - hour * (DateTimeUtils.MILLISECONDS_IN_HOUR)) / (DateTimeUtils.MILLISECONDS_IN_MINUTE));
  167 + let second = Math.floor(duration / DateTimeUtils.MILLISECONDS_IN_SECOND) % DateTimeUtils.SECONDS_IN_MINUTE; // 先除以1000,转换为秒;再对整分钟取余
  168 + if (hour > 0) {
  169 + return `${DateTimeUtils.fill(hour)}:${DateTimeUtils.fill(minute)}:${DateTimeUtils.fill(second)}`
  170 + }
  171 + return `${DateTimeUtils.fill(minute)}:${DateTimeUtils.fill(second)}`
  172 + }
  173 +
  174 + /**
  175 + * 格式化显示倒计时
  176 + * @param milliseconds 时间戳 毫秒
  177 + * @returns 返回格式化后的Duration时间
  178 + */
  179 + static getDurationToNow(milliseconds: number): string {
  180 + let nowDateTime = new Date();
  181 + let duration = milliseconds - nowDateTime.getTime();
  182 + if (duration < 0) {
  183 + duration = 0;
  184 + }
  185 + return DateTimeUtils.getFormattedDuration(duration)
  186 + }
  187 +
  188 + /**
  189 + * 根据日期/时间字符串反序列化对应的时间戳
  190 + * @param dateTimeString string 日期+时间字符串
  191 + * 列举字符串格式,如:
  192 + * 20231129,yyyyMMdd
  193 + * 20231126230559 yyyyMMddHHmmss
  194 + * 20231126230559729 yyyyMMddHHmmssSSS
  195 + * 2023-11-26 23:05:59.729, yyyy-MM-dd HH:mm:ss.SSS(先移除所有中划线和冒号及空格等非数字字符)
  196 + * 日期/时间字符串中包含连字符/中划线(Hyphen/Strike)/移除冒号(Colon)/移除空白字符(包括空格、制表符、换行符等)/移除正斜杠(forward slash '/')/移除反斜杠(back slash '\')
  197 + * @param format:string 日期+时间的格式,默认格式:yyyy-MM-dd
  198 + * @param referenceDate:Date 参考日期+时间
  199 + * @returns 转换后的时间戳:milliseconds 毫秒
  200 + * let dateTimeStr:string = "(预告) 2024年12月24日 02:50 开播"
  201 + * let formatPattern:string = "(预告) yyyy年MM月dd日 HH:mm 开播"
  202 + * let parsedDate = DateTimeUtils.parseDate(dateTimeStr, formatPattern)
  203 + */
  204 + static parseDate(dateTimeString: string, format: string = DateTimeUtils.PATTERN_DATE_HYPHEN, referenceDate?: Date): number {
  205 + if (!dateTimeString) {
  206 + return 0
  207 + }
  208 + if (!referenceDate) {
  209 + referenceDate = new Date(); // 参考当前时间
  210 + }
  211 +
  212 + const optPatternList: string[] = ['y+', 'M+', 'd+', 'H+', 'm+', 's+', 'S+'];
  213 + let targetYear: number = 0 // 年
  214 + let targetMonth: number = 0 // 月, monthIndex
  215 + let targetDay: number = 0 // 日
  216 + let targetHour: number = 0 // 时
  217 + let targetMinute: number = 0 // 分
  218 + let targetSecond: number = 0 // 秒
  219 + let targetMillisecond: number = 0 // 毫秒
  220 + for (let i = 0; i < optPatternList.length; i++) {
  221 + const optPattern = optPatternList[i];
  222 + const regExp = new RegExp(optPattern, 'g');
  223 + // console.log(optPattern);
  224 + let matches = regExp.exec(format);
  225 + if (!matches) {
  226 + continue
  227 + }
  228 + // console.dir(matches)
  229 + // console.dir(matches?.[0])
  230 + // console.dir(matches?.index)
  231 + let pick: number = StringUtils.parseNumber(dateTimeString.substring(matches.index, matches.index + matches?.[0].length))
  232 + // console.dir(pick)
  233 + switch (optPattern) {
  234 + case 'y+':
  235 + if (pick >= -271821 && pick <= 275760) { // 根据最大日期/时间与根据最小日期/时间计算出的年份范围
  236 + targetYear = pick
  237 + } else {
  238 + targetYear = referenceDate.getFullYear() // 当解析不到年份时,则设置为今年
  239 + }
  240 + break;
  241 + case 'M+': // monthIndex between 0 and 11
  242 + if (pick >= 1 && pick <= 12) {
  243 + targetMonth = pick - 1
  244 + } else {
  245 + targetMonth = referenceDate.getMonth() // 当解析不到月份时,则设置为本月
  246 + }
  247 + break;
  248 + case 'd+': // between 1 and 31.
  249 + if (pick >= 1 && pick <= 31) {
  250 + targetDay = pick
  251 + } else {
  252 + targetDay = referenceDate.getDate()
  253 + }
  254 + break;
  255 + case 'H+': // from 0 to 23
  256 + if (pick >= 0 && pick <= 23) {
  257 + targetHour = pick
  258 + } else {
  259 + targetDay = referenceDate.getDate()
  260 + }
  261 + break;
  262 + case 'm+': // from 0 to 59
  263 + if (pick >= 0 && pick <= 59) {
  264 + targetMinute = pick
  265 + } else {
  266 + targetDay = referenceDate.getMinutes()
  267 + }
  268 + break;
  269 + case 's+': // from 0 to 59
  270 + if (pick >= 0 && pick <= 59) {
  271 + targetSecond = pick
  272 + } else {
  273 + targetSecond = referenceDate.getSeconds()
  274 + }
  275 + break;
  276 + case 'S+': // from 0 to 999
  277 + if (pick >= 0 && pick <= 999) {
  278 + targetMillisecond = pick
  279 + } else {
  280 + targetMillisecond = referenceDate.getMilliseconds()
  281 + }
  282 + break;
  283 + default:
  284 + break;
  285 + }
  286 + }
  287 + let targetDate: Date = new Date(targetYear, targetMonth, targetDay, targetHour, targetMinute, targetSecond, targetMillisecond)
  288 + return targetDate.getTime()
  289 + }
  290 +
  291 + /**
  292 + * 把整数时间戳格式化为指定格式的日期+时间字符串
  293 + * @param timestamp:毫秒
  294 + * @param format 指定要格式化的pattern,默认格式:yyyy-MM-dd
  295 + * @returns 格式化后的字符串
  296 + * 举例:
  297 + * let date_fmt = DateTimeUtils.formatDate(dateTime, "预告 (E) MM月dd日 HH:mm 开播")
  298 + */
  299 + static formatDate(timestamp: number, format: string = DateTimeUtils.PATTERN_DATE_HYPHEN): string {
  300 + try {
  301 + const date = new Date(timestamp);
  302 + let targetYear: string = date.getFullYear().toString();
  303 + let targetMonth: string = DateTimeUtils.fill(date.getMonth() + 1);
  304 + let targetDay: string = DateTimeUtils.fill(date.getDate());
  305 + let targetHours: string = DateTimeUtils.fill(date.getHours());
  306 + let targetMinutes: string = DateTimeUtils.fill(date.getMinutes());
  307 + let targetSeconds: string = DateTimeUtils.fill(date.getSeconds());
  308 + let targetMilliseconds: string = DateTimeUtils.fill(date.getMilliseconds(), 3);
  309 + let targetDayOfWeek: number = date.getDay();
  310 +
  311 + const dateOptionPatternList: string[] = ['y+', 'M+', 'd+', 'H+', 'm+', 's+', 'S+', 'E'];
  312 + for (let i = 0; i < dateOptionPatternList.length; i++) {
  313 + const dateOptionPattern = dateOptionPatternList[i];
  314 + const dateOptionRegExp = new RegExp(dateOptionPattern);
  315 + let match = dateOptionRegExp.exec(format); // 使用 RegExp 的exec 方法在目标字符串中查找匹配项
  316 + if (!match) {
  317 + continue
  318 + }
  319 + switch (dateOptionPattern) {
  320 + case 'y+':
  321 + format = format.replace(dateOptionRegExp, targetYear);
  322 + break;
  323 + case 'M+':
  324 + format = format.replace(dateOptionRegExp, targetMonth);
  325 + break;
  326 + case 'd+':
  327 + format = format.replace(dateOptionRegExp, targetDay);
  328 + break;
  329 + case 'H+':
  330 + format = format.replace(dateOptionRegExp, targetHours);
  331 + break;
  332 + case 'm+':
  333 + format = format.replace(dateOptionRegExp, targetMinutes);
  334 + break;
  335 + case 's+':
  336 + format = format.replace(dateOptionRegExp, targetSeconds);
  337 + break;
  338 + case 'S+':
  339 + format = format.replace(dateOptionRegExp, targetMilliseconds);
  340 + break;
  341 + case 'E': // from 0 to 6; the day of the week
  342 + format = format.replace(dateOptionRegExp, DateTimeUtils.CHINESE_WEEK1[targetDayOfWeek]);
  343 + break;
  344 + default:
  345 + break;
  346 + }
  347 + }
  348 + return format;
  349 + } catch (error) {
  350 + console.error(`ERROR formatDate error: ${error}.`);
  351 + return "";
  352 + }
  353 + }
  354 +
  355 + /**
  356 + * 构造一周中的哪一天/构造星期X/周X
  357 + * @param timestamp
  358 + * @returns
  359 + */
  360 + static buildDayOfWeek(timestamp: number, weeks: string[] = DateTimeUtils.CHINESE_WEEK1): string {
  361 + try {
  362 + const date = new Date(timestamp);
  363 + let day = date.getDay()
  364 + let week = weeks[day];
  365 + return week ?? ''
  366 + } catch (error) {
  367 + console.error(`ERROR formatDate error: ${error}.`);
  368 + return ''
  369 + }
  370 + }
  371 +
  372 + static startOfDay(argument: number): Date {
  373 + const _date = new Date(argument);
  374 + _date.setHours(0, 0, 0, 0);
  375 + return _date;
  376 + }
  377 +
  378 + static isSameDay(dateLeft: number, dateRight: number): boolean {
  379 + const dateLeftStartOfDay = DateTimeUtils.startOfDay(dateLeft);
  380 + const dateRightStartOfDay = DateTimeUtils.startOfDay(dateRight);
  381 + return dateLeftStartOfDay.getTime() === dateRightStartOfDay.getTime();
  382 + }
  383 +
  384 + static addDays(argument: number, amount: number): Date {
  385 + const _date = new Date(argument);
  386 + if (amount == 0) {
  387 + return _date;
  388 + }
  389 + _date.setDate(_date.getDate() + amount);
  390 + return _date;
  391 + }
  392 +
  393 + static subDays(date: number, amount: number): Date {
  394 + return DateTimeUtils.addDays(date, -amount);
  395 + }
  396 +
  397 + static isToday(date: number,): boolean {
  398 + return DateTimeUtils.isSameDay(date, Date.now());
  399 + }
  400 +
  401 + static isYesterday(date: number): boolean {
  402 + return DateTimeUtils.isSameDay(date, DateTimeUtils.subDays(Date.now(), 1).getTime());
  403 + }
  404 +
  405 + static isTomorrow(date: number): boolean {
  406 + return DateTimeUtils.isSameDay(date, DateTimeUtils.addDays(Date.now(), 1).getTime());
  407 + }
  408 +
  409 + // 检查指定的日期是过去的吗?
  410 + static isPast(_date: Date): boolean {
  411 + return _date.getTime() < Date.now();
  412 + }
  413 +
  414 + // 检查指定的日期在未来/将来吗?
  415 + static isFuture(_date: Date): boolean {
  416 + return _date.getTime() > Date.now();
  417 + }
  418 +
  419 + // 检查_date是在dateToCompare之后吗?,如
  420 + // const result = isAfter(new Date(1989, 6, 10), new Date(1987, 1, 11))
  421 + // => true
  422 + static isAfter(_date: Date, dateToCompare: Date): boolean {
  423 + return _date.getTime() > dateToCompare.getTime();
  424 + }
  425 +
  426 + // 检查_date是在dateToCompare之前吗?,如
  427 + // const result = isBefore(new Date(1989, 6, 10), new Date(1987, 1, 11))
  428 + // => false
  429 + static isBefore(_date: Date, dateToCompare: Date): boolean {
  430 + return _date.getTime() < dateToCompare.getTime();
  431 + }
  432 +
  433 + /**
  434 + * 获取文章发布时间
  435 + * */
  436 + public static getCommentTime(publishTime: number): string {
  437 + let currentTime: number = new Date().getTime();
  438 + let timeGap = currentTime - publishTime;
  439 + let timeStr = ""
  440 + if (timeGap >= 60 * 60 * 1000 * 48) {
  441 + let publishYear = new Date(publishTime).getFullYear();
  442 + let currentYear = new Date(currentTime).getFullYear();
  443 + if (publishYear == currentYear) {
  444 + timeStr = this.formatDate(publishTime, DateTimeUtils.PATTERN_DATE_SLASH_WITHOUT_YEAR2)
  445 + } else {
  446 + timeStr = this.formatDate(publishTime)
  447 + }
  448 + } else if (timeGap > 60 * 60 * 1000 * 24) {
  449 + timeStr = Math.floor(timeGap / (60 * 60 * 1000 * 24)) + "天前";
  450 + } else if (timeGap > 60 * 60 * 1000) { // 1小时-24小时
  451 + timeStr = Math.floor(timeGap / (60 * 60 * 1000)) + "小时前";
  452 + } else if (timeGap > 60 * 1000) { // 1分钟-59分钟
  453 + timeStr = Math.floor(timeGap / (60 * 1000)) + "分钟前";
  454 + } else { // 1秒钟-59秒钟
  455 + timeStr = "刚刚";
  456 + }
  457 + return timeStr;
  458 + }
  459 +
  460 +}
  461 +
  462 +// const dateTimeUtils = new DateTimeUtils()
  1 +import util from '@ohos.util';
  2 +import deviceInfo from '@ohos.deviceInfo';
  3 +import { SPHelper } from './SPHelper';
  4 +
  5 +const TAG = 'DeviceUtil';
  6 +
  7 +/**
  8 + * 与(硬件)设备相关(不可改变或不可升级)属性或操作
  9 + */
  10 +export class DeviceUtil {
  11 + static clientId() {
  12 + let uuid = SPHelper.default.getSync("clientId", '');
  13 + if (uuid == '') {
  14 + uuid = util.generateRandomUUID();
  15 + SPHelper.default.saveSync("clientId", uuid);
  16 + }
  17 + return uuid.toString();
  18 + }
  19 +
  20 + /**
  21 + * 返回设备厂家名称
  22 + * HUAWEI
  23 + * @returns
  24 + */
  25 + static getManufacture(): string {
  26 + return deviceInfo.manufacture;
  27 + }
  28 +
  29 + /**
  30 + * 返回设备品牌名称
  31 + * HUAWEI MATE 40 PRO
  32 + * @returns
  33 + */
  34 + static getBrand(): string {
  35 + return deviceInfo.brand;
  36 + }
  37 +
  38 + /**
  39 + * 返回认证型号
  40 + * LIO-AL00
  41 + * @returns
  42 + */
  43 + static getProductModel(): string {
  44 + return deviceInfo.productModel;
  45 + }
  46 +
  47 + /**
  48 + * 返回产品版本
  49 + * OpenHarmony-3.2.6.5(Beta2)
  50 + * @returns
  51 + */
  52 + static getDisplayVersion(): string {
  53 + return deviceInfo.productModel;
  54 + }
  55 +}
  1 +import display from '@ohos.display';
  2 +import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
  3 +import {Logger} from './Logger';
  4 +
  5 +const TAG = 'DisplayUtils';
  6 +
  7 +/**
  8 + * 设备显示器/Display属性:
  9 + *
  10 + 手机(HUAWEI Mate 40 Pro & NOH-AN00)-竖屏:
  11 + {
  12 + "id": 0,
  13 + "name": "UNKNOW",
  14 + "alive": true,
  15 + "state": 0,
  16 + "refreshRate": 90,
  17 + "rotation": 0,
  18 + "width": 1344, // 屏幕显示的实际宽度/(像素个数/以像素px为单位)
  19 + "height": 2772, // 屏幕显示的实际高度/(像素个数/以像素px为单位)
  20 + "densityDPI": 560, // 实际像素密度(每英寸像素:120/160/240/320/560)PPI,是一个具体的数值
  21 + "orientation": 0,
  22 + "densityPixels": 3.5, // 屏幕密度(当像素密度为160时屏幕密度为1.0, 像素比例0.75/1.0/1.5/2.0/3.5) | 缩放因子density = 屏幕像素密度/160, 可用于vp和px转化
  23 + "scaledDensity": 3.5, // 可用于fp和px转化
  24 + "xDPI": 461.3188781738281,
  25 + "yDPI": 457.1999816894531
  26 + }
  27 +
  28 + 手机(HUAWEI Mate 40 Pro & NOH-AN00)-横屏:
  29 + {
  30 + "id": 0,
  31 + "name": "UNKNOW",
  32 + "alive": true,
  33 + "state": 0,
  34 + "refreshRate": 90,
  35 + "rotation": 1,
  36 + "width": 2772,
  37 + "height": 1344,
  38 + "densityDPI": 560,
  39 + "orientation": 1,
  40 + "densityPixels": 3.5,
  41 + "scaledDensity": 3.5,
  42 + "xDPI": 951.47021484375,
  43 + "yDPI": 221.67271423339844
  44 + }
  45 +
  46 + Fold手机(HUAWEI Mate X5 & ALT-AL10)-折叠状态-竖屏:
  47 + {
  48 + "id": 0,
  49 + "name": "UNKNOW",
  50 + "alive": true,
  51 + "state": 0,
  52 + "refreshRate": 90,
  53 + "rotation": 0,
  54 + "width": 1080,
  55 + "height": 2504,
  56 + "densityDPI": 500,
  57 + "orientation": 1,
  58 + "densityPixels": 3.125,
  59 + "scaledDensity": 3.125,
  60 + "xDPI": 173.62025451660156,
  61 + "yDPI": 451.0751647949219
  62 + }
  63 +
  64 + Fold手机(HUAWEI Mate X5 & ALT-AL10)-折叠状态-横屏:
  65 + {
  66 + "id": 0,
  67 + "name": "UNKNOW",
  68 + "alive": true,
  69 + "state": 0,
  70 + "refreshRate": 90,
  71 + "rotation": 3,
  72 + "width": 2504,
  73 + "height": 1080,
  74 + "densityDPI": 500,
  75 + "orientation": 2,
  76 + "densityPixels": 3.125,
  77 + "scaledDensity": 3.125,
  78 + "xDPI": 402.541748046875,
  79 + "yDPI": 194.55319213867188
  80 + }
  81 +
  82 + Fold手机(HUAWEI Mate X5 & ALT-AL10)-完全展开状态-竖屏:
  83 + {
  84 + "id": 0,
  85 + "name": "UNKNOW",
  86 + "alive": true,
  87 + "state": 0,
  88 + "refreshRate": 90,
  89 + "rotation": 0,
  90 + "width": 2224,
  91 + "height": 2496,
  92 + "densityDPI": 500,
  93 + "orientation": 1,
  94 + "densityPixels": 3.125,
  95 + "scaledDensity": 3.125,
  96 + "xDPI": 357.52911376953125,
  97 + "yDPI": 449.634033203125
  98 + }
  99 +
  100 + 平板电脑(HUAWEI MatePad Pro & WGR-W09)(2in1设备)-横屏:
  101 + {
  102 + "id": 0,
  103 + "name": "UNKNOW",
  104 + "alive": true,
  105 + "state": 0,
  106 + "refreshRate": 60,
  107 + "rotation": 0,
  108 + "width": 2560,
  109 + "height": 1600,
  110 + "densityDPI": 240,
  111 + "orientation": 1,
  112 + "densityPixels": 1.5,
  113 + "scaledDensity": 1.5,
  114 + "xDPI": 239.05882263183594,
  115 + "yDPI": 239.05882263183594
  116 + }
  117 + */
  118 +/**
  119 + * 与显示器/display/screen(硬件)设备相关(不可改变或不可升级)属性或操作(应该读取窗口Window的宽高,而不是显示器display的宽高,因为电脑设备的窗口可以动态调整Window窗口大小)
  120 + * 屏幕密度是160时,1vp=1px
  121 + */
  122 +export class DisplayUtils {
  123 + /**
  124 + * Get 屏幕显示/display/screen Width(单位vp)
  125 + * 手机(HUAWEI Mate 40 Pro & NOH-AN00)-竖屏:width为1344*3.5=384vp
  126 + * 手机(HUAWEI Mate 40 Pro & NOH-AN00)-横屏:width为2772*3.5=792vp
  127 + * @returns screen width.
  128 + */
  129 + public static getDeviceWidth(): number {
  130 + let displayObject: display.Display = display.getDefaultDisplaySync();
  131 + // Logger.info(TAG, `getDeviceWidth, displayObject: ${JSON.stringify(displayObject)}`);
  132 + let screenWidth = displayObject.width;
  133 + let screenDensityDPI = displayObject.densityDPI;
  134 + // 像素密度(Pixel Density)
  135 + let densityPixels = screenDensityDPI / ConfigurationConstant.ScreenDensity.SCREEN_DENSITY_MDPI
  136 + // let densityPixels = displayObject.densityPixels
  137 + let deviceWidth = screenWidth / densityPixels;
  138 + // Logger.info(TAG, `getDeviceWidth, deviceWidth: ${deviceWidth}`);
  139 + return deviceWidth
  140 + }
  141 +
  142 + /**
  143 + * Get the 显示/display/screen height(单位vp).
  144 + *
  145 + * @returns screen height.
  146 + */
  147 + public static getDeviceHeight(): number {
  148 + let displayObject: display.Display = display.getDefaultDisplaySync();
  149 + let screenHeight = displayObject.height;
  150 + let screenDensityDPI = displayObject.densityDPI;
  151 + // 像素密度(Pixel Density)
  152 + let densityPixels = screenDensityDPI / ConfigurationConstant.ScreenDensity.SCREEN_DENSITY_MDPI
  153 + // let densityPixels = displayObject.densityPixels
  154 + let deviceHeight = screenHeight / densityPixels
  155 + // Logger.info(TAG, `getDeviceHeight, deviceHeight: ${deviceHeight}`); // 345.6 // 711.68
  156 + return deviceHeight
  157 + }
  158 +}
  1 +import { BasicDataSource } from './BasicDataSource';
  2 +
  3 +/**
  4 + * 实现/BasicDataSource抽象类,用于懒数据加载
  5 + */
  6 +export class LazyDataSource<T> extends BasicDataSource<T> {
  7 + // private dataArray: Array<T> = new Array();
  8 + // private dataArray: Array<string> = new Array("0", "1", "2");
  9 + // private dataArray: string[] = ["0", "1", "2"];
  10 + private dataArray: T[] = [];
  11 +
  12 + constructor() {
  13 + super();
  14 + // for (let i = 0; i < 100; i++) {
  15 + // this.dataArray.push(i)
  16 + // }
  17 + }
  18 +
  19 + // 获取数据总个数
  20 + public totalCount(): number {
  21 + return this.dataArray.length;
  22 + }
  23 +
  24 + // 获取索引对应的Item数据
  25 + public getData(index: number): T {
  26 + return this.dataArray[index];
  27 + }
  28 +
  29 + // 获取索引对应的Item数据
  30 + public get(index: number): T {
  31 + return this.getData(index);
  32 + }
  33 +
  34 + public getFirst(): T {
  35 + return this.getData(0);
  36 + }
  37 +
  38 + public getLast(): T {
  39 + return this.getData(this.totalCount() - 1);
  40 + }
  41 +
  42 + // 获取所有数据
  43 + public getDataArray(): T[] {
  44 + return this.dataArray;
  45 + }
  46 +
  47 + // 增加/插入1个Item/数据,若index为undefined,则在数据尾部增加1个元素;否则,在指定索引(可为负/或大于数组长度)位置插入1个元素
  48 + public addItem(item: T, startPosition?: number): void {
  49 + if (startPosition == undefined) {
  50 + // 将新元素添加到数组的末尾
  51 + this.dataArray.push(item);
  52 + // this.dataArray.splice(this.dataArray.length, 0, item)
  53 + this.notifyDataAdd(this.dataArray.length - 1);
  54 + } else {
  55 + // 从数组中的index位置开始删除0个元素,并在同一位置插入((1个或多个))新元素,返回已删除的元素。
  56 + // 当index大于等于数组长度时,不删除任何数据,且item加在数组末尾
  57 + // 当index为负数时,其代表从数组后向前的索引位置
  58 + this.dataArray.splice(startPosition, 0, item);
  59 + this.notifyDataAdd(startPosition);
  60 + }
  61 + }
  62 +
  63 + public addItems(arr: T[], startPosition?: number): void {
  64 + if (startPosition == undefined) {
  65 + // 将新元素添加到数组的末尾
  66 + this.dataArray.push(...arr)
  67 + for (let index = this.dataArray.length - arr.length; index < this.dataArray.length; index++) {
  68 + this.notifyDataAdd(index);
  69 + }
  70 + } else {
  71 + // 从数组中的position位置开始删除0个元素,并在同一位置插入((1个或多个))新元素,返回已删除的元素。
  72 + // 当index大于等于数组长度时,不删除任何数据,且arr加在数组末尾
  73 + // 当index为负数时,其代表从数组后向前的索引位置
  74 + this.dataArray.splice(startPosition, 0, ...arr);
  75 + for (let index = startPosition; index < arr.length; index++) {
  76 + this.notifyDataAdd(index);
  77 + }
  78 + }
  79 + }
  80 +
  81 + // 在数据尾部增加1到多个元素
  82 + public push(...items: T[]): void {
  83 + this.dataArray.push(...items)
  84 + for (let index = this.dataArray.length - items.length; index < this.dataArray.length; index++) {
  85 + this.notifyDataAdd(index);
  86 + }
  87 + }
  88 +
  89 + // 在数据尾部增加一个同类型的LazyDataSource
  90 + public pushDataSource(dataSource: LazyDataSource<T>): void {
  91 + this.push(...dataSource.dataArray)
  92 + }
  93 +
  94 + // 在数据尾部增加1个元素
  95 + public addLastItem(item: T): void {
  96 + this.addItem(item)
  97 + }
  98 +
  99 + // 在数据开头插入1个元素
  100 + public addFirstItem(item: T): void {
  101 + // 在数组的开头插入(1个或多个)新元素
  102 + // this.dataArray.unshift(item)
  103 + // this.notifyDataAdd(0);
  104 + this.addItem(item, 0)
  105 + }
  106 +
  107 + // 在第2个元素位置,插入1个元素
  108 + public add2ndItem(item: T): void {
  109 + this.addItem(item, 1)
  110 + }
  111 +
  112 + // 在第3个元素位置,插入1个元素
  113 + public add3rdItem(item: T): void {
  114 + this.addItem(item, 2)
  115 + }
  116 +
  117 + // 在第4个元素位置,插入1个元素
  118 + public add4thItem(item: T): void {
  119 + this.addItem(item, 3)
  120 + }
  121 +
  122 + // 把from位置的item移动到to位置(暂不支持from/to为负数或大于等于数组长度)
  123 + public moveItem(from: number, to: number): void {
  124 + // 当from/to大于/等于数组长度时;
  125 + if (from >= this.dataArray.length) {
  126 + from = this.dataArray.length - 1;
  127 + }
  128 + if (to >= this.dataArray.length) {
  129 + to = this.dataArray.length - 1;
  130 + }
  131 + // 当from/to为负时;
  132 + let remainderFrom = from % this.dataArray.length
  133 + if (remainderFrom < 0) {
  134 + from = remainderFrom + this.dataArray.length
  135 + }
  136 + let remainderTo = to % this.dataArray.length
  137 + if (remainderTo < 0) {
  138 + to = remainderTo + this.dataArray.length
  139 + }
  140 +
  141 + // 1.把from位置元素提出暂存
  142 + let tempFrom: T = this.dataArray[from];
  143 + // 2.把to位置元素覆盖from位置元素,
  144 + let tempTo: T = this.dataArray[to];
  145 + this.updateItem(tempTo, from)
  146 + // 3.把暂存的from位置元素覆盖to位置元素
  147 + this.updateItem(tempFrom, to)
  148 + this.notifyDataMove(from, to);
  149 + }
  150 +
  151 + // 删除index指定索引位置的元素
  152 + public deleteItem(index?: number): void {
  153 + if (index == undefined) {
  154 + // 删除数组的最后1个元素(倒数第1个元素)
  155 + // this.dataArray.pop()
  156 + this.dataArray.splice(-1, 1)
  157 + this.notifyDataDelete(this.dataArray.length);
  158 + } else {
  159 + // 从数组中的index位置开始,删除1个元素
  160 + // 当index大于等于数组长度时,不删除任何数据
  161 + // 当index为负数时,其代表从数组后向前的索引位置
  162 + this.dataArray.splice(index, 1);
  163 + this.notifyDataDelete(index);
  164 + }
  165 + }
  166 +
  167 + // 删除最后1个元素
  168 + public pop(): void {
  169 + this.deleteItem()
  170 + }
  171 +
  172 + // 删除最后1个元素
  173 + public deleteLastItem(): void {
  174 + this.deleteItem()
  175 + }
  176 +
  177 + // 删除第1个元素
  178 + public deleteFirstItem(): void {
  179 + this.deleteItem(0)
  180 + }
  181 +
  182 + // 删除第1个元素
  183 + public delete1stItem(): void {
  184 + this.deleteItem(0)
  185 + }
  186 +
  187 + // 删除第2个元素
  188 + public delete2ndItem(): void {
  189 + this.deleteItem(1)
  190 + }
  191 +
  192 + // 删除第3个元素
  193 + public delete3rdItem(): void {
  194 + this.deleteItem(2)
  195 + }
  196 +
  197 + // 删除第4个元素
  198 + public delete4thItem(): void {
  199 + this.deleteItem(3)
  200 + }
  201 +
  202 + // 修改/更新index指定索引位置的元素
  203 + public updateItem(item: T, index?: number): void {
  204 + if (index == undefined || index >= this.dataArray.length) {
  205 + // 当不传index或index大于等于数组长度时,修改/更新数组的最后1个元素
  206 + this.dataArray.splice(this.dataArray.length - 1, 1, item);
  207 + this.notifyDataChange(this.dataArray.length);
  208 + } else {
  209 + // 从数组中的index位置开始,删除1个元素
  210 + // 当index为负数时,其代表从数组后向前的索引位置
  211 + this.dataArray.splice(index, 1, item);
  212 + this.notifyDataChange(index);
  213 + }
  214 + }
  215 +
  216 + // 修改/更新最后1个元素
  217 + public updateLastItem(item: T): void {
  218 + this.updateItem(item)
  219 + }
  220 +
  221 + // 修改/更新第1个元素
  222 + public updateFirstItem(item: T): void {
  223 + this.updateItem(item, 0)
  224 + }
  225 +
  226 + // 修改/更新第1个元素
  227 + public update2ndItem(item: T): void {
  228 + this.updateItem(item, 0)
  229 + }
  230 +
  231 + // 修改/更新第2个元素
  232 + public update1stItem(item: T): void {
  233 + this.updateItem(item, 1)
  234 + }
  235 +
  236 + // 修改/更新第3个元素
  237 + public update3rdItem(item: T): void {
  238 + this.updateItem(item, 2)
  239 + }
  240 +
  241 + // 修改/更新第4个元素
  242 + public update4thItem(item: T): void {
  243 + this.updateItem(item, 3)
  244 + }
  245 +
  246 + // 清空数组
  247 + public clear(): void {
  248 + this.replaceAll()
  249 + }
  250 +
  251 + // 把数据全部删除,再添加全部新元素
  252 + public replaceAll(...items: T[]): void {
  253 + // 从数组中的0位置开始删除dataArray.length个元素,并在同一位置插入((1个或多个))新元素,返回已删除的元素。
  254 + this.dataArray.splice(0, this.dataArray.length, ...items);
  255 + this.notifyDataReload()
  256 + }
  257 +
  258 + // 获取指定元素的下标
  259 + public getIndexOf(element: T): number {
  260 + for (let index = 0; index < this.dataArray.length; index++) {
  261 + if (this.dataArray[index] == element) {
  262 + return index
  263 + }
  264 + }
  265 + return -1
  266 + }
  267 +
  268 + public isEmpty(): boolean {
  269 + return this.dataArray.length == 0
  270 + }
  271 +
  272 + public isNotEmpty(): boolean {
  273 + return this.dataArray.length > 0
  274 + }
  275 +
  276 + public sort(comparator: (firstValue: T, secondValue: T) => number): void {
  277 + this.dataArray.sort(comparator)
  278 + }
  279 +}
  1 +import hilog from '@ohos.hilog';
  2 +
  3 +/**
  4 + * Log level define
  5 + *
  6 + * @syscap SystemCapability.HiviewDFX.HiLog
  7 + */
  8 +enum LogLevel {
  9 + DEBUG = 3,
  10 + INFO = 4,
  11 + WARN = 5,
  12 + ERROR = 6,
  13 + FATAL = 7
  14 +}
  15 +
  16 +/**
  17 + * Common log for all features.
  18 + *
  19 + * @param {string} prefix Identifies the log tag.
  20 + */
  21 +export class Logger {
  22 + private static domain: number = 0xFF00;
  23 + private static prefix: string = 'SightApp';
  24 + private static format: string = `%{public}s, %{public}s`;
  25 +
  26 + /**
  27 + * constructor.
  28 + *
  29 + * @param Prefix Identifies the log tag.
  30 + * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
  31 + */
  32 + constructor(prefix: string = 'SightApp', domain: number = 0xFF00) {
  33 + Logger.prefix = prefix;
  34 + Logger.domain = domain;
  35 + }
  36 +
  37 + static debug(...args: string[]) {
  38 + hilog.debug(Logger.domain, Logger.prefix, Logger.format, args);
  39 + }
  40 +
  41 + static info(...args: string[]) {
  42 + hilog.info(Logger.domain, Logger.prefix, Logger.format, args);
  43 + }
  44 +
  45 + static warn(...args: string[]) {
  46 + hilog.warn(Logger.domain, Logger.prefix, Logger.format, args);
  47 + }
  48 +
  49 + static error(...args: string[]) {
  50 + hilog.error(Logger.domain, Logger.prefix, Logger.format, args);
  51 + }
  52 +
  53 + static fatal(...args: string[]) {
  54 + hilog.fatal(Logger.domain, Logger.prefix, Logger.format, args);
  55 + }
  56 +
  57 + static isLoggable(level: LogLevel) {
  58 + hilog.isLoggable(Logger.domain, Logger.prefix, level);
  59 + }
  60 +}
  61 +
  62 +export default new Logger('SightApp', 0xFF00)
  1 +import buffer from '@ohos.buffer';
  2 +import { BusinessError } from '@ohos.base';
  3 +import { Logger } from './Logger';
  4 +
  5 +const TAG = "ResourcesUtils";
  6 +
  7 +// 默认针对entry/src/main/resources/rawfile目录下的文件:
  8 +const JSON_FILE_NAME = "agconnect-services.json";
  9 +
  10 +export class ResourcesUtils {
  11 + /**
  12 + * 获取资源字符串内容
  13 + * @param context
  14 + * @param filename 仅文件名(不包含路径,文件在src\main\resources\rawfile下),如:"agconnect-services.json",test.xml,
  15 + * @returns text 字符串
  16 + */
  17 + static getResourcesText(context: Context, filename: string): Promise<string> {
  18 + return new Promise((success, error) => {
  19 + Logger.info(TAG, "ResourcesUtils getResourcesText filename:" + filename);
  20 + context.resourceManager.getRawFileContent(filename).then((content: Uint8Array) => {
  21 + if (!content) {
  22 + Logger.warn(TAG, 'getResourcesText then content is empty');
  23 + error("file is empty");
  24 + return
  25 + }
  26 + try {
  27 + let text = buffer.from(content).toString("utf8");
  28 + if (text) {
  29 + Logger.info(TAG, "getResourcesText then text:" + text);
  30 + success(text);
  31 + } else {
  32 + Logger.warn(TAG, "getResourcesText then text is empty");
  33 + error("resources file text is empty");
  34 + }
  35 + } catch (err) {
  36 + // 转换异常
  37 + Logger.error(TAG, `getResourcesText catch err.: ${err}`);
  38 + }
  39 + }).catch((err: Error) => {
  40 + Logger.error(TAG, `getResourcesText getRawFileContent catch, error.name : ${err.name}, error.message:${err.message}`);
  41 + error(err);
  42 + })
  43 + })
  44 + }
  45 +
  46 + /**
  47 + * 获取资源字符串内容
  48 + * @param context
  49 + * @param filename 仅文件名(不包含路径,文件在src\main\resources\rawfile下),如:"agconnect-services.json",test.xml,
  50 + * @returns text 字符串
  51 + */
  52 + static getResourcesTextSync(context: Context, filename: string): string | null {
  53 + Logger.info(TAG, `getResourcesTextSync filename: ${filename}`);
  54 + try {
  55 + let content: Uint8Array = context.resourceManager.getRawFileContentSync(filename)
  56 + if (!content) {
  57 + Logger.warn(TAG, 'getResourcesTextSync content is empty');
  58 + return null
  59 + }
  60 + let text = buffer.from(content).toString("utf8");
  61 + if (text) {
  62 + // Logger.info(TAG, "getRawFileContentSync text:" + text);
  63 + } else {
  64 + Logger.warn(TAG, "getRawFileContentSync text is empty");
  65 + }
  66 + return text;
  67 + } catch (err) {
  68 + // 转换异常
  69 + Logger.error(TAG, `getResourcesText catch err.: ${err}`);
  70 + return null;
  71 + }
  72 + }
  73 +
  74 + /**
  75 + * 获取资源文件并转为JSON
  76 + *
  77 + * @param appContext 上下文
  78 + * @param filename 仅文件名(不包含路径,文件在src\main\resources\rawfile下),如:"agconnect-services.json"
  79 + * SubscribeListData.json
  80 + * @returns JSON Object
  81 + *
  82 + * 调用方式:
  83 + * let compRes: MGHttp.ResponseDTO<BodyComponent> = await ResourcesUtils.getResourcesJson<MGHttp.ResponseDTO<BodyComponent>>(context, 'model/componentList.json');
  84 + */
  85 + static getResourcesJson<R>(context: Context, filename: string): Promise<R> {
  86 + Logger.info(TAG, `getResourcesJson filename: ${filename}`);
  87 + return new Promise<R>((resolve, reject) => {
  88 + ResourcesUtils.getResourcesText(context, filename)
  89 + .then((text: string) => {
  90 + try {
  91 + // let config: R = JSON.parse(text)
  92 + let config: R = JSON.parse(text) as R;
  93 + if (config) {
  94 + Logger.info(TAG, "getResourcesJson parse JSON file success.");
  95 + // Logger.info(TAG, `getResourcesJson config : ${JSON.stringify(config)}`);
  96 + resolve(config);
  97 + } else {
  98 + reject("getResourcesJson parse JSON file result is empty");
  99 + }
  100 + } catch (err) {
  101 + // json解析异常
  102 + Logger.error(TAG, `getResourcesJson catch parse failed.: ${err}`);
  103 + reject(err)
  104 + }
  105 + })
  106 + .catch((error: BusinessError) => {
  107 + reject(error)
  108 + })
  109 + })
  110 + }
  111 +
  112 + static getResourcesJsonSync<R>(context: Context, filename: string): R | null {
  113 + Logger.info(TAG, `getResourcesJsonSync filename: ${filename}`);
  114 + let text = ResourcesUtils.getResourcesTextSync(context, filename)
  115 + if (!text) {
  116 + Logger.warn(TAG, `getResourcesJsonSync file result is empty`);
  117 + return null
  118 + }
  119 + try {
  120 + // let config: R = JSON.parse(text)
  121 + let config: R = JSON.parse(text) as R;
  122 + return config
  123 + } catch (err) {
  124 + // json解析异常
  125 + Logger.error(TAG, `getResourcesJsonSync catch parse failed.: ${err}`);
  126 + return null
  127 + }
  128 + }
  129 +}
  1 +import data_preferences from '@ohos.data.preferences';
  2 +
  3 +/*
  4 +// SPHelper.default.get("key1", "defValue1").then((value1) => {
  5 +// this.message = value1.toString();
  6 +// })
  7 +
  8 +// let value2: string = await SPHelper.default.get("key2", "defValue2");
  9 +// this.message = result;
  10 +*
  11 +* 单例模式
  12 + */
  13 +export class SPHelper {
  14 + private static context: Context;
  15 + private static spFilename: string = '__SPHelper';
  16 +
  17 + static init(context: Context) {
  18 + SPHelper.context = context;
  19 + }
  20 +
  21 + static setSpFilename(spFilename: string) {
  22 + SPHelper.spFilename = spFilename;
  23 + }
  24 +
  25 + // 静态属性
  26 + static default: SPHelper = new SPHelper();
  27 +
  28 + // 私有构造函数
  29 + private constructor() {
  30 + }
  31 +
  32 + private async getVideoPreferences(): Promise<data_preferences.Preferences> {
  33 + let preferences: data_preferences.Preferences = await data_preferences.getPreferences(SPHelper.context, SPHelper.spFilename);
  34 + return preferences;
  35 + }
  36 +
  37 + private getVideoPreferencesSync(): data_preferences.Preferences {
  38 + let options: data_preferences.Options = { name: SPHelper.spFilename };
  39 + let preferences: data_preferences.Preferences = data_preferences.getPreferencesSync(SPHelper.context, options);
  40 + return preferences;
  41 + }
  42 +
  43 + async save(key: string, value: data_preferences.ValueType) {
  44 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  45 + await preferences.put(key, value)
  46 + await preferences.flush()
  47 + }
  48 +
  49 + saveSync(key: string, value: data_preferences.ValueType) {
  50 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  51 + preferences.putSync(key, value)
  52 + preferences.flush() // todo:Asynchronously
  53 + }
  54 +
  55 + async get(key: string, defValue: data_preferences.ValueType): Promise<data_preferences.ValueType> {
  56 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  57 + return await preferences.get(key, defValue);
  58 + }
  59 +
  60 + getSync(key: string, defValue: data_preferences.ValueType): data_preferences.ValueType {
  61 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  62 + return preferences.getSync(key, defValue);
  63 + }
  64 +
  65 + async has(key: string): Promise<boolean> {
  66 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  67 + return await preferences.has(key);
  68 + }
  69 +
  70 + hasSync(key: string): boolean {
  71 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  72 + return preferences.hasSync(key);
  73 + }
  74 +
  75 + async delete(key: string) {
  76 + const preferences: data_preferences.Preferences = await this.getVideoPreferences();
  77 + preferences.delete(key).then(async () => {
  78 + await preferences.flush();
  79 + }).catch((err: Error) => {
  80 + // Logger.error(TAG, 'Failed to delete the key. Cause: ' + err);
  81 + });
  82 + }
  83 +
  84 + deleteSync(key: string) {
  85 + const preferences: data_preferences.Preferences = this.getVideoPreferencesSync();
  86 + preferences.deleteSync(key)
  87 + preferences.flush(); // todo:Asynchronously
  88 + }
  89 +
  90 + async clearSync() {
  91 + this.getVideoPreferences().then(async (preferences: data_preferences.Preferences) => {
  92 + preferences.clearSync()
  93 + await preferences.flush()
  94 + }).catch((err: Error) => {
  95 + // Logger.error(TAG, 'get the preferences failed, Cause: ' + err);
  96 + });
  97 + }
  98 +
  99 + // clearSync() {
  100 + // let preferences: data_preferences.Preferences = this.getVideoPreferencesSync()
  101 + // preferences.clearSync()
  102 + // preferences.flush()
  103 + // }
  104 +}
  1 +const SLEEP_TIME: number = 10;
  2 +
  3 +/**
  4 + * StringUtils class.
  5 + */
  6 +export class StringUtils {
  7 + /**
  8 + * The String utils tag.
  9 + */
  10 + private static readonly TAG: string = 'StringUtils';
  11 + // 评分-整数或浮点数
  12 + private static readonly SCORE_REG_EXP = RegExp(/^(\d+)(\.\d+)?$/);
  13 +
  14 + /**
  15 + * Check string is empty.
  16 + *
  17 + * @param {object} any
  18 + * @return {boolean} true(empty)
  19 + */
  20 + static isEmpty(...params: any): boolean {
  21 + if (params.length === 0) {
  22 + return true;
  23 + }
  24 + for (const param of params) {
  25 + if (!param) { // param === undefined || param === null || param === '';
  26 + return true;
  27 + }
  28 + }
  29 + return false;
  30 + }
  31 +
  32 + /**
  33 + * Check obj is not empty.
  34 + *
  35 + * @param {object} obj
  36 + * @return {boolean} true(not empty)
  37 + */
  38 + static isNotEmpty(obj: any): boolean {
  39 + // return (obj && obj !== '');
  40 + // 或
  41 + return (obj !== undefined && obj !== null && obj !== '');
  42 + }
  43 +
  44 + static isObject(value: any): boolean {
  45 + return Object.prototype.toString.call(value) === '[object Object]';
  46 + }
  47 +
  48 + static isNotNullObject(value: unknown): boolean {
  49 + return value !== null ? typeof value === 'object' : false;
  50 + }
  51 +
  52 + static isNullOrUndefined(value: any): boolean {
  53 + return value === undefined || value === null;
  54 + }
  55 +
  56 + static isFunction(object: any): boolean {
  57 + return typeof object === 'function';
  58 + }
  59 +
  60 + static isValidObject(objectList: any): boolean {
  61 + if (!objectList || typeof objectList !== 'object'
  62 + || objectList.constructor.name === 'Object') {
  63 + return false;
  64 + }
  65 + return true;
  66 + }
  67 +
  68 + static isClass(object: any): boolean {
  69 + if (StringUtils.isFunction(object)) {
  70 + const prototype = object.prototype;
  71 + if (!StringUtils.isNotNullObject(prototype)) {
  72 + return false;
  73 + }
  74 + const constructor = prototype.constructor;
  75 + if (StringUtils.isFunction(constructor)) {
  76 + return true;
  77 + }
  78 + }
  79 + return false;
  80 + }
  81 +
  82 + static getClassName(clz: any): string {
  83 + if (StringUtils.isNullOrUndefined(clz)) {
  84 + return clz;
  85 + }
  86 + let input = clz;
  87 + if (!StringUtils.isClass(input)) {
  88 + input = clz.constructor;
  89 + }
  90 + return input.className ? input.className : input.name;
  91 + }
  92 +
  93 + static hash(value: string | number): string {
  94 + const str = '' + value;
  95 + let hash = 5381;
  96 + let index = str.length;
  97 + while (index) {
  98 + hash = (hash * 33) ^ str.charCodeAt(--index);
  99 + }
  100 + return '' + (hash >>> 0);
  101 + }
  102 +
  103 + static parseNumber(input: string): number {
  104 + // let input: string = '135' // 135
  105 + // let input: string = '135T' // NaN
  106 + // let input: string = 'ytr' // NaN
  107 + // let input: string = '' // 0
  108 + // let input: string = null // 0
  109 + // let input: string = undefined // NaN
  110 + const parsedInt = Number(input)
  111 + if (!isNaN(parsedInt)) {
  112 + return parsedInt
  113 + } else {
  114 + return 0
  115 + }
  116 + }
  117 +
  118 + static async sleep(times: number): Promise<void> {
  119 + if (!times) {
  120 + times = SLEEP_TIME;
  121 + }
  122 + await new Promise((res) => setTimeout(res, times)) // .then().catch().finally();
  123 + }
  124 +
  125 + static isScore(text: string): boolean {
  126 + return StringUtils.SCORE_REG_EXP.test(text);
  127 + }
  128 +}
  129 +
  130 +// export default new StringUtils();
  1 +import deviceInfo from '@ohos.deviceInfo';
  2 +
  3 +/**
  4 + * 与鸿蒙系统(软件)设备相关(可改变或可升级)属性或操作
  5 + */
  6 +export class SystemUtils {
  7 + /**
  8 + * 返回AGC格式的系统名称
  9 + * OpenHarmony 3.2.6.5(Beta2)
  10 + */
  11 + static getOsFullName(): string {
  12 + // OpenHarmony-3.2.6.5(Beta2)
  13 + let fullName = deviceInfo.osFullName.split('-');
  14 + if (fullName && fullName.length >= 1) {
  15 + return fullName[0] + ' ' + fullName[1];
  16 + }
  17 + return '';
  18 + }
  19 +
  20 + /**
  21 + * 返回系统空闲存储空间,TODO:
  22 + * @returns number
  23 + */
  24 + static getFreeBytes(): number {
  25 + return 0;
  26 + }
  27 +}
  1 +import prompt from '@ohos.promptAction'
  2 +
  3 +export class ToastUtils {
  4 + private static longToastTime: number = 3000
  5 + private static shortToastTime: number = 1000
  6 +
  7 + static showToast(message: ResourceStr, duration: number) {
  8 + prompt.showToast({ message: message, duration: duration })
  9 + }
  10 +
  11 + static shortToast(message: ResourceStr) {
  12 + ToastUtils.showToast(message, ToastUtils.shortToastTime)
  13 + }
  14 +
  15 + static longToast(message: ResourceStr) {
  16 + ToastUtils.showToast(message, ToastUtils.longToastTime)
  17 + }
  18 +}
  19 +
  20 +// export default new ToastUtils()
  1 +import window from '@ohos.window';
  2 +import { BusinessError } from '@ohos.base';
  3 +import deviceInfo from '@ohos.deviceInfo'
  4 +import display from '@ohos.display';
  5 +
  6 +export class Size {
  7 + width: number = 0
  8 + height: number = 0
  9 +
  10 + constructor(width: number, height: number) {
  11 + this.width = width;
  12 + this.height = height;
  13 + }
  14 +}
  15 +
  16 +export class WindowModel {
  17 + private windowStage?: window.WindowStage;
  18 +
  19 + static shared: WindowModel = new WindowModel()
  20 +
  21 + static TAG = "WindowModel";
  22 +
  23 + setWindowStage(windowStage: window.WindowStage) {
  24 + this.windowStage = windowStage;
  25 + }
  26 +
  27 + setMainWindowFullScreen(fullScreen: boolean) {
  28 + if (deviceInfo.deviceType != "phone") {
  29 + return
  30 + }
  31 + if (this.windowStage === undefined) {
  32 + return;
  33 + }
  34 + this.windowStage.getMainWindow((err, windowClass: window.Window) => {
  35 + if (err.code) {
  36 + return;
  37 + }
  38 + windowClass.setWindowLayoutFullScreen(fullScreen, (err) => {
  39 + if (err.code) {
  40 + return;
  41 + }
  42 + });
  43 + windowClass.setWindowSystemBarEnable(fullScreen ? [] : ['status', 'navigation'], (err) => {
  44 + if (err.code) {
  45 + return;
  46 + }
  47 + });
  48 + });
  49 + }
  50 +
  51 + getWindowSize(): Promise<Size> {
  52 + return new Promise((resolve, reject) => {
  53 + if (!this.windowStage) {
  54 + let dis = display.getDefaultDisplaySync();
  55 + reject(new Size(dis.width, dis.height))
  56 + return;
  57 + }
  58 + let rect = this.windowStage.getMainWindowSync().getWindowProperties().windowRect;
  59 + resolve(new Size(rect.width, rect.height));
  60 + });
  61 + }
  62 +
  63 + setWindowKeepScreenOn(isKeepScreenOn: boolean) {
  64 + this.windowStage?.getMainWindow((err, windowClass: window.Window) => {
  65 + windowClass.setWindowKeepScreenOn(isKeepScreenOn, (err: BusinessError) => {
  66 + const errCode: number = err.code;
  67 + if (errCode) {
  68 + console.error(WindowModel.TAG +'设置屏幕常亮:' + isKeepScreenOn + ',失败: ' + JSON.stringify(err));
  69 + return;
  70 + }
  71 + console.info(WindowModel.TAG +'设置屏幕常亮:' + isKeepScreenOn + ",成功");
  72 + })
  73 + })
  74 + }
  75 +
  76 + /**
  77 + * 设置窗口的显示方向属性
  78 + * @param orientation
  79 + */
  80 + setPreferredOrientation(orientation: window.Orientation): Promise<void> {
  81 + return new Promise((resolve, reject) => {
  82 + if (!this.windowStage) {
  83 + console.error('Failed, the main window is empty');
  84 + reject("Failed, the main window is empty")
  85 + return;
  86 + }
  87 + this.windowStage.getMainWindow((err: BusinessError, windowClass: window.Window) => {
  88 + if (err.code) {
  89 + console.error('Failed to obtain the main window. Cause: ' + err.message);
  90 + reject(err)
  91 + return;
  92 + }
  93 + // 2.设置窗口的显示方向属性,使用Promise异步回调。
  94 + windowClass.setPreferredOrientation(orientation).then(() => {
  95 + console.info('Succeeded in setting the window orientation.');
  96 + resolve()
  97 + }).catch((err: BusinessError) => {
  98 + console.error('Failed to set the window orientation. Cause: ' + err.message);
  99 + reject(err)
  100 + });
  101 + });
  102 + })
  103 + }
  104 +}
  105 +
  1 +{
  2 + "module": {
  3 + "name": "wdKit",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true
  12 + }
  13 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "全局工具包"
  6 + }
  7 + ]
  8 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { ResponseDTO } from "./src/main/ets/bean/ResponseDTO"
  2 +
  3 +export { HttpRequest as WDHttp } from "./src/main/ets/http/HttpRequest"
  4 +
  5 +export { HttpUrlUtils } from "./src/main/ets/http/HttpUrlUtils"
  6 +
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "meta": {
  3 + "stableOrder": true
  4 + },
  5 + "lockfileVersion": 3,
  6 + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
  7 + "specifiers": {
  8 + "@ohos/axios@^2.1.1": "@ohos/axios@2.1.1",
  9 + "wdConstant@../../../../commons/wdConstant": "wdConstant@../wdConstant",
  10 + "wdKit@../../../../commons/wdKit": "wdKit@../wdKit"
  11 + },
  12 + "packages": {
  13 + "@ohos/axios@2.1.1": {
  14 + "name": "@ohos/axios",
  15 + "integrity": "sha512-EQax257+eKKT0Rx7h6N6xvmKbDRWDjCCWOP2vyyktySFwvjtypXuXmQKEvRjmAalR6cqf8mbfhWmpg0bD9OQ3w==",
  16 + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/axios/-/axios-2.1.1.har",
  17 + "registryType": "ohpm"
  18 + },
  19 + "wdConstant@../wdConstant": {
  20 + "name": "wdconstant",
  21 + "resolved": "../wdConstant",
  22 + "registryType": "local"
  23 + },
  24 + "wdKit@../wdKit": {
  25 + "name": "wdkit",
  26 + "resolved": "../wdKit",
  27 + "registryType": "local"
  28 + }
  29 + }
  30 +}
@@ -2,16 +2,13 @@ @@ -2,16 +2,13 @@
2 "license": "Apache-2.0", 2 "license": "Apache-2.0",
3 "devDependencies": {}, 3 "devDependencies": {},
4 "author": "", 4 "author": "",
5 - "name": "wdcomponent", 5 + "name": "wdnetwork",
6 "description": "Please describe the basic information.", 6 "description": "Please describe the basic information.",
7 "main": "Index.ets", 7 "main": "Index.ets",
8 "version": "1.0.0", 8 "version": "1.0.0",
9 "dependencies": { 9 "dependencies": {
10 "wdConstant": "file:../wdConstant", 10 "wdConstant": "file:../wdConstant",
11 "wdKit": "file:../wdKit", 11 "wdKit": "file:../wdKit",
12 - "wdWebComponent": "file:../wdWebComponent",  
13 - "wdBean": "file:../wdBean",  
14 - "wdRouter": "file:../wdRouter",  
15 - "wdNetwork": "file:../wdNetwork" 12 + "@ohos/axios": "^2.1.1"
16 } 13 }
17 } 14 }
  1 +/**
  2 + * ResponseDTO
  3 + */
  4 +export interface ResponseDTO<T = string> {
  5 + success:boolean;
  6 +
  7 + // 服务请求响应值/微服务响应状态码”
  8 + code: number;
  9 +
  10 + // 服务请求响应说明
  11 + message: string;
  12 +
  13 + // 响应结果
  14 + data?: T;
  15 +
  16 + // 请求响应时间戳(unix格式)
  17 + timestamp?: number;
  18 +}
  1 +import axios, {
  2 + AxiosError,
  3 + AxiosInstance,
  4 + AxiosResponse,
  5 + HttpStatusCode,
  6 + InternalAxiosRequestConfig
  7 +} from '@ohos/axios';
  8 +
  9 +// import type ResponseDTO from '../models/ResponseDTO';
  10 +
  11 +// const key = 'xxxxyyyyzzz'
  12 +
  13 +// const apiBaseUrl = 'http://127.0.0.1:5566'
  14 +// const apiBaseUrl = "https://display-sc.miguvideo.com/display/v3/static";
  15 +// console.log("apiBaseUrl:" + apiBaseUrl);
  16 +// axios.defaults.baseURL = apiBaseUrl
  17 +
  18 +// 使跨域请求带上cookie等用户认证凭据
  19 +// axios.defaults.withCredentials = true;
  20 +
  21 +// 实例化axios
  22 +const instance: AxiosInstance = axios.create({
  23 + // axios请求基础URL(包括端口号)
  24 + // baseURL: apiBaseUrl,
  25 + // 单位毫秒
  26 + timeout: 15000,
  27 + // 跨域请求时,携带cookie等用户认证凭据
  28 + // withCredentials: true
  29 +});
  30 +
  31 +// 添加请求拦截器
  32 +instance.interceptors.request.use(
  33 + (config: InternalAxiosRequestConfig) => {
  34 + // 在发送请求之前
  35 + if (!config.headers["Content-Type"]) {
  36 + config.headers["Content-Type"] = "application/json;charset=utf-8";
  37 + }
  38 + // 公共请求参数
  39 + // config.params.key = key
  40 + return config;
  41 + },
  42 + (error: AxiosError) => {
  43 + // 请求错误
  44 + console.log(`全局请求失败拦截,message:${error.message}`)
  45 + return Promise.reject(error);
  46 + }
  47 +);
  48 +
  49 +// // GET /api/success
  50 +// {
  51 +// "code": 0,
  52 +// "message": "请求成功",
  53 +// "data": {
  54 +// "name": "管理员"
  55 +// }
  56 +// }
  57 +// // GET /api/fail
  58 +// {
  59 +// "code": -1,
  60 +// "message": "请求失败:XXX错误!",
  61 +// "data": null
  62 +// }
  63 +
  64 +// 添加响应拦截器
  65 +instance.interceptors.response.use(// 响应拦截器response类型就是Axios.request<T = any, R = AxiosResponse<T>, D = any>中的泛型R
  66 + // 泛型 T 就是服务器返回数据的类型
  67 + // 泛型 R 就是这个泛型 T 数据经过 axios 包装一层得到的 response 对象的类型,而 request 方法的返回值是一个 Promise,其值就是成功态的 R,也就是 response对象。
  68 + (response: AxiosResponse) => {
  69 + // response为AxiosResponse类型,含有config\data\headers\request\status\statusText属
  70 + // console.dir(response)
  71 + // 正常响应,可对响应数据做通用处理
  72 + // 2xx 范围内的状态码都会触发该函数。
  73 + if (response.status === HttpStatusCode.Ok) {
  74 + // 直接返回response,当然你也可以只返回response.data
  75 + // return response;
  76 + // 也可以先解析服务器返回的状态码,可判断code处理通用逻辑
  77 + // const { code, message, data } = response.data
  78 + // if (code === 0) {
  79 + // // 将组件用的数据返回
  80 + // return data
  81 + // } else {
  82 + // // 处理业务错误。
  83 + // console.log(`处理业务,message:${message}`)
  84 + // return Promise.reject(new Error(message))
  85 + // }
  86 + // const data: ResponseBean<any> = response.data
  87 + // 改造返回的数据,即将AxiosResponse的data返回,服务端返回的数据
  88 + return response.data;
  89 + } else {
  90 + console.log(`httpStatus:${response.status}-${response.status}!`)
  91 + // return Promise.reject(error);
  92 + return response.data;
  93 + }
  94 + },
  95 + (error: AxiosError) => {
  96 + // 异常响应
  97 + // console.log('全局响应失败拦截')
  98 + // console.log(error.request)
  99 + // console.log(error.response)
  100 + // 这里用来处理http常见错误,进行全局提示
  101 + let message = buildErrorMsg(error.response?.status);
  102 + // 错误消息可以使用全局弹框展示出来
  103 + console.log(`httpStatus:${error.response?.status}-${message},请检查网络或联系管理员!`)
  104 + return Promise.reject(error);
  105 + }
  106 +);
  107 +
  108 +export default instance;
  109 +
  110 +function buildErrorMsg(httpStatus: number): string {
  111 + let message = "";
  112 + switch (httpStatus) {
  113 + case HttpStatusCode.BadRequest:
  114 + message = "请求错误(400)";
  115 + break;
  116 + case HttpStatusCode.Unauthorized:
  117 + message = "未授权(或token 失效),请重新登录(401)";
  118 + // 可以触发退出的 action
  119 + // 可以做清空storage并跳转到登录页的操作
  120 + break;
  121 + case HttpStatusCode.Forbidden:
  122 + message = "拒绝访问(403)";
  123 + break;
  124 + case HttpStatusCode.NotFound:
  125 + message = "请求出错(404)";
  126 + // 请求地址错误
  127 + break;
  128 + case HttpStatusCode.RequestTimeout:
  129 + message = "请求超时(408)";
  130 + break;
  131 + case HttpStatusCode.InternalServerError:
  132 + message = "服务器错误(500)";
  133 + // 服务器故障
  134 + break;
  135 + case HttpStatusCode.NotImplemented:
  136 + message = "服务未实现(501)";
  137 + break;
  138 + case HttpStatusCode.BadGateway:
  139 + message = "网络错误(502)";
  140 + break;
  141 + case HttpStatusCode.ServiceUnavailable:
  142 + message = "服务不可用(503)";
  143 + break;
  144 + case HttpStatusCode.GatewayTimeout:
  145 + message = "网络超时(504)";
  146 + break;
  147 + case HttpStatusCode.HttpVersionNotSupported:
  148 + message = "HTTP版本不受支持(505)";
  149 + break;
  150 + default:
  151 + // 网络连接故障
  152 + message = `连接出错(${httpStatus})!`;
  153 + }
  154 + return message;
  155 +}
  1 +import ArrayList from '@ohos.util.ArrayList';
  2 +import service from './AxiosRequest';
  3 +import { HttpUtils } from '../utils/HttpUtils';
  4 +import { AxiosError, AxiosHeaders, AxiosRequestConfig, RawAxiosRequestHeaders } from '@ohos/axios';
  5 +import { ResponseDTO } from '../bean/ResponseDTO';
  6 +import HashMap from '@ohos.util.HashMap';
  7 +
  8 +export class HttpRequest {
  9 + private static globalHeaderProviders: ArrayList<() => Record<string, string>> = new ArrayList();
  10 +
  11 + static addGlobalHeaderProvider(provider: () => Record<string, string>) {
  12 + HttpRequest.globalHeaderProviders.add(provider)
  13 + }
  14 +
  15 + static initHttpHeader() {
  16 + HttpRequest.addGlobalHeaderProvider(() => {
  17 + return HttpUtils.buildHeaders();
  18 + })
  19 + }
  20 +
  21 + // 加入泛型限定,返回数据类型为T,
  22 + static request<T = ResponseDTO<string>>(config: AxiosRequestConfig): Promise<T> {
  23 + return new Promise<T>((resolve, reject) => {
  24 + service.request<ResponseDTO<string>, T>(config)
  25 + .then((res: T) => {
  26 + resolve(res)
  27 + })
  28 + .catch((err: AxiosError) => {
  29 + reject(err)
  30 + })
  31 + })
  32 + // return service.request<any, T>(config)
  33 + }
  34 +
  35 + static buildHeaderWithGlobalHeader(headers?: HashMap<string, string>): AxiosHeaders {
  36 + let commonHeader: AxiosHeaders = new AxiosHeaders()
  37 + headers?.forEach((v, k) => {
  38 + commonHeader.set(k, v);
  39 + });
  40 + HttpRequest.globalHeaderProviders.forEach((func) => {
  41 + let headers = func();
  42 + for (const obj of Object.entries(headers)) {
  43 + commonHeader.set(obj[0], obj[1]);
  44 + }
  45 + })
  46 + if (!commonHeader.get('Content-Type')) {
  47 + commonHeader.set('Content-Type', 'application/json;charset=utf-8');
  48 + }
  49 + return commonHeader
  50 + }
  51 +
  52 + static get<T = ResponseDTO<string>>(url: string, headers?: HashMap<string, string>): Promise<T> {
  53 + let config: AxiosRequestConfig = {
  54 + headers: HttpRequest.buildHeaderWithGlobalHeader(headers)
  55 + }
  56 + return service.get(url, config)
  57 + }
  58 +
  59 + static post0<T = ResponseDTO<string>>(url: string, data?: object, headers?: HashMap<string, string>): Promise<T> {
  60 + let config: AxiosRequestConfig = {
  61 + headers: HttpRequest.buildHeaderWithGlobalHeader(headers)
  62 + }
  63 + return service.post(url, data, config)
  64 + }
  65 +
  66 + static post<T = object>(url: string, data1?: object, headers?: HashMap<string, string>): Promise<T> {
  67 + let requestHeaders: AxiosHeaders = new AxiosHeaders()
  68 + headers?.forEach((v, k) => {
  69 + requestHeaders.set(k, v);
  70 + });
  71 + let config: AxiosRequestConfig = {
  72 + headers: requestHeaders as RawAxiosRequestHeaders,
  73 + }
  74 + return service.post(url, data1, config)
  75 + }
  76 +
  77 + static put<T = ResponseDTO<string>>(url: string, data?: object, headers?: HashMap<string, string>): Promise<T> {
  78 + let config: AxiosRequestConfig = {
  79 + headers: HttpRequest.buildHeaderWithGlobalHeader(headers)
  80 + }
  81 + return service.put(url, data, config)
  82 + }
  83 +
  84 + static delete<T = ResponseDTO<string>>(url: string, headers?: HashMap<string, string>): Promise<T> {
  85 + let config: AxiosRequestConfig = {
  86 + headers: HttpRequest.buildHeaderWithGlobalHeader(headers)
  87 + }
  88 + return service.delete(url, config)
  89 + }
  90 +}
  1 +import HashMap from '@ohos.util.HashMap'
  2 +import { ConfigConstants } from 'wdConstant'
  3 +import { DateTimeUtils, Logger } from 'wdKit'
  4 +
  5 +/**
  6 + * 网络请求业务侧工具类
  7 + */
  8 +export class HttpUrlUtils {
  9 + /**
  10 + * uat环境url
  11 + */
  12 + static readonly HOST_UAT: string = "https://pd-apis-uat.pdnews.cn";
  13 + /**
  14 + * 中文端sit环境
  15 + */
  16 + static readonly HOST_SIT: string = "https://pd-apis-sit.pdnews.cn";
  17 + /**
  18 + * 正式环境url
  19 + */
  20 + static readonly HOST_PRODUCT: string = "https://pdapis.pdnews.cn";
  21 + /**
  22 + * dev环境url
  23 + */
  24 + static readonly HOST_DEV: string = "https://pd-apis-dev.pdnews.cn";
  25 + /**
  26 + * 启动接口(底导接口)
  27 + */
  28 + static readonly BOTTOM_NAV_PATH: string = "/api/rmrb-bff-display-zh/display/zh/c/bottomNavGroup";
  29 +
  30 + /**
  31 + * 展现pageInfo接口
  32 + */
  33 + static readonly PAGE_INFO_PATH: string = "/api/rmrb-bff-display-zh/display/zh/c/pageInfo";
  34 + /**
  35 + * 展现comp接口
  36 + */
  37 + static readonly COMP_PATH: string = "/api/rmrb-bff-display-zh/display/zh/c/compInfo";
  38 + /**
  39 + * 详情页面详情接口
  40 + */
  41 + static readonly DETAIL_PATH: string = "/api/rmrb-bff-display-zh/content/zh/c/content/detail";
  42 + /**
  43 + * 批查接口,查询互动相关数据,如收藏数、评论数等
  44 + */
  45 + static readonly INTERACT_DATA_PATH: string = "/api/rmrb-contact/contact/zh/c/content/interactData";
  46 +
  47 + /**
  48 + * 电子报信息
  49 + */
  50 + static readonly E_NEWSPAPER_INFO_PATH: string = "/api/rmrb-bff-display-zh/display/zh/c/paperApi/paperTime";
  51 +
  52 + /**
  53 + * 电子报列表
  54 + */
  55 + static readonly E_NEWSPAPER_LIST_PATH: string = "/api/rmrb-bff-display-zh/display/zh/c/paperApi/paperList";
  56 +
  57 + private static hostUrl: string = HttpUrlUtils.HOST_PRODUCT;
  58 +
  59 + static getCommonHeaders(): HashMap<string, string> {
  60 + let headers: HashMap<string, string> = new HashMap<string, string>()
  61 + headers.set('User-Agent', 'Dalvik/2.1.0 (Linux; U; Android 13; 22101317C Build/TKQ1.221013.002)')
  62 + headers.set('channel', HttpUrlUtils.getChannel())
  63 + //headers.set('appCode', ConfigConstants.appCode)
  64 + headers.set('plat', HttpUrlUtils.getPlat())
  65 + //headers.set('Authorization', 'APPCODE 83092caa603a421aa0222308b3f6b27a')
  66 + headers.set('Content-Type', 'application/json; charset=utf-8')
  67 + headers.set('timestamp', HttpUrlUtils.getTimestamp())
  68 + headers.set('RMRB-X-TOKEN', HttpUrlUtils.getXToken())
  69 + headers.set('device_id', HttpUrlUtils.getDeviceId())
  70 + headers.set('cookie', 'RMRB-X-TOKEN=eyJhbGciOiJIUzI1NiIsImtpZCI6ImQ4WkI2QkhxSEZrdjJ2U25BNlRwZEdKRjBHcjItVzBvS2FaYzdLOUUycmcifQ.eyJpc3MiOiJwZW9wbGVzLWRhaWx5LWZvdXJhIiwic3ViIjoicGVvcGxlcy1kYWlseS1mb3VyYSIsImV4cCI6MTcwMzY0OTYwNiwidXNlcklkIjo0NTk3NzYyOTc0NzQ5NDksInVzZXJWZXJzaW9uIjoiNDU5Nzc2Mjk3NDc0OTQ5XzIiLCJ1c2VyTmFtZSI6IkJ1bGlraWtpMTgxIiwidXNlclR5cGUiOjIsImNyZWF0b3JJZCI6NDI2NTM5MH0.jhQ9kylcm3FxWf0-lBMZuLkdtIQ6XpFnAi0AFZJNwfc')
  71 + headers.set('build_version', HttpUrlUtils.getVersion())
  72 + headers.set('adcode', HttpUrlUtils.getAdCode())
  73 + headers.set('os_version', HttpUrlUtils.getOsVersion())
  74 + //headers.set('X-Ca-Stage', 'PRE')
  75 + headers.set('versionCode', HttpUrlUtils.getVersionCode())
  76 + headers.set('system', HttpUrlUtils.getTerminalId())
  77 + headers.set('version_name', HttpUrlUtils.getVersionName())
  78 + headers.set('EagleEye-TraceID', 'D539562E48554A60977AF4BECB6D6C7A')
  79 + headers.set('imei', HttpUrlUtils.getImei())
  80 + headers.set('Accept-Language', 'zh')
  81 + headers.set('city', HttpUrlUtils.getCity())
  82 + headers.set('city_dode', HttpUrlUtils.getCityCode())
  83 + // TODO 判断是否登录
  84 + headers.set('userId', HttpUrlUtils.getUserId())
  85 + headers.set('userType', HttpUrlUtils.getUserType())
  86 +
  87 + headers.set('mpassid', 'ZbHTMeTsfaYDAHqt8ZHIzcPs')
  88 + HttpUrlUtils.addSpecialHeaders(headers);
  89 + // Logger.debug("TAG", '******************* commonHeaders headers start ******************************** ');
  90 + // headers.forEach((v,k)=>{
  91 + // Logger.debug("TAG", 'getCommonHeaders header: ' + k + ': ' + v);
  92 + // })
  93 + // Logger.debug("TAG", '******************* commonHeaders headers end ******************************** ');
  94 + return headers;
  95 + }
  96 +
  97 + static addSpecialHeaders(headers: HashMap<string, string>) {
  98 + switch (HttpUrlUtils.hostUrl) {
  99 + case HttpUrlUtils.HOST_UAT:
  100 + // TODO 待优化到常量类里
  101 + headers.set('X-Ca-Stage', 'PRE');
  102 + headers.set('Authorization', 'APPCODE 83092caa603a421aa0222308b3f6b27a');
  103 + headers.set('appCode', '83092caa603a421aa0222308b3f6b27a');
  104 + break
  105 + case HttpUrlUtils.HOST_SIT:
  106 + headers.set('X-Ca-Stage', 'TEST');
  107 + headers.set('Authorization', 'APPCODE 0af1f9085e484c97b2a44704bae72c07');
  108 + headers.set('appCode', '0af1f9085e484c97b2a44704bae72c07');
  109 + break
  110 + case HttpUrlUtils.HOST_PRODUCT:
  111 + headers.set('X-Ca-Stage', 'RELEASE');
  112 + headers.set('Authorization', 'APPCODE 3d4181bceeb94d9780e10dbb6c67bbf6');
  113 + headers.set('appCode', '3d4181bceeb94d9780e10dbb6c67bbf6');
  114 + break
  115 + case HttpUrlUtils.HOST_DEV:
  116 + headers.set('X-Ca-Stage', 'TEST');
  117 + headers.set('Authorization', 'APPCODE ff33172859e14f9a8299e3bd769e79f9');
  118 + headers.set('appCode', 'ff33172859e14f9a8299e3bd769e79f9');
  119 + break
  120 + default:
  121 + break
  122 + }
  123 + }
  124 +
  125 + static getHost() {
  126 + return HttpUrlUtils.hostUrl;
  127 + }
  128 +
  129 + private static getCity() {
  130 + // TODO 对接定位
  131 + return '%E5%90%88%E8%82%A5%E5%B8%82';
  132 + }
  133 +
  134 + private static getChannel() {
  135 + // TODO 对接配置
  136 + return 'rmrb_china_0000';
  137 + }
  138 +
  139 + private static getPlat() {
  140 + return 'Phone';
  141 + }
  142 +
  143 + private static getTimestamp() {
  144 + // return DateTimeUtils.getCurrentTime() + '';
  145 + // TODO 暂时写死,有些page 真实时间戳 返回数据为空
  146 + return '155203523';
  147 + }
  148 +
  149 + private static getXToken() {
  150 + return 'eyJhbGciOiJIUzI1NiIsImtpZCI6ImQ4WkI2QkhxSEZrdjJ2U25BNlRwZEdKRjBHcjItVzBvS2FaYzdLOUUycmcifQ.eyJpc3MiOiJwZW9wbGVzLWRhaWx5LWZvdXJhIiwic3ViIjoicGVvcGxlcy1kYWlseS1mb3VyYSIsImV4cCI6MTcwMzY0OTYwNiwidXNlcklkIjo0NTk3NzYyOTc0NzQ5NDksInVzZXJWZXJzaW9uIjoiNDU5Nzc2Mjk3NDc0OTQ5XzIiLCJ1c2VyTmFtZSI6IkJ1bGlraWtpMTgxIiwidXNlclR5cGUiOjIsImNyZWF0b3JJZCI6NDI2NTM5MH0.jhQ9kylcm3FxWf0-lBMZuLkdtIQ6XpFnAi0AFZJNwfc';
  151 + }
  152 +
  153 + private static getDeviceId() {
  154 + // TODO
  155 + return '8a81226a-cabd-3e1b-b630-b51db4a720ed';
  156 + }
  157 +
  158 + private static getVersion() {
  159 + // TODO
  160 + return '202401242103';
  161 + }
  162 +
  163 + private static getVersionCode() {
  164 + // TODO
  165 + return '7301';
  166 + }
  167 +
  168 + private static getVersionName() {
  169 + // TODO
  170 + return '7.3.0.1';
  171 + }
  172 +
  173 + private static getAdCode() {
  174 + return '340000';
  175 + }
  176 +
  177 + private static getOsVersion() {
  178 + // TODO
  179 + return '12';
  180 + }
  181 +
  182 + public static getCityCode() {
  183 + // TODO
  184 + return '340100';
  185 + }
  186 +
  187 + public static getProvinceCode() {
  188 + // TODO
  189 + return '340000';
  190 + }
  191 +
  192 + /**
  193 + * 地区code
  194 + */
  195 + public static getDistrictCode() {
  196 + // TODO
  197 + return '340103';
  198 + }
  199 +
  200 + private static getTerminalId() {
  201 + return 'Android';
  202 + }
  203 +
  204 + private static getImei() {
  205 + // TODO
  206 + return '8a81226a-cabd-3e1b-b630-b51db4a720ed';
  207 + }
  208 +
  209 + private static getUserId() {
  210 + // TODO 对接登录
  211 + return '459776297474949';
  212 + }
  213 +
  214 + private static getUserType() {
  215 + return '2';
  216 + }
  217 +}
  1 +// import { ResponseLogin } from '../bean/account/ResponseLogin';
  2 +import { Logger } from 'wdKit';
  3 +
  4 +// import { UserInfo } from '../bean/account/UserInfo';
  5 +// import { ConfigConstants } from '../constants/ConfigConstants';
  6 +// import { AccountManagerUtils } from './AccountManagerUtils';
  7 +// import { AppUtils } from './AppUtils';
  8 +// import { DeviceUtil } from './DeviceUtil';
  9 +// import { Logger } from './Logger';
  10 +// import { StringUtils } from './StringUtils';
  11 +
  12 +const TAG: string = '[HttpUtils]'
  13 +
  14 +export class HttpUtils {
  15 + static buildHeaders(): Record<string, string> {
  16 + let timestamp: number = new Date().getTime(); // 单位毫秒
  17 + // let clientId = DeviceUtil.clientId()
  18 +
  19 + let headers: Record<string, string> = {};
  20 + // 通用请求头
  21 + headers["version"] = 'V3';
  22 + // headers["appId"] = ConfigConstants.appId; // 应用id
  23 + // headers["terminalId"] = ConfigConstants.terminalId; // 终端ID
  24 + // headers["sourceId"] = ConfigConstants.sourceId; // 咪咕视频
  25 + // headers["appType"] = ConfigConstants.appType; // 手机客户端App(安卓)
  26 + // headers["clientType"] = ConfigConstants.clientType; // 客户端类型
  27 + // headers["appVersion"] = AppUtils.getAppVersionCode() + ''; // 客户端版本号:2600010500
  28 + // headers["APP-VERSION-CODE"] = AppUtils.getAppVersionCode() + ''; // APP版本号:250006577
  29 + // headers["appVersionName"] = AppUtils.getAppVersionName() // app对外显示版本: '6.1.5.00'
  30 + // headers["appCode"] = ConfigConstants.appCode; // 应用编码:产品_渠道_应用的拼接串,用下划线_ 拼接而成
  31 + // headers["ptvCode"] = ConfigConstants.ptvCode; // 基线版本号_应用版本号 todo:
  32 + // headers["clientProvinceCode"] = ''; // 客户端分省号 // 02
  33 + // headers["provinceCode"] = ''; // 客户端分省号 // 02
  34 + // headers["clientCityId"] = ''; // 客户端城市ID // 0210
  35 + // headers["carrierCode"] = ''; // 运营商信息
  36 +
  37 + // 设备信息请求头
  38 + // headers["User-Agent"] = ''; // 终端UA,自动获取
  39 + headers["Content-Type"] = 'application/json;charset=UTF-8'; // 返回/响应的HTTP内容类型
  40 + headers["os"] = 'android'; // 操作系统类型:鸿蒙、安卓或iOS
  41 + // headers["osInfo"] = 'AD'; // 操作系统信息
  42 + headers["Phone-Info"] = 'HUAWEI'; // 手机信息: todo
  43 + // headers["oaid"] = ''; // 开放匿名设备ID,是中国移动安全联盟(MSA)发起并制定标准用户识别ID
  44 + headers["networkInfo"] = 'WIFI'; // 网络类型: todo
  45 + headers["cache-control"] = 'no-cache'; // 请求和响应遵循的缓存机制
  46 + // headers["clientId"] = clientId; // 客户端编号:客户端初始化时生成的客户端ID,保证唯一性
  47 + headers["imei"] = 'd1de6d3ae0db44bea1b3f0e20a14d90a'; // 终端手机序列号: todo
  48 + headers["X-UP-CLIENT-CHANNEL-ID"] = '2600010500-99000-101700010130012'; // 客户端渠道ID: todo
  49 + headers["channelCode"] = 'VIDEO_APPMAIL'; // 渠道编码
  50 + // headers["l_c"] = clientId; // 客户端id,同clientId
  51 + // headers["l_t"] = timestamp + ''; // 本机时间戳
  52 + // headers["l_s"] = ''; // l_c和l_t拼接后的MD5校验
  53 +
  54 + // 签名相关请求头
  55 + headers["timeStamp"] = timestamp + ''; // 服务端时间戳(毫秒):1701667763664
  56 + headers["signType"] = 'RSA'; // 签名类型,固定RSA
  57 +
  58 + // 业务请求头
  59 + // headers["promotionID"] = '54b0f421-a6df-41d3-9be2-92820b2c5d8c'; // 促销Id todo
  60 + // headers["tenantId"] = ''; // 租户Id
  61 +
  62 + // 添加其他header
  63 + // headers["sdkCeId"] = '27fb3129-5a54-45bc-8af1-7dc8f1155501'; // 用户中台老接口定义的ID,保持不变,现网:咪咕视频Android版,27fb3129-5a54-45bc-8af1-7dc8f1155501 todo
  64 + headers["support-pendant"] = '1'; // 挂件标识, "1":客户端支持挂件节目结构;非"1":不支持挂件节目结构-展示通用对象
  65 + Logger.info(TAG, "buildHeader headers:" + JSON.stringify(headers));
  66 + return headers;
  67 + }
  68 +}
  69 +
  70 +// export default new HttpUtils()
  1 +{
  2 + "module": {
  3 + "name": "wdNetwork",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true,
  12 + "requestPermissions": [
  13 + {
  14 + "name": "ohos.permission.INTERNET"
  15 + }
  16 + ]
  17 + }
  18 +}
  1 +{
  2 + "color": [
  3 + {
  4 + "name": "white",
  5 + "value": "#FFFFFF"
  6 + }
  7 + ]
  8 +}
  1 +{
  2 + "string": [
  3 + {
  4 + "name": "shared_desc",
  5 + "value": "网络库"
  6 + }
  7 + ]
  8 +}
  1 +import localUnitTest from './LocalUnit.test';
  2 +
  3 +export default function testsuite() {
  4 + localUnitTest();
  5 +}
  1 +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium';
  2 +
  3 +export default function localUnitTest() {
  4 + describe('localUnitTest',() => {
  5 + // Defines a test suite. Two parameters are supported: test suite name and test suite function.
  6 + beforeAll(() => {
  7 + // Presets an action, which is performed only once before all test cases of the test suite start.
  8 + // This API supports only one parameter: preset action function.
  9 + });
  10 + beforeEach(() => {
  11 + // Presets an action, which is performed before each unit test case starts.
  12 + // The number of execution times is the same as the number of test cases defined by **it**.
  13 + // This API supports only one parameter: preset action function.
  14 + });
  15 + afterEach(() => {
  16 + // Presets a clear action, which is performed after each unit test case ends.
  17 + // The number of execution times is the same as the number of test cases defined by **it**.
  18 + // This API supports only one parameter: clear action function.
  19 + });
  20 + afterAll(() => {
  21 + // Presets a clear action, which is performed after all test cases of the test suite end.
  22 + // This API supports only one parameter: clear action function.
  23 + });
  24 + it('assertContain', 0, () => {
  25 + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
  26 + let a = 'abc';
  27 + let b = 'b';
  28 + // Defines a variety of assertion methods, which are used to declare expected boolean conditions.
  29 + expect(a).assertContain(b);
  30 + expect(a).assertEqual(a);
  31 + });
  32 + });
  33 +}
  1 +/node_modules
  2 +/oh_modules
  3 +/.preview
  4 +/build
  5 +/.cxx
  6 +/.test
  1 +export { WDRouterRule } from './src/main/ets/router/WDRouterRule'
  2 +
  3 +export { WDRouterPage } from './src/main/ets/router/WDRouterPage'
  4 +
  5 +export { registerRouter } from './src/main/ets/router/Action2Page'
  1 +{
  2 + "apiType": "stageMode",
  3 + "buildOption": {
  4 + "arkOptions": {
  5 + // "apPath": "./modules.ap" /* Profile used for profile-guided optimization (PGO), a compiler optimization technique to improve app runtime performance. */
  6 + }
  7 + },
  8 + "buildOptionSet": [
  9 + {
  10 + "name": "release",
  11 + "arkOptions": {
  12 + "obfuscation": {
  13 + "ruleOptions": {
  14 + "enable": true,
  15 + "files": [
  16 + "./obfuscation-rules.txt"
  17 + ]
  18 + }
  19 + }
  20 + }
  21 + },
  22 + ],
  23 + "targets": [
  24 + {
  25 + "name": "default"
  26 + }
  27 + ]
  28 +}
  1 +import { hspTasks } from '@ohos/hvigor-ohos-plugin';
  2 +
  3 +export default {
  4 + system: hspTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
  5 + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
  6 +}
  1 +# Define project specific obfuscation rules here.
  2 +# You can include the obfuscation configuration files in the current module's build-profile.json5.
  3 +#
  4 +# For more details, see
  5 +# https://gitee.com/openharmony/arkcompiler_ets_frontend/blob/master/arkguard/README.md
  6 +
  7 +# Obfuscation options:
  8 +# -disable-obfuscation: disable all obfuscations
  9 +# -enable-property-obfuscation: obfuscate the property names
  10 +# -enable-toplevel-obfuscation: obfuscate the names in the global scope
  11 +# -compact: remove unnecessary blank spaces and all line feeds
  12 +# -remove-log: remove all console.* statements
  13 +# -print-namecache: print the name cache that contains the mapping from the old names to new names
  14 +# -apply-namecache: reuse the given cache file
  15 +
  16 +# Keep options:
  17 +# -keep-property-name: specifies property names that you want to keep
  18 +# -keep-global-name: specifies names that you want to keep in the global scope
  1 +{
  2 + "meta": {
  3 + "stableOrder": true
  4 + },
  5 + "lockfileVersion": 3,
  6 + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
  7 + "specifiers": {
  8 + "wdBean@../../../../features/wdBean": "wdBean@../../features/wdBean",
  9 + "wdKit@../../../../commons/wdKit": "wdKit@../wdKit"
  10 + },
  11 + "packages": {
  12 + "wdBean@../../features/wdBean": {
  13 + "name": "wdbean",
  14 + "resolved": "../../features/wdBean",
  15 + "registryType": "local"
  16 + },
  17 + "wdKit@../wdKit": {
  18 + "name": "wdkit",
  19 + "resolved": "../wdKit",
  20 + "registryType": "local"
  21 + }
  22 + }
  23 +}
1 { 1 {
2 - "license": "", 2 + "license": "Apache-2.0",
3 "devDependencies": {}, 3 "devDependencies": {},
4 "author": "", 4 "author": "",
5 - "name": "entry", 5 + "name": "wdrouter",
6 "description": "Please describe the basic information.", 6 "description": "Please describe the basic information.",
7 - "main": "", 7 + "main": "Index.ets",
8 "version": "1.0.0", 8 "version": "1.0.0",
9 "dependencies": { 9 "dependencies": {
10 - "wdComponent": "file:../wdComponent",  
11 - "wdConstant": "file:../wdConstant",  
12 "wdKit": "file:../wdKit", 10 "wdKit": "file:../wdKit",
13 - "wdWebComponent": "file:../wdWebComponent",  
14 - "wdBean": "file:../wdBean",  
15 - "wdRouter": "file:../wdRouter",  
16 - "wdNetwork": "file:../wdNetwork" 11 + "wdBean": "file:../../features/wdBean"
17 } 12 }
18 } 13 }
  1 +import ArrayList from '@ohos.util.ArrayList';
  2 +import { Action } from 'wdBean'
  3 +import { WDRouterPage } from './WDRouterPage'
  4 +
  5 +interface HandleObject {
  6 + handle: (action: Action) => (WDRouterPage | undefined)
  7 + priority: number
  8 +}
  9 +
  10 +export class Action2Page {
  11 + private static handles: Record<string, ArrayList<HandleObject>> = {};
  12 +
  13 + static register(actionType: string, handle: (action: Action) => (WDRouterPage | undefined), priority: number = 0) {
  14 + let handles = Action2Page.handles[actionType] ?? new ArrayList();
  15 + let obj: HandleObject = {
  16 + handle: handle,
  17 + priority: priority
  18 + };
  19 + handles.add(obj);
  20 + handles.sort((f, s) => {
  21 + return f.priority - s.priority;
  22 + })
  23 + Action2Page.handles[actionType] = handles;
  24 + }
  25 +
  26 + static get(action?: Action): WDRouterPage | undefined {
  27 + if (!action || !action.type) {
  28 + return undefined;
  29 + }
  30 + let handles = Action2Page.handles[action.type];
  31 + if (!handles) {
  32 + return undefined;
  33 + }
  34 + let page: WDRouterPage | undefined
  35 + for (let i = 0; i < handles.length; i++) {
  36 + let tmp = (handles[i] as HandleObject).handle(action);
  37 + if (tmp) {
  38 + page = tmp;
  39 + break
  40 + }
  41 + }
  42 + return page
  43 + }
  44 +}
  45 +
  46 +export function registerRouter() {
  47 + // Action2Page.register("USER_LOGIN", (action: Action) => {
  48 + // return WDRouterPage.webLoginPage
  49 + // })
  50 +
  51 + Action2Page.register("JUMP_DETAIL_PAGE", (action: Action) => {
  52 + // if (action.params?.detailPageType == 2 || action.params?.detailPageType == 6) {
  53 + // return WDRouterPage.detailPlayLivePage
  54 + // }
  55 + if (action.params?.detailPageType == 7 || action.params?.detailPageType == 8) {
  56 + return WDRouterPage.detailPlayShortVideoPage
  57 + }
  58 + return WDRouterPage.detailPlayVodPage
  59 + })
  60 +
  61 + Action2Page.register("JUMP_H5_BY_WEB_VIEW", (action) => {
  62 + return WDRouterPage.defaultWebPage
  63 + })
  64 +
  65 + Action2Page.register("JUMP_INNER_NEW_PAGE", (action) => {
  66 + if (action.params?.pageID == "E_NEWSPAPER") {
  67 + return WDRouterPage.eNewspaper
  68 + } else if (action.params?.pageID == "MorningEveningPaper"){
  69 + return WDRouterPage.morningEveningPaperPage
  70 + } else if (action.params?.pageID == "IMAGE_TEXT_DETAIL"){
  71 + return WDRouterPage.imageTextDetailPage
  72 + }
  73 + return undefined
  74 + })
  75 +}
  1 +import bundleManager from '@ohos.bundle.bundleManager'
  2 +
  3 +export class WDRouterPage {
  4 + private moduleName: string
  5 + private pagePath: string
  6 +
  7 + constructor(moduleName: string, pagePath: string) {
  8 + this.moduleName = moduleName
  9 + this.pagePath = pagePath
  10 + }
  11 +
  12 + url() {
  13 + let bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT)
  14 + return `@bundle:${bundleInfo.name}/${this.moduleName}/${this.pagePath}`
  15 + }
  16 +
  17 + // 主页
  18 + static index = new WDRouterPage("entry", "ets/pages/Index");
  19 + // 关于页面
  20 + // static aboutPage = new WDRouterPage("entry", "ets/pages/about/AboutPage");
  21 + // web默认页面
  22 + static defaultWebPage = new WDRouterPage("entry", "ets/pages/web/DefaultWebPage");
  23 + // 电子报页面
  24 + static eNewspaper = new WDRouterPage("entry", "ets/pages/ENewspaper")
  25 + // 早晚报页面
  26 + static morningEveningPaperPage = new WDRouterPage("entry", "ets/pages/MorningEveningPaperPage")
  27 + // 图文详情页
  28 + static imageTextDetailPage = new WDRouterPage("entry", "ets/pages/ImageAndTextDetailPage");
  29 + // 短视频详情页
  30 + static detailPlayShortVideoPage = new WDRouterPage("wdDetailPlayShortVideo", "ets/pages/DetailPlayShortVideoPage");
  31 + // 点播详情页
  32 + static detailPlayVodPage = new WDRouterPage("wdDetailPlayVod", "ets/pages/DetailPlayVodPage");
  33 + // 直播详情页
  34 + static detailPlayLivePage = new WDRouterPage("wdDetailPlayLive", "ets/pages/DetailPlayLivePage");
  35 +}
  1 +import router from '@ohos.router'
  2 +import { Action } from 'wdBean'
  3 +import { ToastUtils } from 'wdKit'
  4 +import { Action2Page } from './Action2Page'
  5 +import { WDRouterPage } from './WDRouterPage'
  6 +
  7 +export class WDRouterRule {
  8 + static jumpWithAction(action?: Action) {
  9 + if (!action || !action.type) {
  10 + ToastUtils.showToast("跳转参数异常", 1000);
  11 + return
  12 + }
  13 + let page = Action2Page.get(action);
  14 + WDRouterRule.jumpWithPage(page, action)
  15 + }
  16 +
  17 + private static jumpWithPage(page?: WDRouterPage, params?: object) {
  18 + if (page) {
  19 + if (params) {
  20 + // router.pushUrl({ url: 'pages/routerpage2', , params: params })
  21 + router.pushUrl({ url: page.url(), params: params })
  22 + } else {
  23 + router.pushUrl({ url: page.url() })
  24 + }
  25 + } else {
  26 + ToastUtils.showToast("功能开发中", 1000);
  27 + }
  28 + }
  29 +}
  1 +{
  2 + "module": {
  3 + "name": "wdRouter",
  4 + "type": "shared",
  5 + "description": "$string:shared_desc",
  6 + "deviceTypes": [
  7 + "phone",
  8 + "tablet",
  9 + "2in1"
  10 + ],
  11 + "deliveryWithInstall": true
  12 + }
  13 +}