From 6ef4ef6d276287647d344c7ff3b0c16d1a78e178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Tue, 7 May 2024 17:41:21 +0800 Subject: [PATCH] init --- .gitignore | 6 + BuildProfile.ets | 5 + CHANGELOG.md | 9 + Index.ets | 40 +++ LICENSE | 15 ++ README.md | 229 +++++++++++++++++ UTILS.md | 211 ++++++++++++++++ build-profile.json5 | 44 ++++ consumer-rules.txt | 0 hvigorfile.ts | 6 + obfuscation-rules.txt | 1 + oh-package.json5 | 10 + src/main/ets/dialog/XDialogCommon.ets | 9 + src/main/ets/dialog/XDialogController.ets | 4 + src/main/ets/dialog/XDialogList.ets | 82 ++++++ src/main/ets/http/HttpHelper.ts | 189 ++++++++++++++ src/main/ets/pages/XWebview.ets | 233 ++++++++++++++++++ src/main/ets/utils/AppStorageHelper.ets | 22 ++ src/main/ets/utils/PreferencesHelper.ets | 23 ++ src/main/ets/utils/ToolsHelper.ets | 200 +++++++++++++++ src/main/ets/utils/ValidatorHelper.ets | 14 ++ src/main/ets/utils/WindowHelper.ets | 93 +++++++ src/main/ets/utils/XWebHelper.ets | 31 +++ src/main/module.json5 | 11 + src/main/resources/base/element/string.json | 16 ++ .../resources/base/media/title_bar_back.png | Bin 0 -> 922 bytes src/main/resources/en_US/element/string.json | 16 ++ src/main/resources/zh_CN/element/string.json | 16 ++ src/test/List.test.ets | 5 + src/test/LocalUnit.test.ets | 33 +++ 30 files changed, 1573 insertions(+) create mode 100644 .gitignore create mode 100644 BuildProfile.ets create mode 100644 CHANGELOG.md create mode 100644 Index.ets create mode 100644 LICENSE create mode 100644 README.md create mode 100644 UTILS.md create mode 100644 build-profile.json5 create mode 100644 consumer-rules.txt create mode 100644 hvigorfile.ts create mode 100644 obfuscation-rules.txt create mode 100644 oh-package.json5 create mode 100644 src/main/ets/dialog/XDialogCommon.ets create mode 100644 src/main/ets/dialog/XDialogController.ets create mode 100644 src/main/ets/dialog/XDialogList.ets create mode 100644 src/main/ets/http/HttpHelper.ts create mode 100644 src/main/ets/pages/XWebview.ets create mode 100644 src/main/ets/utils/AppStorageHelper.ets create mode 100644 src/main/ets/utils/PreferencesHelper.ets create mode 100644 src/main/ets/utils/ToolsHelper.ets create mode 100644 src/main/ets/utils/ValidatorHelper.ets create mode 100644 src/main/ets/utils/WindowHelper.ets create mode 100644 src/main/ets/utils/XWebHelper.ets create mode 100644 src/main/module.json5 create mode 100644 src/main/resources/base/element/string.json create mode 100644 src/main/resources/base/media/title_bar_back.png create mode 100644 src/main/resources/en_US/element/string.json create mode 100644 src/main/resources/zh_CN/element/string.json create mode 100644 src/test/List.test.ets create mode 100644 src/test/LocalUnit.test.ets diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2713a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/BuildProfile.ets b/BuildProfile.ets new file mode 100644 index 0000000..3479da7 --- /dev/null +++ b/BuildProfile.ets @@ -0,0 +1,5 @@ +export default class BuildProfile { + static readonly HAR_VERSION = '1.0.2'; + static readonly BUILD_MODE_NAME = 'debug'; + static readonly DEBUG = true; +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..23ebc86 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# [v1.0.0] 2024.04.23 + +------ + +> - 网络请求 +> - 正则验证 +> - 基础工具 +> - 统一弹窗 +> - 存储管理 \ No newline at end of file diff --git a/Index.ets b/Index.ets new file mode 100644 index 0000000..5c6ab5c --- /dev/null +++ b/Index.ets @@ -0,0 +1,40 @@ +/** + * 常用工具 + */ +export { ToolsHelper } from './src/main/ets/utils/ToolsHelper' + +/** + * 存储相关 + */ +export { AppStorageHelper } from './src/main/ets/utils/AppStorageHelper' + +export { PreferencesHelper } from './src/main/ets/utils/PreferencesHelper' + +/** + * 正则相关 + */ +export { ValidatorHelper } from './src/main/ets/utils/ValidatorHelper' + +/** + * 网络请求 + */ +export { HttpHelper } from './src/main/ets/http/HttpHelper' + +/** + * 打开H5页面 + */ +export { XWebHelper } from './src/main/ets/utils/XWebHelper' + +/** + * 列表选择弹窗 + */ +export { XDialogList } from './src/main/ets/dialog/XDialogList' +export { XDialogCommon } from './src/main/ets/dialog/XDialogCommon' + +export { XDialogController } from './src/main/ets/dialog/XDialogController' + +/** + * 窗口管理 + */ +export { WindowHelper } from './src/main/ets/utils/WindowHelper' + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a6f730a --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +Mozilla Public License Version 2.0 + +1. Definitions +2. Scope +3. Grant of License +4. Restrictions +5. Original Code +6. Modifications +7. Required Notices +8. Disclaimer of Warranty +9. Limitation of Liability +10. Termination +11. Jurisdiction +12. Miscellaneous + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe2c4c2 --- /dev/null +++ b/README.md @@ -0,0 +1,229 @@ +# 基础开发工具包 +```shell +ohpm install @szyx/sdk_base +``` + +## 1.[utils](./src/main/ets/utils) + +### 1.1.[AppStorageHelper](./src/main/ets/utils/AppStorageHelper.ets) +> 缓存工具类,运行时存储,应用停止运行后清空 + +```typescript +import { AppStorageHelper } from '@szyx/sdk_base/Index' + +// 存储string数据 +AppStorageHelper.save(StorageKeys.CLIENT_ID, d) + + // 获取存储的strign数据 +let d = AppStorageHelper.get(StorageKeys.CLIENT_ID) +``` + +### 1.2.[PreferencesHelper](./src/main/ets/utils/PreferencesHelper.ets) +> 永久存储类,应用停止后也不会清空 +> 需要验证,更新应用会不会被清理 +> 可存储类型 `number | string | boolean | Array | Array | Array | Uint8Array` + +```typescript +import { PreferencesHelper } from '@szyx/sdk_base/Index' +// 存储数据 +PreferencesHelper.put(StorageKeys.CLIENT_ID, value) + + // 获取存储的数据 +PreferencesHelper.get(StorageKeys.CLIENT_ID).then(res => { + console.log('>>>>>', res) +}) +``` + +### 1.3.[ToolsHelper](./src/main/ets/utils/ToolsHelper.ets) +> 常用方法工具栏 +> 基础方法 +#### 1.3.1.弹出Toast提示 + +```typescript +import { ToolsHelper } from '@szyx/sdk_base'; + +ToolsHelper.showMessage('Hello Word!') +``` + +### 1.4.[ValidatorHelper](./src/main/ets/utils/ValidatorHelper.ets) +> 常用正则 + +#### 1.4.1.验证手机号 + +```typescript +import { ValidatorHelper } from '@szyx/sdk_base'; + ValidatorHelper.isPhone('13800000000') +``` + +### 1.5.[XWebHelper](./src/main/ets/utils/XWebHelper.ets) +> 打开webview页面 + +```typescript +import { XWebHelper } from '@szyx/sdk_base'; +// 必须先引入,否则无法跳转 +const XWebview = import('../pages/XWebview'); + +XWebHelper.openWeb({ + url: 'https://www.baidu.com', + title: '百度一下', +}) +``` + +## 2.[Dialog](./src/main/ets/dialog) +### 2.1.弹出list选中弹窗 +```typescript +import { XDialogController } from '../dialog/XDialogController'; +import { XDialogList } from '../dialog/XDialogList'; + +@Component +struct MyView { + // 控制器,控制开关 + dialogController: XDialogController = {} as XDialogController + + build() { + Column() { + Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) { + Image($r('sys.media.ohos_ic_public_more')) + .width(26).height(26) + }.width(65) + .onClick(() => { + if (this.dialogController != null) { + this.dialogController.open() + } + }) + + XDialogList({ + // 控制器 + controller: this.dialogController, + // 标题(可选) + title: '选择您的操作', + // 选择内容列表 + values: ['刷新', '浏览器打开', '分享', '复制地址'], + // 用户选择事件 + onSelected: (index: number, value: string) => { + ToolsHelper.showMessage(`用户选择了第${index}个,内容为:${value}`) + }, + // 用户取消事件 + onCancel: () => { + ToolsHelper.showMessage('用户取消操作') + }, + // 是否可取消(点击空白处,或者物理返回键) + autoCancel: true + }) + + }.width('100%').height('100%') + } +} + +``` +## 3.[网络请求](./src/main/ets/http/HttpHelper.ts) +> 使用室建议二次封装 +> +> 参数定义 +> ```typescript +> /** +> * +> * @param url url地址 +> * @param data 请求参数 +> * @param headers 请求头 +> * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 +> * @returns +> */ +> ``` + +### 3.1.get请求 +```typescript + + HttpHelper.get() + .get>(url.url.startsWith('http') ? url.url : GlobalValue.getInstance().envUrl + url.url, + data ? JSON.stringify(data) : undefined, { + userId: GlobalValue.getInstance().userId, + clientId: GlobalValue.getInstance().getClientId(), + version: ConstantValue.VERSION, + deviceType: '01', + timeStamp: timeStamp + '', + sign: sign, + phoneModel: 'sign', + phoneVersion: 'sign', + phoneBrand: 'HarmonyOS' + }, url.apiNo) + .then((res: HttpResult) => { + if (res.status === '0') { + resolve(res.data as T) + } else { + reject(new Error(res.message)) + } + + }) + .catch((error: Error) => { + reject(error) + }) +``` +### 3.2.postJson +```typescript + +HttpHelper.get() + .post>(url.url.startsWith('http') ? url.url : GlobalValue.getInstance().envUrl + url.url, + data ? JSON.stringify(data) : undefined, { + userId: GlobalValue.getInstance().userId, + clientId: GlobalValue.getInstance().getClientId(), + version: ConstantValue.VERSION, + deviceType: '01', + timeStamp: timeStamp + '', + sign: sign, + phoneModel: 'sign', + phoneVersion: 'sign', + phoneBrand: 'HarmonyOS' + }, url.apiNo) + .then((res: HttpResult) => { + if (res.status === '0') { + resolve(res.data as T) + } else { + reject(new Error(res.message)) + } + + }) + .catch((error: Error) => { + reject(error) + }) +``` + + + +# **** 常见问题 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UTILS.md b/UTILS.md new file mode 100644 index 0000000..bc52926 --- /dev/null +++ b/UTILS.md @@ -0,0 +1,211 @@ +- # [@State装饰器:组件内状态](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-state-0000001774279614) +> @State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。 +> +> 在状态变量相关装饰器中,@State是最基础的,使变量拥有状态属性的装饰器,它也是大部分状态变量的数据源。 + +> @State装饰的变量,与声明式范式中的其他被装饰变量一样,是私有的,只能从组件内部访问,在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。 +> +> @State装饰的变量拥有以下特点: +> +> @State装饰的变量与子组件中的@Prop装饰变量之间建立单向数据同步,与@Link、@ObjectLink装饰变量之间建立双向数据同步。 +> +> @State装饰的变量生命周期与其所属自定义组件的生命周期相同。 +> + +- # [@Prop装饰器:父子单向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-prop-0000001774119942) +> @Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。 + +>@Prop装饰的变量和父组件建立单向的同步关系: +> +>@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。 +> +>当数据源更改时,@Prop装饰的变量都会更新,并且会覆盖本地所有更改。因此,数值的同步是父组件到子组件(所属组件),子组件数值的变化不会同步到父组件。 +> +- # [@Link装饰器:父子双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-link-0000001820999565) + +> 子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 +> +> @Link装饰的变量与其父组件中的数据源共享相同的值。 +> +> @Link装饰器不能在@Entry装饰的自定义组件中使用。 + +- # [@Provide装饰器和@Consume装饰器:与后代组件双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-provide-and-consume-0000001820879589) + +> @Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递,@Provide和@Consume摆脱参数传递机制的束缚,实现跨层级传递。 +> +> 其中@Provide装饰的变量是在祖先组件中,可以理解为被“提供”给后代的状态变量。@Consume装饰的变量是在后代组件中,去“消费(绑定)”祖先组件提供的变量。 + +> @Provide/@Consume装饰的状态变量有以下特性: +> +> @Provide装饰的状态变量自动对其所有后代组件可用,即该变量被“provide”给他的后代组件。由此可见,@Provide的方便之处在于,开发者不需要多次在组件之间传递变量。 +> +> 后代通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步,与@State/@Link不同的是,前者可以在多层级的父子组件之间传递。 +> +> @Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,建议类型相同,否则会发生类型隐式转换,从而导致应用行为异常。 + +```typescript +// 通过相同的变量名绑定 +@Provide a: number = 0; +@Consume a: number; + +// 通过相同的变量别名绑定 +@Provide('a') b: number = 0; +@Consume('a') c: number; +@Provide和@Consume通过相同的变量名或者相同的变量别名绑定时,@Provide装饰的变量和@Consume装饰的变量是一对多的关系。不允许在同一个自定义组件内,包括其子组件中声明多个同名或者同别名的@Provide装饰的变量,@Provide的属性名或别名需要唯一且确定,如果声明多个同名或者同别名的@Provide装饰的变量,会发生运行时报错。 + +``` + +- # [@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-observed-and-objectlink-0000001774279618) +> 上文所述的装饰器仅能观察到第一层的变化,但是在实际应用开发中,应用会根据开发需要,封装自己的数据模型。对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。 + +> @ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步: +> +> 被@Observed装饰的类,可以被观察到属性的变化; +> +> 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。 +> +> 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。 + +> 使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。 +> +> @ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。 + + + + + +- # [@Watch装饰器:状态变量更改通知](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-watch-0000001774119954) +> @Watch应用于对状态变量的监听。如果开发者需要关注某个状态变量的值是否改变,可以使用@Watch为状态变量设置回调函数。 + +> @Watch用于监听状态变量的变化,当状态变量变化时,@Watch的回调方法将被调用。@Watch在ArkUI框架内部判断数值有无更新使用的是严格相等(===),遵循严格相等规范。当在严格相等为false的情况下,就会触发@Watch的回调。 + +```typescript +@Component +struct TotalView { + @Prop @Watch('onCountUpdated') count: number = 0; + @State total: number = 0; + // @Watch 回调 + onCountUpdated(propName: string): void { + this.total += this.count; + } + + build() { + Text(`Total: ${this.total}`) + } +} + +@Entry +@Component +struct CountModifier { + @State count: number = 0; + + build() { + Column() { + Button('add to basket') + .onClick(() => { + this.count++ + }) + TotalView({ count: this.count }) + } + } +} +``` + + + +- # [$$语法:内置组件双向同步](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-two-way-sync-0000001820999577) + +```typescript +// xxx.ets +@Entry +@Component +struct TextInputExample { + @State text: string = '' + controller: TextInputController = new TextInputController() + + build() { + Column({ space: 20 }) { + Text(this.text) + TextInput({ text: $$this.text, placeholder: 'input your word...', controller: this.controller }) + .placeholderColor(Color.Grey) + .placeholderFont({ size: 14, weight: 400 }) + .caretColor(Color.Blue) + .width(300) + }.width('100%').height('100%').justifyContent(FlexAlign.Center) + } +} +``` + + + +- # [@Track装饰器:class对象属性级更新](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-track-0000001820879601) + +> @Track应用于class对象的属性级更新。@Track装饰的属性变化时,只会触发该属性关联的UI更新。 + +> @Track是class对象的属性装饰器。当一个class对象是状态变量时,@Track装饰的属性发生变化,只会触发该属性关联的UI更新;而未被标记的属性不能在UI中使用。 + +```typescript +class LogTrack { + @Track str1: string; + @Track str2: string; + + constructor(str1: string) { + this.str1 = str1; + this.str2 = 'World'; + } +} + +class LogNotTrack { + str1: string; + str2: string; + + constructor(str1: string) { + this.str1 = str1; + this.str2 = '世界'; + } +} + +@Entry +@Component +struct AddLog { + @State logTrack: LogTrack = new LogTrack('Hello'); + @State logNotTrack: LogNotTrack = new LogNotTrack('你好'); + + isRender(index: number) { + console.log(`Text ${index} is rendered`); + return 50; + } + + build() { + Row() { + Column() { + Text(this.logTrack.str1) // UINode1 + .fontSize(this.isRender(1)) + .fontWeight(FontWeight.Bold) + Text(this.logTrack.str2) // UINode2 + .fontSize(this.isRender(2)) + .fontWeight(FontWeight.Bold) + Button('change logTrack.str1') + .onClick(() => { + this.logTrack.str1 = 'Bye'; + }) + Text(this.logNotTrack.str1) // UINode3 + .fontSize(this.isRender(3)) + .fontWeight(FontWeight.Bold) + Text(this.logNotTrack.str2) // UINode4 + .fontSize(this.isRender(4)) + .fontWeight(FontWeight.Bold) + Button('change logNotTrack.str1') + .onClick(() => { + this.logNotTrack.str1 = '再见'; + }) + } + .width('100%') + } + .height('100%') + } +} +``` + + + diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..abede8d --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,44 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + { + "name": "release2", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + } + ] +} diff --git a/consumer-rules.txt b/consumer-rules.txt new file mode 100644 index 0000000..e69de29 diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..4218707 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/obfuscation-rules.txt b/obfuscation-rules.txt new file mode 100644 index 0000000..2a06f12 --- /dev/null +++ b/obfuscation-rules.txt @@ -0,0 +1 @@ +-remove-log \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..92253e2 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "name": "@szyx/sdk_base", + "version": "1.0.2", + "description": "数字医信公司,鸿蒙app开发基础工具。", + "main": "Index.ets", + "author": "xuqm", + "license": "Mozilla-2.0", + "repository": "", + "dependencies": {} +} diff --git a/src/main/ets/dialog/XDialogCommon.ets b/src/main/ets/dialog/XDialogCommon.ets new file mode 100644 index 0000000..44ebf11 --- /dev/null +++ b/src/main/ets/dialog/XDialogCommon.ets @@ -0,0 +1,9 @@ +@CustomDialog +export struct XDialogCommon { + controller: CustomDialogController + @BuilderParam dialogContent: () => void + + build() { + this.dialogContent() + } +} \ No newline at end of file diff --git a/src/main/ets/dialog/XDialogController.ets b/src/main/ets/dialog/XDialogController.ets new file mode 100644 index 0000000..cbbddaf --- /dev/null +++ b/src/main/ets/dialog/XDialogController.ets @@ -0,0 +1,4 @@ +export interface XDialogController { + open: () => void + close: () => void +} \ No newline at end of file diff --git a/src/main/ets/dialog/XDialogList.ets b/src/main/ets/dialog/XDialogList.ets new file mode 100644 index 0000000..a04b154 --- /dev/null +++ b/src/main/ets/dialog/XDialogList.ets @@ -0,0 +1,82 @@ +import { XDialogCommon } from './XDialogCommon' +import { XDialogController } from './XDialogController' + + +@Preview +@Component +export struct XDialogList { + controller: XDialogController | null = null + onSelected: (index: number, value: string) => void = (index: number, value: string) => { + } + onCancel?: () => void = undefined + title?: string = undefined + values?: Array | Array = undefined + autoCancel?: boolean = false + private dialogController: CustomDialogController | null = null + + build() { + this.buildContent() + } + + aboutToAppear(): void { + + this.dialogController = new CustomDialogController({ + builder: XDialogCommon({ + dialogContent: () => { + this.buildContent() + }, + }), + alignment: DialogAlignment.Center, + autoCancel: this.autoCancel ?? false, + cancel: () => { + this.onCancel && this.onCancel() + } + }) + if (this.controller) { + this.controller.open = () => { + this.dialogController?.open() + } + this.controller.close = () => { + this.dialogController?.close() + } + } + } + + @Builder + buildContent() { + Column() { + Text(this.title) + .fontSize(13) + .textAlign(TextAlign.Center) + .width('60%') + .maxLines(2) + .ellipsisMode(EllipsisMode.END) + .textOverflow({ + overflow: TextOverflow.Ellipsis + }) + .visibility(this.title ? Visibility.Visible : Visibility.None) + List({ space: 20, initialIndex: 0 }) { + ForEach(this.values ?? [], (item: string, index: number) => { + ListItem() { + Text(item) + .width('100%') + .fontSize(16) + .textAlign(TextAlign.Center) + .onClick(() => { + this.onSelected(index, item) + this.dialogController?.close() + }) + } + }, (item: string) => item) + } + .listDirection(Axis.Vertical) // 排列方向 + .scrollBar(BarState.Off) + .friction(0.6) + .divider({ strokeWidth: 1, color: 0xEEEEEE, startMargin: 20, endMargin: 20 }) // 每行之间的分界线 + .edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring + .width('100%') + .height(this.values === undefined ? '20%' : this.values?.length < 8 ? `${this.values?.length / 16 * 100}%` : '50%') + .margin({ top: 20 }) + }.padding({ top: 20, bottom: 20, left: 20, right: 20 }) + } +} \ No newline at end of file diff --git a/src/main/ets/http/HttpHelper.ts b/src/main/ets/http/HttpHelper.ts new file mode 100644 index 0000000..688830e --- /dev/null +++ b/src/main/ets/http/HttpHelper.ts @@ -0,0 +1,189 @@ +import { ArrayList, HashMap } from '@kit.ArkTS'; +import http from '@ohos.net.http'; + + +export class HttpHelper { + private static instance: HttpHelper | null = null + + // 单例模式 + static get() { + // 判断系统是否已经有单例了 + if (HttpHelper.instance === null) { + HttpHelper.instance = new HttpHelper() + } + return HttpHelper.instance + } + + //请求中队列 + private httpHandlerList = new HashMap(); + // 并发白名单,这个名单里面的api,重复请求不会取消 + private concurrentList = new ArrayList(); + + constructor() { + this.httpHandlerList = new HashMap(); + this.concurrentList.clear() + } + + /** + * 添加并发白名单 + * @param apiNo + */ + public addConcurrent(apiNo: string) { + if (this.concurrentList.getIndexOf(apiNo) === -1) { + this.concurrentList.add(apiNo) + } + } + + public removeConcurrent(apiNo: string) { + if (this.concurrentList.getIndexOf(apiNo) !== -1) { + this.concurrentList.remove(apiNo) + } + } + + /** + * postJson请求 + * @param url url地址 + * @param data 请求参数 + * @param headers + * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 + * @returns + */ + public post(url: string, data: string | undefined, headers?: Object, apiNo?: string): Promise { + + return new Promise((resolve, reject) => { + + if (this.concurrentList.getIndexOf(apiNo ?? url) === -1 && this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.get(apiNo ?? url).destroy() + this.httpHandlerList.remove(apiNo ?? url) + } + let httpRequest = http.createHttp(); + + if (this.concurrentList.getIndexOf(apiNo ?? url) === -1) { + this.httpHandlerList.set(apiNo ?? url, httpRequest) + } + + const header = { + "Content-Type": "application/json", + "Accept": "application/json", + ...headers + } + console.log('>>>>>', '接口请求', JSON.stringify(header)) + console.log('>>>>>', '接口请求', data) + console.log('>>>>>', '接口请求', url) + + httpRequest.request(url, { + method: http.RequestMethod.POST, + connectTimeout: 60000, + readTimeout: 60000, + header: header, + extraData: data + }) + .then((data: http.HttpResponse) => { + console.info('=====>' + 'Result:' + data.result as string); + console.info('=====>' + 'code:' + data.responseCode); + // console.info('=====>' + 'type:' + JSON.stringify(data.resultType)); + // console.info('=====>' + 'header:' + JSON.stringify(data.header)); + // console.info('=====>' + 'cookies:' + data.cookies); // 自API version 8开始支持cookie + // console.info('=====>' + 'header.content-Type:' + JSON.stringify(data.header)); + // console.info('=====>' + 'header.Status-Line:' + JSON.stringify(data.header)); + + if (this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.remove(apiNo ?? url) + } + if (data.responseCode === 200) { + resolve(JSON.parse(data.result as string) as T) + } else { + reject('服务异常') + } + }).catch((err: Error) => { + if (this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.remove(apiNo ?? url) + } + if (err.message === 'Failed writing received data to disk/application') { + reject('cancel') + } else + reject(err) + }); + }); + + } + + /** + * get请求 + * @param url url地址 + * @param data 请求参数 + * @param headers + * @param apiNo 请求标识,取消请求或者去重使用|考虑做自动重试使用 + * @returns + */ + public get(url: string, data: string | undefined, headers?: Object, apiNo?: string): Promise { + + return new Promise((resolve, reject) => { + + if (this.concurrentList.getIndexOf(apiNo ?? url) === -1 && this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.get(apiNo ?? url).destroy() + this.httpHandlerList.remove(apiNo ?? url) + } + let httpRequest = http.createHttp(); + + if (this.concurrentList.getIndexOf(apiNo ?? url) === -1) { + this.httpHandlerList.set(apiNo ?? url, httpRequest) + } + + const header = { + // "Content-Type": "application/json", + // "Accept": "application/json", + ...headers + } + // console.log('>>>>>', '接口请求', JSON.stringify(header)) + // console.log('>>>>>', '接口请求', data) + // console.log('>>>>>', '接口请求', url) + + if (data) { + url = `${url}?` + const json = JSON.parse(data) + for (let jsonKey in json) { + const value = json[jsonKey] + if (value) { + url = `${url}${jsonKey}=${json[jsonKey]}&` + } + } + url = url.slice(0, url.length - 1) + } + + httpRequest.request(url, { + method: http.RequestMethod.GET, + connectTimeout: 60000, + readTimeout: 60000, + header: header, + }) + .then((data: http.HttpResponse) => { + console.info('=====>' + 'Result:' + data.result as string); + console.info('=====>' + 'code:' + data.responseCode); + // console.info('=====>' + 'type:' + JSON.stringify(data.resultType)); + // console.info('=====>' + 'header:' + JSON.stringify(data.header)); + // console.info('=====>' + 'cookies:' + data.cookies); // 自API version 8开始支持cookie + // console.info('=====>' + 'header.content-Type:' + JSON.stringify(data.header)); + // console.info('=====>' + 'header.Status-Line:' + JSON.stringify(data.header)); + + if (this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.remove(apiNo ?? url) + } + if (data.responseCode === 200) { + resolve(JSON.parse(data.result as string) as T) + } else { + reject('服务异常') + } + }).catch((err: Error) => { + if (this.httpHandlerList.hasKey(apiNo ?? url)) { + this.httpHandlerList.remove(apiNo ?? url) + } + if (err.message === 'Failed writing received data to disk/application') { + reject('cancel') + } else + reject(err) + }); + }); + + } +} \ No newline at end of file diff --git a/src/main/ets/pages/XWebview.ets b/src/main/ets/pages/XWebview.ets new file mode 100644 index 0000000..499c248 --- /dev/null +++ b/src/main/ets/pages/XWebview.ets @@ -0,0 +1,233 @@ +import web_webview from '@ohos.web.webview'; +import { router } from '@kit.ArkUI'; +import { XDialogController } from '../dialog/XDialogController'; +import { XDialogList } from '../dialog/XDialogList'; +import { picker } from '@kit.CoreFileKit'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { ToolsHelper } from '../utils/ToolsHelper'; +import { XWebParams } from '../utils/XWebHelper'; + +@Entry({ routeName: 'XWebview' }) +@Preview +@Component +export struct XWebview { + // 手机号 + @State url: string = (router.getParams() as XWebParams).url + @State title?: string = (router.getParams() as XWebParams).title + @State errorInfo: string | null = null + @State progress: number = 0 + controller: web_webview.WebviewController = new web_webview.WebviewController(); + dialogController: XDialogController = {} as XDialogController + + aboutToAppear(): void { + } + + aboutToDisappear(): void { + } + + onBackPress(): boolean | void { + if (this.controller.accessBackward()) { + this.controller.backward() + } + return true + } + + build() { + Column() { + + Row() { + Row() { + Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) { + Image($r('sys.media.ohos_ic_back')) + .width(26).height(26) + }.onClick(() => { + this.onBackPress() + }) + + Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) { + Image($r('sys.media.ohos_ic_public_close')) + .width(26).height(26) + }.margin({ left: 12 }) + .onClick(() => { + router.back() + }) + }.width(65) + + Text(this.title) + .maxLines(1) + .fontColor('#222222') + .fontSize(18) + .textAlign(TextAlign.Center) + .width('50%') + .ellipsisMode(EllipsisMode.END) + .textOverflow({ + overflow: TextOverflow.Ellipsis + }) + + Button({ buttonStyle: ButtonStyleMode.TEXTUAL }) { + Image($r('sys.media.ohos_ic_public_more')) + .width(26).height(26) + }.width(65) + .onClick(() => { + if (this.dialogController != null) { + this.dialogController.open() + } + }) + } + .width('100%') + .height(45) + .justifyContent(FlexAlign.SpaceBetween) + .padding({ left: 15, right: 15 }) + + Divider().height(2).color(0xCCCCCC) + Progress({ value: this.progress, type: ProgressType.Linear }) + .visibility(this.progress > 95 || this.progress == 0 ? Visibility.None : Visibility.Visible) + .width('100%') + + Web({ src: this.url, controller: this.controller }) + .width('100%') + .height('100%') + .visibility(this.errorInfo == null ? Visibility.Visible : Visibility.None) + .mixedMode(MixedMode.All)//允许加载HTTP和HTTPS混合内容 + .zoomAccess(false)//不支持手势进行缩放 + .mediaPlayGestureAccess(false)//有声视频播放不需要用户手动点击 + .cacheMode(CacheMode.Default)//设置缓存模式 + .onConfirm((event) => { // 自定义Confirm弹窗 + if (event) { + console.log("event.url:" + event.url) + console.log("event.message:" + event.message) + AlertDialog.show({ + title: '提示', + message: event.message, + primaryButton: { + value: '取消', + action: () => { + event.result.handleCancel() + } + }, + secondaryButton: { + value: '确定', + action: () => { + event.result.handleConfirm() + } + }, + cancel: () => { + event.result.handleCancel() + } + }) + } + return true + }) + .onAlert((event) => { // 自定义Alert弹窗 + if (event) { + console.log("event.url:" + event.url) + console.log("event.message:" + event.message) + AlertDialog.show({ + title: '提示', + message: event.message, + secondaryButton: { + value: '确定', + action: () => { + event.result.handleConfirm() + } + } + }) + } + return true + }) + .onDownloadStart((event) => { // 下载文件 + if (event) { + console.log('url:' + event.url) + console.log('userAgent:' + event.userAgent) + console.log('contentDisposition:' + event.contentDisposition) + console.log('contentLength:' + event.contentLength) + console.log('mimetype:' + event.mimetype) + } + }) + .onErrorReceive((event) => { // 加载失败 + if (this.progress > 65) return + if (event) { + this.errorInfo = `错误码:${event.error.getErrorCode()}\n${event.error.getErrorInfo()}` + } else { + this.errorInfo = '错误码:-1\n未知错误' + } + }) + .onHttpErrorReceive((event) => { // 加载失败 + if (this.progress > 65) return + if (event) { + this.errorInfo = `错误码:${event.response.getResponseCode()}\n${event.response.getReasonMessage()}` + } else { + this.errorInfo = '错误码:-1\n未知错误' + } + }) + .onProgressChange((event) => { // 加载进度 + if (event) { + console.log('newProgress:' + event.newProgress) + this.progress = event.newProgress + } + }) + .onTitleReceive((event) => { + // 如果没有传输title,则从H5获取title赋值 + if (event && !this.title) { + this.title = event.title + } + }) + .onShowFileSelector((event) => { // 选择文件 + console.log('MyFileUploader onShowFileSelector invoked') + const documentSelectOptions = new picker.DocumentSelectOptions(); + let uri: string | null = null; + const documentViewPicker = new picker.DocumentViewPicker(); + documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => { + uri = documentSelectResult[0]; + console.info('documentViewPicker.select to file succeed and uri is:' + uri); + if (event) { + event.result.handleFileList([uri]); + } + }).catch((err: BusinessError) => { + if (event) { + event.result.handleFileList([]) + } + ToolsHelper.showMessage(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`) + console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`); + }) + return true + }) + Column() { + Text(this.errorInfo) + Button('点击重试') + .onClick(() => { + this.controller.refresh() + this.errorInfo = null + this.progress = 0 + }) + .margin({ top: 30 }) + } + .visibility(this.errorInfo == null ? Visibility.None : Visibility.Visible) + .width('100%') + .height('100%') + .justifyContent(FlexAlign.Center) + .padding({ bottom: 80 }) + + XDialogList({ + // 控制器 + controller: this.dialogController, + // 标题(可选) + title: '选择您的操作', + // 选择内容列表 + values: ['刷新', '浏览器打开', '分享', '复制地址'], + // 用户选择事件 + onSelected: (index: number, value: string) => { + ToolsHelper.showMessage(`用户选择了第${index}个,内容为:${value}`) + }, + // 用户取消事件 + onCancel: () => { + ToolsHelper.showMessage('用户取消操作') + }, + // 是否可取消(点击空白处,或者物理返回键) + autoCancel: true + }) + + }.width('100%').height('100%') + } +} + diff --git a/src/main/ets/utils/AppStorageHelper.ets b/src/main/ets/utils/AppStorageHelper.ets new file mode 100644 index 0000000..fe59065 --- /dev/null +++ b/src/main/ets/utils/AppStorageHelper.ets @@ -0,0 +1,22 @@ +/** + * 永久化存储,存储在本地文件 + */ +export class AppStorageHelper { + /** + * 缓存string + * @param key + * @param value + */ + public static save(key: string, value: string|undefined) { + PersistentStorage.persistProp(key, value) + } + + /** + * 获取已缓存的string + * @param key + * @returns + */ + public static get(key: string):string|undefined { + return AppStorage.get(key) + } +} \ No newline at end of file diff --git a/src/main/ets/utils/PreferencesHelper.ets b/src/main/ets/utils/PreferencesHelper.ets new file mode 100644 index 0000000..c39d5bd --- /dev/null +++ b/src/main/ets/utils/PreferencesHelper.ets @@ -0,0 +1,23 @@ +import preferences from '@ohos.data.preferences'; + +export class PreferencesHelper { + public static async put(key: string, value: preferences.ValueType|undefined) { + + const pref = await preferences.getPreferences(getContext(), 'PreferencesHelper') + // 写入数据 + await pref.put(key, value) + // 刷盘 + await pref.flush() + } + + public static async get(key: string): Promise { + return new Promise(async (resolve, reject) => { + + const pref = await preferences.getPreferences(getContext(), 'PreferencesHelper') + pref.get(key, undefined).then((r1: preferences.ValueType) => { + resolve(r1) + }) + }) + + } +} \ No newline at end of file diff --git a/src/main/ets/utils/ToolsHelper.ets b/src/main/ets/utils/ToolsHelper.ets new file mode 100644 index 0000000..de0463b --- /dev/null +++ b/src/main/ets/utils/ToolsHelper.ets @@ -0,0 +1,200 @@ +import promptAction from '@ohos.promptAction'; +import { Resource } from 'GlobalResource'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { HashMap } from '@kit.ArkTS'; + +export interface Btn { + text?: string | Resource; + color?: string | Resource; + onClick: () => void +} + +export interface AlertOptions { + title?: string + msg?: string + action: Btn +} + +export interface ConfirmOptions { + title?: string + msg?: string + confirm: Btn + cancel: Btn +} + +export interface ListOptions { + title?: string + cancel?: Btn + values: Array + onSelected: (index: number, value: T) => void + onError?: (msg: string) => void +} + +interface ListItem { + content: string +} + +@Builder +function customDialogBuilder(option: ListOptions, dialogId: number) { + + Column() { + Text(option.title) + .fontSize(13) + .textAlign(TextAlign.Center) + .width('60%') + .maxLines(2) + .ellipsisMode(EllipsisMode.END) + .textOverflow({ + overflow: TextOverflow.Ellipsis + }) + .visibility(option.title ? Visibility.Visible : Visibility.None) + List({ space: 20, initialIndex: 0 }) { + ForEach(option.values, (item: T, index: number) => { + ListItem() { + Text(typeof item === "string" ? item : (item as ListItem).content) + .width('100%') + .fontSize(16) + .textAlign(TextAlign.Center) + .onClick(() => { + if (ToolsHelper.mapDialog.get(dialogId)) { + promptAction.closeCustomDialog(ToolsHelper.mapDialog.get(dialogId)) + ToolsHelper.mapDialog.remove(dialogId) + } + option.onSelected(index, item) + }) + } + }, (item: string) => item) + } + .listDirection(Axis.Vertical) // 排列方向 + .scrollBar(BarState.Off) + .friction(0.6) + .divider({ strokeWidth: 1, color: 0xEEEEEE, startMargin: 20, endMargin: 20 }) // 每行之间的分界线 + .edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring + .width('100%') + .height(option.values.length < 8 ? `${option.values.length / 16 * 100}%` : '50%') + .margin({ top: 20 }) + }.padding({ top: 20, bottom: 20, left: 20, right: 20 }) +} + +/** + * 常用方法 + */ +export class ToolsHelper { + /** + * 弹出Toast + * @param msg + */ + static showMessage(msg: string) { + console.info(msg); + promptAction.showToast({ + message: msg, + duration: 1500 + }); + } + + /**kio9 + * 弹出Alert弹窗 + * @param options + */ + static showAlertDialog(options: AlertOptions) { + try { + promptAction.showDialog({ + alignment: 1, + title: options.title, + message: options.msg, + buttons: [{ + text: options.action.text ?? "确定", + color: options.action.color ?? "#000000", + }] + }) + .then(() => { + options.action.onClick() + }) + .catch((err: Error) => { + ToolsHelper.showMessage(err.message) + }) + } catch (error) { + let message = (error as BusinessError).message + ToolsHelper.showMessage(message) + } + } + + /** + * 弹出Confirm弹窗 + * @param options + */ + static showConfirmDialog(options: ConfirmOptions) { + try { + promptAction.showDialog({ + alignment: 1, + title: options.title, + message: options.msg, + buttons: [{ + text: options.confirm.text ?? "确定", + color: options.confirm.color ?? "#000000", + }, { + text: options.cancel.text ?? "取消", + color: options.cancel.color ?? "#666666", + }] + }) + .then((data) => { + if (data.index === 0) { + options.confirm.onClick() + } else { + options.cancel.onClick() + } + }) + .catch((err: Error) => { + ToolsHelper.showMessage(err.message) + }) + } catch (error) { + let message = (error as BusinessError).message + ToolsHelper.showMessage(message) + } + } + + public static mapDialog = new HashMap() + + /** + * 弹出List弹窗 + * @param options values 如果是非string列表的话,需要存在content字段 + */ + static showListDialog(options: ListOptions, p: object) { + + let isSuccess: Array = [] + options.values.forEach((item, index) => { + if (typeof item !== 'string') { + if (!(item as ListItem).content) { + isSuccess.push(index) + } + } + }) + if (isSuccess.length > 0) { + options.onError && options.onError(`第(${isSuccess.join("、")})个数据中,没有content字段。`) + } else { + const dialogTag = new Date().getTime() + promptAction.openCustomDialog({ + alignment: 1, + builder: customDialogBuilder.bind(p, options, dialogTag) + }).then((dialogId: number) => { + ToolsHelper.mapDialog.set(dialogTag, dialogId) + }) + } + } + + /** + * 弹出自定义弹窗 + * @param alignment 弹窗在竖直方向上的对齐方式 + */ + static showCustomDialog(b: CustomBuilder, alignment?: DialogAlignment) { + const dialogTag = new Date().getTime() + promptAction.openCustomDialog({ + alignment: alignment ?? DialogAlignment.Center, + builder: b + }).then((dialogId: number) => { + ToolsHelper.mapDialog.set(dialogTag, dialogId) + }).catch((error: Error) => { + console.log('>>>>>', JSON.stringify(error)) + }) + } +} \ No newline at end of file diff --git a/src/main/ets/utils/ValidatorHelper.ets b/src/main/ets/utils/ValidatorHelper.ets new file mode 100644 index 0000000..d1e6f33 --- /dev/null +++ b/src/main/ets/utils/ValidatorHelper.ets @@ -0,0 +1,14 @@ +/** + * 常用正则验证 + */ +export class ValidatorHelper { + /** + * 是否为手机号 + * @param phone + * @returns + */ + public static isPhone(phone: string) { + let regexp: RegExp = new RegExp('^1[0-9]{10}$'); + return regexp.test(phone) + } +} \ No newline at end of file diff --git a/src/main/ets/utils/WindowHelper.ets b/src/main/ets/utils/WindowHelper.ets new file mode 100644 index 0000000..bd063e5 --- /dev/null +++ b/src/main/ets/utils/WindowHelper.ets @@ -0,0 +1,93 @@ +import { display, window } from '@kit.ArkUI'; +import { common } from '@kit.AbilityKit'; + + +export class WindowHelper { + /** + * 缓存窗体,关闭时需要 + * 同时只能出现一个窗口,所以只做一个缓存就可以 + */ + private static cacheWindow: window.Window | null = null; + + /** + * 根据参数创建窗口 + * @param options + * @returns + */ + static async open(options: WinOptions): Promise { + if (WindowHelper.cacheWindow) { + options.callBack && options.callBack(-1, '窗口已存在') + return + } + if (!options) { + options = new WinOptions(); + } + if (!options.name) { + options.name = 'window'; + } + if (options.windowType == undefined) { + options.windowType = window.WindowType.TYPE_DIALOG; + } + if (!options.bgColor) { + options.bgColor = '#33606266'; + } + try { + //创建窗口 + let windowClass = await window.createWindow({ + name: options.name, + windowType: options.windowType, + ctx: getContext() as common.UIAbilityContext + }); + //将窗口缓存 + WindowHelper.cacheWindow = windowClass; + await windowClass.setUIContent(options.router); + //获取屏幕四大角 + let d = display.getDefaultDisplaySync(); + //设置窗口大小 + await windowClass.resize(d.width, d.height); + // 设置窗口背景颜色 + windowClass.setWindowBackgroundColor(options.bgColor); + //显示窗口 + await windowClass.showWindow(); + } catch (exception) { + options.callBack && options.callBack(-1, '创建窗口失败,原因为:' + JSON.stringify(exception)) + } + } + + /** + * 关闭窗口 + * @returns + */ + static async close(): Promise { + if (WindowHelper.cacheWindow) { + await WindowHelper.cacheWindow.destroyWindow(); + WindowHelper.cacheWindow = null + } + } +} + +/** + * 窗口入参对象 + */ +class WinOptions { + /** + * 窗口名称 默认window + */ + name?: string; + /** + * 窗口类型 默认TYPE_DIALOG + */ + windowType?: window.WindowType; + /** + *窗口要显示的路由 如:pages/Welcome需要在main_pages.json中声明 + */ + router: string = ''; + /** + * 窗口背景颜色,默认#33606266 + */ + bgColor?: string; + /** + * 窗口创建回调函数 + */ + callBack?: (code: number, msg: string) => void; +} \ No newline at end of file diff --git a/src/main/ets/utils/XWebHelper.ets b/src/main/ets/utils/XWebHelper.ets new file mode 100644 index 0000000..b659fda --- /dev/null +++ b/src/main/ets/utils/XWebHelper.ets @@ -0,0 +1,31 @@ +import { router } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { ToolsHelper } from './ToolsHelper'; + +const XWebview = import('../pages/XWebview'); + + +export interface XWebParams { + url: string + title?: string +} + +export class XWebHelper { + /** + * 打开web页面,加载h5 + * @param params + */ + public static openWeb(params: XWebParams) { + + router.pushNamedRoute({ + name: 'XWebview', + params: params + }, router.RouterMode.Single).then(() => { + console.info('Succeeded in jumping to the XWebview page.') + + }).catch((err: BusinessError) => { + console.error(`Failed to jump to the second page.Code is ${err.code}, message is ${err.message}`) + ToolsHelper.showMessage(`Failed to jump to the second page.Code is ${err.code}, message is ${err.message}`) + }) + } +} \ No newline at end of file diff --git a/src/main/module.json5 b/src/main/module.json5 new file mode 100644 index 0000000..bbca727 --- /dev/null +++ b/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "basic", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] + } +} diff --git a/src/main/resources/base/element/string.json b/src/main/resources/base/element/string.json new file mode 100644 index 0000000..222adc4 --- /dev/null +++ b/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "permission_internet", + "value": "需要使用网络权限连接网络" + }, + { + "name": "permission_device", + "value": "需要获取设备唯一码" + } + ] +} diff --git a/src/main/resources/base/media/title_bar_back.png b/src/main/resources/base/media/title_bar_back.png new file mode 100644 index 0000000000000000000000000000000000000000..073a8452a1bd7ce7051b53991fe5dce0c7b1a206 GIT binary patch literal 922 zcmV;L17-Y)P)Px&R7pfZR9Hv7m`!LCK@`W|n{Ctzsr3U?Jm?`Rf`VZ;*{1anXbz24FSZ^K?&fo|drf%1 z`M)>s&3^`=1z#i*S>4&$c>(|~13(7=jGCr-6a8kXsv1JX2Z(r_bDj^cC<@rNealyv zs;X-daa;(|T`mF<7kq=olL$>G@_H)2QC6mcDxm@m@5TeH|kUW<-=L3||XZ3q z3K92}kDo%|MF2R$7<=0YXc^Yk)wRd6tmgo*+XJ*f2svgL#_L8wd6=&2n=Q*?0IZ|Q7wfQ6}lPf?VUUQ-_ct}@1M)YB8y&a*Mjx%3??KHttWC$c=LXJ=Hz^M5aJ3VhMl*oe`KJ+HE?ES761SM07*qoM6N<$f+7x{jQ{`u literal 0 HcmV?d00001 diff --git a/src/main/resources/en_US/element/string.json b/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000..222adc4 --- /dev/null +++ b/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "permission_internet", + "value": "需要使用网络权限连接网络" + }, + { + "name": "permission_device", + "value": "需要获取设备唯一码" + } + ] +} diff --git a/src/main/resources/zh_CN/element/string.json b/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..222adc4 --- /dev/null +++ b/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + }, + { + "name": "permission_internet", + "value": "需要使用网络权限连接网络" + }, + { + "name": "permission_device", + "value": "需要获取设备唯一码" + } + ] +} diff --git a/src/test/List.test.ets b/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/src/test/LocalUnit.test.ets b/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..ed22d4d --- /dev/null +++ b/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest',() => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file