├── AppScope ├── app.json5 └── resources │ └── base │ ├── element │ └── string.json │ └── media │ └── app_icon.png ├── README.md ├── build-profile.json5 ├── entry ├── build-profile.json5 ├── hvigorfile.ts ├── obfuscation-rules.txt ├── oh-package.json5 └── src │ ├── main │ ├── ets │ │ ├── common │ │ │ └── HdNav.ets │ │ ├── component │ │ │ ├── CodeInputView.ets │ │ │ └── CropAvatarView.ets │ │ ├── dialog │ │ │ ├── GlobalDialog.ets │ │ │ ├── LoadingDialog.ets │ │ │ ├── PrivacyPolicyDialog.ets │ │ │ ├── PrivacyPolicyDialogTwo.ets │ │ │ └── SelectRegionDialog.ets │ │ ├── entryability │ │ │ └── EntryAbility.ets │ │ ├── entrybackupability │ │ │ └── EntryBackupAbility.ets │ │ ├── model │ │ │ ├── Region.ets │ │ │ └── TabBarItem.ets │ │ ├── pages │ │ │ ├── ArktsUiPage.ets │ │ │ ├── CropAvatarPage.ets │ │ │ ├── DialogPage.ets │ │ │ ├── HomePage.ets │ │ │ ├── Main.ets │ │ │ ├── SelectMediaPage.ets │ │ │ ├── SelectRegionPage.ets │ │ │ └── WebViewPage.ets │ │ └── utils │ │ │ ├── AppConstant.ets │ │ │ ├── AppUtil.ets │ │ │ └── Logger.ets │ ├── module.json5 │ └── resources │ │ ├── base │ │ ├── element │ │ │ ├── color.json │ │ │ ├── float.json │ │ │ └── string.json │ │ ├── media │ │ │ ├── background.png │ │ │ ├── foreground.png │ │ │ ├── icon_album_add.png │ │ │ ├── icon_birthday_normal.png │ │ │ ├── icon_birthday_select.png │ │ │ ├── icon_delete_image.png │ │ │ ├── icon_discover_normal.png │ │ │ ├── icon_discover_select.png │ │ │ ├── icon_loading_3.png │ │ │ ├── icon_person_normal.png │ │ │ ├── icon_person_select.png │ │ │ ├── icon_title_back.png │ │ │ ├── layered_image.json │ │ │ └── startIcon.png │ │ └── profile │ │ │ ├── backup_config.json │ │ │ └── main_pages.json │ │ ├── en_US │ │ └── element │ │ │ └── string.json │ │ ├── rawfile │ │ └── pc.json │ │ └── zh_CN │ │ └── element │ │ └── string.json │ ├── mock │ └── mock-config.json5 │ ├── ohosTest │ ├── ets │ │ └── test │ │ │ ├── Ability.test.ets │ │ │ └── List.test.ets │ └── module.json5 │ └── test │ ├── List.test.ets │ └── LocalUnit.test.ets ├── hvigor └── hvigor-config.json5 ├── hvigorfile.ts ├── oh-package-lock.json5 ├── oh-package.json5 └── screenshot ├── SelectRegionPage.gif └── dialog ├── 加载中弹窗.gif ├── 自定义弹窗.png └── 警告弹窗.png /AppScope/app.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "cn.ansen.myapplication", 4 | "vendor": "example", 5 | "versionCode": 1000000, 6 | "versionName": "1.0.0", 7 | "icon": "$media:app_icon", 8 | "label": "$string:app_name" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /AppScope/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "app_name", 5 | "value": "鸿蒙工具箱" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AppScope/resources/base/media/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/AppScope/resources/base/media/app_icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 鸿蒙API使用案例的工具库,后续会陆续增加功能以及维护。 2 | 3 | ### 弹窗 4 | - 警告弹窗 5 | - 自定义弹窗 6 | - 加载中弹窗 7 | - 全局弹窗 8 | - 模拟弹窗 9 | 10 | ### 图片 11 | - 圆形头像裁剪 12 | - 从系统选择图片 13 | -------------------------------------------------------------------------------- /build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "signingConfigs": [ 4 | { 5 | "name": "default", 6 | "type": "HarmonyOS", 7 | "material": { 8 | "certpath": "/Users/anhui/.ohos/config/default_harmony_tools_NkJmhRuWgGo4daw7vk-KDY0bdS65MAQPkb0J0v91eMw=.cer", 9 | "storePassword": "0000001B01CC4C4A47A75C3C17A3925AD86041EFF3A373FD7B7BDB350542C38355B0E819304BB765C71D61", 10 | "keyAlias": "debugKey", 11 | "keyPassword": "0000001B178C0706DBDBED2931DAF993F408A64B93DE5788D459CA603B3D2DC0262115DA4E473EF41D4AFF", 12 | "profile": "/Users/anhui/.ohos/config/default_harmony_tools_NkJmhRuWgGo4daw7vk-KDY0bdS65MAQPkb0J0v91eMw=.p7b", 13 | "signAlg": "SHA256withECDSA", 14 | "storeFile": "/Users/anhui/.ohos/config/default_harmony_tools_NkJmhRuWgGo4daw7vk-KDY0bdS65MAQPkb0J0v91eMw=.p12" 15 | } 16 | } 17 | ], 18 | "products": [ 19 | { 20 | "name": "default", 21 | "signingConfig": "default", 22 | "compatibleSdkVersion": "5.0.0(12)", 23 | "runtimeOS": "HarmonyOS", 24 | } 25 | ], 26 | "buildModeSet": [ 27 | { 28 | "name": "debug", 29 | }, 30 | { 31 | "name": "release" 32 | } 33 | ] 34 | }, 35 | "modules": [ 36 | { 37 | "name": "entry", 38 | "srcPath": "./entry", 39 | "targets": [ 40 | { 41 | "name": "default", 42 | "applyToProducts": [ 43 | "default" 44 | ] 45 | } 46 | ] 47 | } 48 | ] 49 | } -------------------------------------------------------------------------------- /entry/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": "stageMode", 3 | "buildOption": { 4 | }, 5 | "buildOptionSet": [ 6 | { 7 | "name": "release", 8 | "arkOptions": { 9 | "obfuscation": { 10 | "ruleOptions": { 11 | "enable": true, 12 | "files": [ 13 | "./obfuscation-rules.txt" 14 | ] 15 | } 16 | } 17 | } 18 | }, 19 | ], 20 | "targets": [ 21 | { 22 | "name": "default" 23 | }, 24 | { 25 | "name": "ohosTest", 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /entry/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | import { hapTasks } from '@ohos/hvigor-ohos-plugin'; 2 | 3 | export default { 4 | system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ 5 | plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ 6 | } 7 | -------------------------------------------------------------------------------- /entry/obfuscation-rules.txt: -------------------------------------------------------------------------------- 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://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 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 -------------------------------------------------------------------------------- /entry/oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "name": "entry", 3 | "version": "1.0.0", 4 | "description": "Please describe the basic information.", 5 | "main": "", 6 | "author": "", 7 | "license": "", 8 | "dependencies": {} 9 | } 10 | 11 | -------------------------------------------------------------------------------- /entry/src/main/ets/common/HdNav.ets: -------------------------------------------------------------------------------- 1 | import { router } from '@kit.ArkUI' 2 | 3 | @Component 4 | export struct HdNav { 5 | @StorageProp('topHeight') 6 | topHeight: number = 0 7 | @Prop 8 | title: string|Resource = '' 9 | @Prop 10 | hasBorder: boolean = false 11 | 12 | @Prop showLeftIcon:boolean=true; 13 | 14 | @Prop leftIcon: ResourceStr = $r('app.media.icon_title_back') 15 | @Prop rightIcon: ResourceStr = $r('sys.media.ohos_ic_public_more') 16 | 17 | @BuilderParam titleBuilder: () => void 18 | 19 | @BuilderParam rightBuilder: () => void 20 | 21 | @Builder 22 | defaultMenu() { 23 | 24 | } 25 | 26 | build() { 27 | RelativeContainer() { 28 | if (this.showLeftIcon){ 29 | Image(this.leftIcon) 30 | .size({ width: 34, height: 36 }) 31 | .onClick(() => router.back()) 32 | .padding({ 33 | left:15,right:10,bottom:10,top:10 34 | }).alignRules({ 35 | center: { anchor: '__container__', align: VerticalAlign.Center } 36 | }) 37 | } 38 | 39 | Row() { 40 | if (this.title) { 41 | Text(this.title) 42 | .fontWeight(600) 43 | .textAlign(TextAlign.Center) 44 | .fontSize(18) 45 | .maxLines(1) 46 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 47 | } else if (this.titleBuilder) { 48 | this.titleBuilder() 49 | } 50 | }.alignRules({ 51 | middle: { anchor: '__container__', align: HorizontalAlign.Center }, 52 | center: { anchor: '__container__', align: VerticalAlign.Center } 53 | }) 54 | 55 | if (this.rightBuilder) { 56 | Stack(){ 57 | this.rightBuilder() 58 | }.padding(15).alignRules({ 59 | right: { anchor: '__container__', align: HorizontalAlign.End }, 60 | center: { anchor: '__container__', align: VerticalAlign.Center } 61 | }) 62 | } 63 | } 64 | .height(56 + this.topHeight) 65 | .width('100%') 66 | .border({ 67 | width: { bottom: this.hasBorder ? 0.5 : 0 }, 68 | color: '#f3f4f5' 69 | }).backgroundColor($r('app.color.white')) 70 | } 71 | } -------------------------------------------------------------------------------- /entry/src/main/ets/component/CodeInputView.ets: -------------------------------------------------------------------------------- 1 | @Preview 2 | @Component 3 | export struct CodeInputView { 4 | @State viewSize: number = 4 5 | 6 | inputResultCallback!: (result:string) => void 7 | 8 | @Link codeKids: Array //创建一个包含4个空字符串的数组,用于存储输入的数字 9 | @State showCaret: boolean = true 10 | private inputKey = "code_input" 11 | 12 | aboutToAppear() { 13 | if (this.codeKids == null) { 14 | this.codeKids = new Array(this.viewSize).fill(''); 15 | } 16 | } 17 | 18 | build() { 19 | Stack() { 20 | if (this.codeKids != null) { 21 | Row({ space: 10 }) { 22 | ForEach(this.codeKids, (item: string, index: number) => { 23 | Text(item) 24 | .backgroundColor("#CCFFFFFF") 25 | .height("100%") 26 | .layoutWeight(1) 27 | .fontSize(25) 28 | .textAlign(TextAlign.Center) 29 | .align(Alignment.Center) 30 | .borderRadius(15) 31 | .focusable(false) 32 | .defaultFocus(false) 33 | .focusOnTouch(false) 34 | .onClick(() => { 35 | focusControl.requestFocus(this.inputKey) 36 | }) 37 | }, (item: string) => item) 38 | }.height("100%").width("100%") 39 | 40 | TextInput() 41 | .maxLength(this.viewSize) 42 | .fontSize(25) 43 | .borderRadius(15) 44 | .type(InputType.Number) 45 | .key(this.inputKey) 46 | .onChange((value) => { 47 | let a = value.split('') 48 | this.codeKids.forEach((value, index) => { 49 | this.codeKids[index] = a[index] || '' 50 | }) 51 | if (a.length >= this.viewSize) { 52 | this.inputResultCallback(value) 53 | } 54 | this.showCaret = (a.length == 0) 55 | }) 56 | .copyOption(CopyOptions.None) 57 | .caretColor(this.showCaret ? Color.Black : Color.Transparent) 58 | .fontColor(Color.Transparent) 59 | .backgroundColor(Color.Transparent) 60 | //TODO 系统问题,如果背景色是透明的也没用,非透明可以 61 | // .stateStyles({ pressed: {.backgroundColor("跟背景一样的颜色(纯透明会黑色闪一下)")}}) 62 | .height("100%") 63 | .width("100%") 64 | } 65 | }.height(80).backgroundColor(Color.Gray) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /entry/src/main/ets/component/CropAvatarView.ets: -------------------------------------------------------------------------------- 1 | import { image } from '@kit.ImageKit'; 2 | import { picker } from '@kit.CoreFileKit'; 3 | import Matrix4 from '@ohos.matrix4' 4 | import fs from '@ohos.file.fs'; 5 | import { hilog } from '@kit.PerformanceAnalysisKit'; 6 | 7 | /** 8 | * 参考博客链接:https://developer.huawei.com/consumer/cn/blog/topic/03160245819168283 9 | * 参考第三方库:https://ohpm.openharmony.cn/#/cn/detail/@xinyansoft%2Foh-crop 10 | */ 11 | @Component 12 | export struct CropAvatarView { 13 | @State private model: CropModel = new CropModel(); 14 | 15 | private settings: RenderingContextSettings = new RenderingContextSettings(true); 16 | private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); 17 | 18 | @State private matrix: object = Matrix4.identity() 19 | .translate({ x: 0, y: 0 }) 20 | .scale({ x: this.model.scale, y: this.model.scale}); 21 | 22 | /** 临时变量,无需手动赋值 **/ 23 | private tempScale = 1; 24 | /** 临时变量,无需手动赋值 **/ 25 | private startOffsetX: number = 0; 26 | /** 临时变量,无需手动赋值 **/ 27 | private startOffsetY: number = 0; 28 | 29 | build() { 30 | Stack() { 31 | Image(this.model.src) 32 | .width('100%') 33 | .height('100%') 34 | .alt(this.model.previewSource) 35 | .objectFit(ImageFit.Contain) 36 | .transform(this.matrix) 37 | .onComplete((msg) => { 38 | if (msg) { // 图片加载成功 39 | this.model.imageWidth = msg.width; 40 | this.model.imageHeight = msg.height; 41 | this.model.componentWidth = msg.componentWidth; 42 | this.model.componentHeight = msg.componentHeight; 43 | this.checkImageAdapt(); 44 | 45 | if (this.model.imageLoadEventListener != null && msg.loadingStatus == 1) { 46 | this.model.imageLoadEventListener.onImageLoaded(msg); 47 | } 48 | } 49 | }) 50 | .onError((error) => { 51 | if (this.model.imageLoadEventListener != null) { 52 | this.model.imageLoadEventListener.onImageLoadError(error); 53 | } 54 | }) 55 | Canvas(this.context) 56 | .width('100%') 57 | .height('100%') 58 | .backgroundColor(Color.Transparent) 59 | .onReady(() => { 60 | if (this.context == null) { 61 | return 62 | } 63 | 64 | let height = this.context.height 65 | let width = this.context.width 66 | this.context.fillStyle= this.model.maskColor; 67 | this.context.fillRect(0, 0, width, height) 68 | 69 | // 计算圆形的中心点和半径 70 | let centerX = width / 2; 71 | let centerY = height / 2; 72 | let minDimension = Math.min(width, height); 73 | let frameRadiusInVp = (minDimension - px2vp(this.model.frameWidth)) / 2; // 减去边框宽度 74 | 75 | // 把中间的取景框透出来 76 | this.context.globalCompositeOperation = 'destination-out' 77 | this.context.fillStyle = 'white' 78 | let frameWidthInVp = px2vp(this.model.frameWidth); 79 | let frameHeightInVp = px2vp(this.model.getFrameHeight()); 80 | let x = (width - px2vp(this.model.frameWidth)) / 2; 81 | let y = (height - px2vp(this.model.getFrameHeight())) / 2; 82 | // this.context.fillRect(x, y, frameWidthInVp, frameHeightInVp) 83 | console.info(`width:${width}`) 84 | console.info(`height:${height}`) 85 | console.info(`x:${x}`) 86 | console.info(`y:${y}`) 87 | console.info(`this.model.frameWidth:${this.model.frameWidth}`) 88 | console.info(`this.model.getFrameHeight():${this.model.getFrameHeight()}`) 89 | console.info(`frameWidthInVp:${frameWidthInVp}`) 90 | console.info(`frameHeightInVp:${frameHeightInVp}`) 91 | console.info(`frameRadiusInVp:${frameRadiusInVp}`) 92 | this.context.beginPath(); 93 | this.context.arc(centerX, centerY, px2vp(this.model.frameWidth/2), 0, 2 * Math.PI); 94 | this.context.fill(); 95 | 96 | // 设置综合操作模式为源覆盖,以便在现有图形上添加新的图形 97 | this.context.globalCompositeOperation = 'source-over'; 98 | 99 | // 设置描边颜色 100 | this.context.strokeStyle = this.model.strokeColor; 101 | 102 | // 计算圆形的半径,这里我们取正方形边框的较短边的一半作为半径 103 | let radius = Math.min(frameWidthInVp, frameHeightInVp) / 2; 104 | 105 | // 开始绘制路径 106 | this.context.beginPath(); 107 | 108 | // 使用 arc 方法绘制圆形 109 | this.context.arc(centerX, centerY, radius, 0, 2 * Math.PI); 110 | 111 | // 关闭路径 112 | this.context.closePath(); 113 | 114 | // 描绘圆形边框 115 | this.context.lineWidth = 1; // 边框宽度 116 | this.context.stroke(); 117 | 118 | }) 119 | .enabled(false) 120 | } 121 | .clip(true) 122 | .width('100%') 123 | .height('100%') 124 | .backgroundColor("#00000080") 125 | .priorityGesture( 126 | TapGesture({ count: 2, fingers: 1 }) 127 | .onAction((event:GestureEvent) => { 128 | if(!event){ 129 | return 130 | } 131 | if (this.model.zoomEnabled) { 132 | if (this.model.scale != 1) { 133 | this.model.scale = 1; 134 | this.model.reset(); 135 | this.updateMatrix(); 136 | } else { 137 | this.zoomTo(2); 138 | } 139 | } 140 | 141 | this.checkImageAdapt(); 142 | }) 143 | ) 144 | .gesture( 145 | GestureGroup(GestureMode.Parallel, 146 | // 拖动手势 147 | PanGesture({}) 148 | .onActionStart(() => { 149 | hilog.info(0, "CropView", "Pan gesture start"); 150 | this.startOffsetX = this.model.offsetX; 151 | this.startOffsetY = this.model.offsetY; 152 | }) 153 | .onActionUpdate((event:GestureEvent) => { 154 | hilog.info(0, "CropView", `Pan gesture update: ${JSON.stringify(event)}`); 155 | if (event) { 156 | if (this.model.panEnabled) { 157 | let distanceX: number = this.startOffsetX + vp2px(event.offsetX) / this.model.scale; 158 | let distanceY: number = this.startOffsetY + vp2px(event.offsetY) / this.model.scale; 159 | this.model.offsetX = distanceX; 160 | this.model.offsetY = distanceY; 161 | this.updateMatrix() 162 | } 163 | } 164 | }) 165 | .onActionEnd(() => { 166 | hilog.info(0, "CropView", "Pan gesture end"); 167 | this.checkImageAdapt(); 168 | }), 169 | 170 | // 缩放手势处理 171 | PinchGesture({ fingers: 2 }) 172 | .onActionStart(() => { 173 | this.tempScale = this.model.scale 174 | }) 175 | .onActionUpdate((event) => { 176 | if (event) { 177 | if (!this.model.zoomEnabled) return; 178 | this.zoomTo(this.tempScale * event.scale); 179 | } 180 | }) 181 | .onActionEnd(() => { 182 | this.checkImageAdapt(); 183 | }) 184 | ) 185 | ) 186 | } 187 | 188 | /** 189 | * 检查手势操作后,图片是否填满取景框,没填满则进行调整 190 | */ 191 | private checkImageAdapt() { 192 | let offsetX = this.model.offsetX; 193 | let offsetY = this.model.offsetY; 194 | let scale = this.model.scale; 195 | hilog.info(0, "CropView", `offsetX: ${offsetX}, offsetY: ${offsetY}, scale: ${scale}`) 196 | 197 | // 图片适配控件的时候也进行了缩放,计算出这个缩放比例 198 | let widthScale = this.model.componentWidth / this.model.imageWidth; 199 | let heightScale = this.model.componentHeight / this.model.imageHeight; 200 | let adaptScale = Math.min(widthScale, heightScale); 201 | hilog.info(0, "CropView", `Image scale ${adaptScale} while attaching the component[${this.model.componentWidth}, ${this.model.componentHeight}]`) 202 | 203 | // 经过两次缩放(适配控件、手势)后,图片的实际显示大小 204 | let showWidth = this.model.imageWidth * adaptScale * this.model.scale; 205 | let showHeight = this.model.imageHeight * adaptScale * this.model.scale; 206 | let imageX = (this.model.componentWidth - showWidth) / 2; 207 | let imageY = (this.model.componentHeight - showHeight) / 2; 208 | hilog.info(0, "CropView", `Image left top is (${imageX}, ${imageY})`) 209 | 210 | // 取景框的左上角坐标 211 | let frameX = (this.model.componentWidth - this.model.frameWidth) / 2; 212 | let frameY = (this.model.componentHeight - this.model.getFrameHeight()) / 2; 213 | 214 | // 图片左上角坐标 215 | let showX = imageX + offsetX * scale; 216 | let showY = imageY + offsetY * scale; 217 | hilog.info(0, "CropView", `Image show at (${showX}, ${showY})`) 218 | 219 | if(this.model.frameWidth > showWidth || this.model.getFrameHeight() > showHeight) { // 图片缩放后,大小不足以填满取景框 220 | let xScale = this.model.frameWidth / showWidth; 221 | let yScale = this.model.getFrameHeight() / showHeight; 222 | let newScale = Math.max(xScale, yScale); 223 | this.model.scale = this.model.scale * newScale; 224 | showX *= newScale; 225 | showY *= newScale; 226 | } 227 | 228 | // 调整x轴方向位置,使图像填满取景框 229 | if(showX > frameX) { 230 | showX = frameX; 231 | } else if(showX + showWidth < frameX + this.model.frameWidth) { 232 | showX = frameX + this.model.frameWidth - showWidth; 233 | } 234 | // 调整y轴方向位置,使图像填满取景框 235 | if(showY > frameY) { 236 | showY = frameY; 237 | } else if(showY + showHeight < frameY + this.model.getFrameHeight()) { 238 | showY = frameY + this.model.getFrameHeight() - showHeight; 239 | } 240 | this.model.offsetX = (showX - imageX) / scale; 241 | this.model.offsetY = (showY - imageY) / scale; 242 | this.updateMatrix(); 243 | } 244 | 245 | public zoomTo(scale: number): void { 246 | this.model.scale = scale; 247 | this.updateMatrix(); 248 | } 249 | 250 | public updateMatrix(): void { 251 | this.matrix = Matrix4.identity() 252 | .translate({ x: this.model.offsetX, y: this.model.offsetY }) 253 | .scale({ x: this.model.scale, y: this.model.scale }) 254 | } 255 | } 256 | 257 | interface ImageLoadedEvent { 258 | width: number; 259 | height: number; 260 | componentWidth: number; 261 | componentHeight: number; 262 | loadingStatus: number; 263 | contentWidth: number; 264 | contentHeight: number; 265 | contentOffsetX: number; 266 | contentOffsetY: number; 267 | } 268 | 269 | export interface ImageLoadEventListener { 270 | 271 | onImageLoaded(msg: ImageLoadedEvent): void; 272 | 273 | onImageLoadError(error: ImageError): void; 274 | } 275 | 276 | export class CropModel { 277 | /** 278 | * 图片uri 279 | * 类型判断太麻烦了,先只支持string,其他形式的需要先转换成路径 280 | */ 281 | src: string = ''; 282 | /** 283 | * 图片预览 284 | */ 285 | previewSource: string | Resource = ''; 286 | /** 287 | * 是否可以拖动 288 | */ 289 | panEnabled: boolean = true; 290 | /** 291 | * 是否可以缩放 292 | */ 293 | zoomEnabled: boolean = true; 294 | /** 295 | * 取景框宽度 296 | */ 297 | frameWidth = 1000; 298 | /** 299 | * 取景框宽高比 300 | */ 301 | frameRatio = 1; 302 | /** 303 | * 遮罩颜色 304 | */ 305 | maskColor: string = '#AA000000'; 306 | /** 307 | * 取景框边框颜色 308 | */ 309 | strokeColor: string = '#FFFFFF'; 310 | /** 311 | * 图片加载监听 312 | */ 313 | imageLoadEventListener: ImageLoadEventListener | null = null; 314 | 315 | /// 以下变量不要手动赋值 /// 316 | /** 317 | * 图片宽度,加载完成之后才会赋值 318 | */ 319 | imageWidth: number = 0; 320 | /** 321 | * 图片高度,加载完成之后才会赋值 322 | */ 323 | imageHeight: number = 0; 324 | /** 325 | * 控件宽度 326 | */ 327 | componentWidth: number = 0; 328 | /** 329 | * 控件高度 330 | */ 331 | componentHeight: number = 0; 332 | /** 333 | * 手势缩放比例 334 | * 图片经过了两重缩放,一是适配控件的时候进行了缩放,二是手势操作的时候进行了缩放 335 | */ 336 | scale: number = 1; 337 | /** 338 | * x轴方向偏移量 339 | */ 340 | offsetX: number = 0; 341 | /** 342 | * y轴方向偏移量 343 | */ 344 | offsetY: number = 0; 345 | 346 | ///////////////////////////////////////////// 347 | 348 | public setImage(src: string, previewSource?: string | Resource): CropModel { 349 | this.src = src; 350 | if (!!previewSource) { 351 | this.previewSource = previewSource; 352 | } 353 | return this; 354 | } 355 | 356 | public setScale(scale: number): CropModel { 357 | this.scale = scale; 358 | return this; 359 | } 360 | 361 | public isPanEnabled(): boolean { 362 | return this.panEnabled; 363 | } 364 | 365 | public setPanEnabled(panEnabled: boolean): CropModel { 366 | this.panEnabled = panEnabled; 367 | return this; 368 | } 369 | 370 | public setZoomEnabled(zoomEnabled: boolean): CropModel { 371 | this.zoomEnabled = zoomEnabled; 372 | return this; 373 | } 374 | 375 | public setFrameWidth(frameWidth: number) : CropModel { 376 | this.frameWidth = frameWidth; 377 | return this; 378 | } 379 | 380 | public setFrameRatio(frameRatio: number) : CropModel { 381 | this.frameRatio = frameRatio; 382 | return this; 383 | } 384 | 385 | public setMaskColor(color: string) : CropModel { 386 | this.maskColor = color; 387 | return this; 388 | } 389 | 390 | public setStrokeColor(color: string) : CropModel { 391 | this.strokeColor = color; 392 | return this; 393 | } 394 | 395 | public setImageLoadEventListener(listener: ImageLoadEventListener) : CropModel { 396 | this.imageLoadEventListener = listener; 397 | return this; 398 | } 399 | 400 | public getScale(): number { 401 | return this.scale; 402 | } 403 | 404 | public isZoomEnabled(): boolean { 405 | return this.zoomEnabled; 406 | } 407 | 408 | public getImageWidth(): number { 409 | return this.imageWidth; 410 | } 411 | 412 | public getImageHeight(): number { 413 | return this.imageHeight; 414 | } 415 | 416 | public getFrameHeight() { 417 | return this.frameWidth / this.frameRatio; 418 | } 419 | 420 | public reset(): void { 421 | this.scale = 1; 422 | this.offsetX = 0; 423 | this.offsetY = 0; 424 | } 425 | 426 | public async crop(format: image.PixelMapFormat) : Promise { 427 | if(!this.src || this.src == '') { 428 | throw new Error('Please set src first'); 429 | } 430 | if(this.imageWidth == 0 || this.imageHeight == 0) { 431 | throw new Error('The image is not loaded'); 432 | } 433 | 434 | // 图片适配控件的时候也进行了缩放,计算出这个缩放比例 435 | let widthScale = this.componentWidth / this.imageWidth; 436 | let heightScale = this.componentHeight / this.imageHeight; 437 | let adaptScale = Math.min(widthScale, heightScale); 438 | 439 | // 经过两次缩放(适配控件、手势)后,图片的实际显示大小 440 | let totalScale = adaptScale * this.scale; 441 | let showWidth = this.imageWidth * totalScale; 442 | let showHeight = this.imageHeight * totalScale; 443 | let imageX = (this.componentWidth - showWidth) / 2; 444 | let imageY = (this.componentHeight - showHeight) / 2; 445 | 446 | // 取景框的左上角坐标 447 | let frameX = (this.componentWidth - this.frameWidth) / 2; 448 | let frameY = (this.componentHeight - this.getFrameHeight()) / 2; 449 | 450 | // 图片左上角坐标 451 | let showX = imageX + this.offsetX * this.scale; 452 | let showY = imageY + this.offsetY * this.scale; 453 | 454 | let x = (frameX - showX) / totalScale; 455 | let y = (frameY - showY) / totalScale; 456 | let file = fs.openSync(this.src, fs.OpenMode.READ_ONLY) 457 | let imageSource : image.ImageSource = image.createImageSource(file.fd); 458 | let decodingOptions : image.DecodingOptions = { 459 | editable: true, 460 | desiredPixelFormat: image.PixelMapFormat.BGRA_8888, 461 | } 462 | 463 | // 创建pixelMap 464 | let pm = await imageSource.createPixelMap(decodingOptions); 465 | let cp = await this.copyPixelMap(pm); 466 | pm.release(); 467 | let region: image.Region = { x: x, y: y, size: { width: this.frameWidth / totalScale, height: this.getFrameHeight() / totalScale } }; 468 | cp.cropSync(region); 469 | return cp; 470 | } 471 | 472 | async copyPixelMap(pm: PixelMap): Promise { 473 | const imageInfo: image.ImageInfo = await pm.getImageInfo(); 474 | const buffer: ArrayBuffer = new ArrayBuffer(pm.getPixelBytesNumber()); 475 | // TODO 知识点:通过readPixelsToBuffer实现PixelMap的深拷贝,其中readPixelsToBuffer输出为BGRA_8888 476 | await pm.readPixelsToBuffer(buffer); 477 | // TODO 知识点:readPixelsToBuffer输出为BGRA_8888,此处createPixelMap需转为RGBA_8888 478 | const opts: image.InitializationOptions = { 479 | editable: true, 480 | pixelFormat: image.PixelMapFormat.RGBA_8888, 481 | size: { height: imageInfo.size.height, width: imageInfo.size.width } 482 | }; 483 | return image.createPixelMap(buffer, opts); 484 | } 485 | } 486 | 487 | interface MyEvent { 488 | result: FileSelectorResult, 489 | fileSelector: FileSelectorParam 490 | } -------------------------------------------------------------------------------- /entry/src/main/ets/dialog/GlobalDialog.ets: -------------------------------------------------------------------------------- 1 | 2 | import { ComponentContent} from '@kit.ArkUI'; 3 | 4 | export class GlobalDialog { 5 | static contentNode:ComponentContent; 6 | 7 | //显示弹窗 8 | static show(context: UIContext,dialogParam: GlobalDialogParam){ 9 | //ComponentContent对象有三个参数 10 | //参数1:UI 上下文 11 | //参数2:使用 wrapBuilder 包装 buildGlobalDialogComponent 函数,这个函数用于构建对话框的实际内容 12 | //参数3:传递给对话框的参数,包含内容文本和按钮的回调函数 13 | GlobalDialog.contentNode = new ComponentContent(context, wrapBuilder(buildGlobalDialogComponent), dialogParam); 14 | 15 | const promptAction = context.getPromptAction()//通过 context 获取 promptAction,用于操作对话框显示 16 | 17 | //显示弹窗 18 | promptAction.openCustomDialog(GlobalDialog.contentNode,{ 19 | alignment: DialogAlignment.Center,//对话框在屏幕中央显示 20 | autoCancel: false,//点击弹窗外区域是否取消弹窗 21 | }); 22 | } 23 | 24 | //关闭弹窗 25 | static close(context: UIContext){ 26 | const promptAction = context.getPromptAction() 27 | promptAction.closeCustomDialog(GlobalDialog.contentNode) 28 | } 29 | } 30 | 31 | @Builder function buildGlobalDialogComponent(param: GlobalDialogParam){ 32 | Column() { 33 | Text(param.content).fontSize(17).fontColor("#181818").lineHeight(24).margin({ 34 | bottom:29,top:29,left:31,right:31 35 | }) 36 | 37 | Divider().color("#D8D8D8").height(0.5) 38 | RowSplit() { 39 | Text("取消").fontSize(17).fontColor("#181818").fontWeight(FontWeight.Bold).onClick(event=>{ 40 | param.onCancel(); 41 | }).textAlign(TextAlign.Center).padding({ 42 | top:15,bottom:15 43 | }).width('50%') 44 | 45 | Text("确定").fontSize(17).fontColor($r('app.color.mainColor')).fontWeight(FontWeight.Bold).onClick(event=>{ 46 | param.onConfirm(); 47 | }).textAlign(TextAlign.Center).padding({ 48 | top:15,bottom:15 49 | }).width('50%') 50 | } 51 | }.backgroundColor($r('app.color.white')).width('80%').borderRadius(12) 52 | } 53 | 54 | interface GlobalDialogParam { 55 | content:string;//弹窗显示内容 56 | onConfirm: () => void //确认按钮的回调函数 57 | onCancel: () => void // 取消按钮的回调函数 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /entry/src/main/ets/dialog/LoadingDialog.ets: -------------------------------------------------------------------------------- 1 | 2 | @CustomDialog 3 | export default struct LoadingDialog { 4 | controller: CustomDialogController 5 | 6 | private loadingText: string|Resource = "加载中..." 7 | @State angle:number = 10 8 | 9 | aboutToAppear(){ 10 | setTimeout(()=>{ 11 | this.angle = 1440 // 设定一个大的旋转角度,确保动画执行 12 | },100) 13 | } 14 | 15 | build() { 16 | Column(){ 17 | Image($r('app.media.icon_loading_3')) 18 | .width(70) 19 | .height(70) 20 | .rotate({angle:this.angle}) 21 | .animation({ 22 | duration: 5000, 23 | curve: Curve.Linear, 24 | delay: 0, 25 | iterations: -1, // 设置-1表示动画无限循环 26 | playMode: PlayMode.Normal 27 | }) 28 | 29 | 30 | Text(this.loadingText).fontSize(14).fontColor(0xffffff).margin({top:10}) 31 | }.backgroundColor(0x88000000).borderRadius(10).padding({ 32 | left:20,right:20,top:10,bottom:10 33 | }) 34 | } 35 | } -------------------------------------------------------------------------------- /entry/src/main/ets/dialog/PrivacyPolicyDialog.ets: -------------------------------------------------------------------------------- 1 | import { promptAction, router } from '@kit.ArkUI' 2 | import { AppConstant, Pages } from '../utils/AppConstant' 3 | import { AppUtil } from '../utils/AppUtil' 4 | import { logger } from '../utils/Logger' 5 | 6 | @CustomDialog 7 | export default struct PrivacyPolicyDialog{ 8 | controller: CustomDialogController 9 | 10 | cancel!: () => void 11 | confirm!: () => void 12 | 13 | build() { 14 | Column() { 15 | Text($r('app.string.simple_user_policy')).fontSize(18).fontColor($r('app.color.title_color')).margin({ top: 30, bottom: 19 }) 16 | 17 | Scroll(){ 18 | Text(){ 19 | Span($r('app.string.privacy_policy_start')) 20 | Span($r('app.string.user_agreement_two')).fontColor($r('app.color.mainColor')).onClick(() => { 21 | AppUtil.openWebUrl("/useragreement.html"); 22 | }) 23 | Span($r('app.string.and')) 24 | Span($r('app.string.privacy_policy_two')).fontColor($r('app.color.mainColor')).onClick(() => { 25 | AppUtil.openWebUrl("/privacypolicy.html"); 26 | }) 27 | Span($r('app.string.simple_privacy_policy')) 28 | }.fontSize(16).fontColor($r('app.color.body_color')).margin({ 29 | left:25, 30 | right:25 31 | }) 32 | }.height(120) 33 | 34 | Column(){ 35 | Button($r('app.string.disagree_privacy_policy')).onClick(() => { 36 | this.controller.close(); 37 | this.cancel(); 38 | }).fontColor($r('app.color.other_color')).fontSize(15).backgroundColor(Color.Transparent) 39 | 40 | Button($r('app.string.agree_privacy_policy')).onClick(() => { 41 | this.controller.close(); 42 | this.confirm(); 43 | }).fontColor($r('app.color.white')).fontSize(17) 44 | .linearGradient({ 45 | direction: GradientDirection.Right, colors:[[$r('app.color.start_main_color'),0.0],[$r('app.color.end_main_color'),1.0]] 46 | }).width('80%').margin({ 47 | top:15,bottom:21 48 | }).borderRadius(24) 49 | } 50 | } 51 | } 52 | 53 | 54 | 55 | 56 | } -------------------------------------------------------------------------------- /entry/src/main/ets/dialog/PrivacyPolicyDialogTwo.ets: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { AppUtil } from '../utils/AppUtil' 4 | 5 | @Component 6 | export default struct PrivacyPolicyDialogTwo{ 7 | cancel!: () => void 8 | confirm!: () => void 9 | 10 | build() { 11 | Stack(){ 12 | Column() { 13 | Text($r('app.string.simple_user_policy')).fontSize(18).fontColor($r('app.color.title_color')).margin({ top: 30, bottom: 19 }) 14 | 15 | Scroll(){ 16 | Text(){ 17 | Span($r('app.string.privacy_policy_start')) 18 | Span($r('app.string.user_agreement_two')).fontColor($r('app.color.mainColor')).onClick(() => { 19 | AppUtil.openWebUrl("/useragreement.html"); 20 | }) 21 | Span($r('app.string.and')) 22 | Span($r('app.string.privacy_policy_two')).fontColor($r('app.color.mainColor')).onClick(() => { 23 | AppUtil.openWebUrl("/privacypolicy.html"); 24 | }) 25 | Span($r('app.string.simple_privacy_policy')) 26 | }.fontSize(16).fontColor($r('app.color.body_color')).margin({ 27 | left:25, 28 | right:25 29 | }) 30 | }.height(120) 31 | 32 | Column(){ 33 | Button($r('app.string.disagree_privacy_policy')).onClick(() => { 34 | this.cancel(); 35 | }).fontColor($r('app.color.other_color')).fontSize(15).backgroundColor(Color.Transparent) 36 | 37 | Button($r('app.string.agree_privacy_policy')).onClick(() => { 38 | this.confirm(); 39 | }).fontColor($r('app.color.white')).fontSize(17) 40 | .linearGradient({ 41 | direction: GradientDirection.Right, colors:[[$r('app.color.start_main_color'),0.0],[$r('app.color.end_main_color'),1.0]] 42 | }).width('80%').margin({ 43 | top:15,bottom:21 44 | }).borderRadius(24) 45 | } 46 | }.backgroundColor($r('app.color.white')).borderRadius(13).width('80%') 47 | }.width('100%') 48 | .height('100%').backgroundColor("#4D000000")//黑色背景 透明度约为 30% 49 | } 50 | } -------------------------------------------------------------------------------- /entry/src/main/ets/dialog/SelectRegionDialog.ets: -------------------------------------------------------------------------------- 1 | 2 | import { AppUtil } from '../utils/AppUtil'; 3 | 4 | @CustomDialog 5 | export default struct SelectRegionDialog { 6 | controller: CustomDialogController 7 | 8 | map: Map = new Map();//省市数据 9 | 10 | @State selectProvince:string=''//选择的省份 11 | selectCity:string=''//选中的城市 12 | 13 | confirm!: (province:string,city:string) => void //确定按钮点击回调 14 | 15 | aboutToAppear(): void { 16 | this.initData(); 17 | } 18 | 19 | build() { 20 | Column(){ 21 | Row(){ 22 | Button("取消").onClick(event=>{ 23 | this.controller.close(); 24 | }) 25 | Blank().layoutWeight(1) 26 | Button("确定").onClick(event=>{ 27 | this.confirm(this.selectProvince,this.selectCity); 28 | this.controller.close(); 29 | }) 30 | }.margin({ 31 | left:15,right:15,top:30 32 | }) 33 | 34 | Row(){ 35 | TextPicker({ range: this.getProvinceStrList(), value: this.selectProvince }) 36 | .onChange((value: string|string[], index: number| number[]) => { 37 | this.selectProvince = Array.isArray(value) ? value[0] : value; 38 | 39 | //省份改变,默认选择第一个城市 40 | let cityList=this.map.get(this.selectProvince); 41 | if (cityList != null && cityList?.length>0){ 42 | this.selectCity = cityList[0]; 43 | } 44 | }).width('50%') 45 | .canLoop(false)//是否循环 46 | .textStyle({ 47 | color:'#888888' 48 | }).selectedTextStyle({ 49 | color:$r('app.color.title_color') 50 | }) 51 | 52 | TextPicker({ range: this.getCityStrList(), value: this.selectCity }) 53 | .onChange((value: string|string[], index: number| number[]) => { 54 | this.selectCity = Array.isArray(value) ? value[0] : value; 55 | }).width('50%').canLoop(false).textStyle({ 56 | color:'#888888' 57 | }).selectedTextStyle({ 58 | color:$r('app.color.title_color') 59 | }) 60 | }.margin({ 61 | top:30,bottom:30 62 | }) 63 | } 64 | } 65 | 66 | initData(){ 67 | let result=AppUtil.getRawFileContent(this,'pc.json'); 68 | let jsonObj: Record = JSON.parse(result); 69 | let newMap: Map = new Map(Object.entries(jsonObj)); 70 | 71 | newMap.forEach((value, key) => { 72 | let valueData = value as string[]; 73 | this.map.set(key,valueData); 74 | }) 75 | } 76 | 77 | getProvinceStrList(){ 78 | let keysArray: string[] = Array.from(this.map.keys()); 79 | return keysArray; 80 | } 81 | 82 | getCityStrList(){ 83 | let cityList=this.map.get(this.selectProvince); 84 | return cityList; 85 | } 86 | } -------------------------------------------------------------------------------- /entry/src/main/ets/entryability/EntryAbility.ets: -------------------------------------------------------------------------------- 1 | import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 2 | import { hilog } from '@kit.PerformanceAnalysisKit'; 3 | import { window } from '@kit.ArkUI'; 4 | 5 | export default class EntryAbility extends UIAbility { 6 | onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 7 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 8 | } 9 | 10 | onDestroy(): void { 11 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 12 | } 13 | 14 | onWindowStageCreate(windowStage: window.WindowStage): void { 15 | // Main window is created, set main page for this ability 16 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 17 | 18 | windowStage.loadContent('pages/Main', (err) => { 19 | if (err.code) { 20 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', 21 | JSON.stringify(err) ?? ''); 22 | return; 23 | } 24 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 25 | }); 26 | } 27 | 28 | onWindowStageDestroy(): void { 29 | // Main window is destroyed, release UI related resources 30 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 31 | } 32 | 33 | onForeground(): void { 34 | // Ability has brought to foreground 35 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 36 | } 37 | 38 | onBackground(): void { 39 | // Ability has back to background 40 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /entry/src/main/ets/entrybackupability/EntryBackupAbility.ets: -------------------------------------------------------------------------------- 1 | import { hilog } from '@kit.PerformanceAnalysisKit'; 2 | import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; 3 | 4 | export default class EntryBackupAbility extends BackupExtensionAbility { 5 | async onBackup() { 6 | hilog.info(0x0000, 'testTag', 'onBackup ok'); 7 | } 8 | 9 | async onRestore(bundleVersion: BundleVersion) { 10 | hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); 11 | } 12 | } -------------------------------------------------------------------------------- /entry/src/main/ets/model/Region.ets: -------------------------------------------------------------------------------- 1 | 2 | export class Region{ 3 | code=0; 4 | name='' 5 | provinceCode=0; 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/model/TabBarItem.ets: -------------------------------------------------------------------------------- 1 | 2 | export class TabBarItem{ 3 | icon: ResourceStr = '' 4 | selectIcon: ResourceStr = '' 5 | title: ResourceStr = '' 6 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/ArktsUiPage.ets: -------------------------------------------------------------------------------- 1 | import { CodeInputView } from '../component/CodeInputView' 2 | 3 | @Component 4 | export struct ArktsUiPage { 5 | @State codeKids: Array = new Array(4).fill(''); 6 | 7 | build() { 8 | Column() { 9 | Text("验证码输入框") 10 | CodeInputView({ 11 | codeKids:this.codeKids, 12 | inputResultCallback:this.inputResultCallback.bind(this) 13 | }).margin({ 14 | top:10 15 | }).width('90%').height(50) 16 | } 17 | .height('100%') 18 | .width('100%') 19 | .justifyContent(FlexAlign.Center); 20 | } 21 | 22 | inputResultCallback(result:string){ 23 | console.log("result:"+result) 24 | } 25 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/CropAvatarPage.ets: -------------------------------------------------------------------------------- 1 | import { picker } from '@kit.CoreFileKit'; 2 | import { CropAvatarView, CropModel } from '../component/CropAvatarView'; 3 | import { image } from '@kit.ImageKit'; 4 | import { HdNav } from '../common/HdNav'; 5 | 6 | /** 7 | * 参考博客:https://developer.huawei.com/consumer/cn/blog/topic/03160245819168283 8 | */ 9 | @Entry 10 | @Component 11 | export struct CropAvatarPage { 12 | @State pm: PixelMap | undefined = undefined; 13 | @State private model: CropModel = new CropModel(); 14 | 15 | build() { 16 | Column() { 17 | HdNav({ 18 | title:"裁剪圆形头像" 19 | }) 20 | 21 | CropAvatarView({ 22 | model: this.model, 23 | }).layoutWeight(1).width('100%') 24 | 25 | Row({space:20}){ 26 | Button('选择图片').onClick(()=>{ 27 | this.openPicker() 28 | }) 29 | 30 | Button('测试剪裁').onClick(async () => { 31 | try { 32 | this.pm = await this.model.crop(image.PixelMapFormat.RGBA_8888); 33 | } catch (e) { 34 | console.info(`e:${JSON.stringify(e)}`) 35 | } 36 | }) 37 | }.margin({ 38 | top:10,bottom:10 39 | }) 40 | Image(this.pm).width('300lpx').height('300lpx').borderRadius('150lpx') 41 | } 42 | .height('100%') 43 | .width('100%') 44 | } 45 | 46 | // 弹出图片选择器方法 47 | async openPicker() { 48 | try { 49 | // 设置图片选择器选项 50 | const photoSelectOptions = new picker.PhotoSelectOptions(); 51 | // 限制只能选择一张图片 52 | photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; 53 | photoSelectOptions.maxSelectNumber = 1; 54 | // 创建并实例化图片选择器 55 | const photoViewPicker = new picker.PhotoViewPicker(); 56 | // 选择图片并获取图片URI 57 | let uris: picker.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions); 58 | if (!uris || uris.photoUris.length === 0) return; 59 | // 获取选中图片的第一张URI 60 | let uri: string = uris.photoUris[0]; 61 | this.model.setImage(uri) 62 | .setFrameWidth(1000) 63 | .setFrameRatio(1); 64 | } catch (e) { 65 | console.error('openPicker', JSON.stringify(e)); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/DialogPage.ets: -------------------------------------------------------------------------------- 1 | import PrivacyPolicyDialog from '../dialog/PrivacyPolicyDialog'; 2 | import { AppUtil } from '../utils/AppUtil'; 3 | import LoadingDialog from '../dialog/LoadingDialog'; 4 | import { HdNav } from '../common/HdNav'; 5 | import { GlobalDialog } from '../dialog/GlobalDialog'; 6 | import PrivacyPolicyDialogTwo from '../dialog/PrivacyPolicyDialogTwo'; 7 | 8 | @Entry 9 | @Component 10 | struct DialogPage { 11 | //参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-methods-custom-dialog-box-0000001477981237-V2 12 | privacyPolicyDialog: CustomDialogController = new CustomDialogController({ 13 | builder: PrivacyPolicyDialog({ 14 | cancel:this.onCancel.bind(this), 15 | confirm:this.onAgree.bind(this) 16 | }), 17 | alignment: DialogAlignment.Default, // 可设置dialog的对齐方式,设定显示在底部或中间等,默认为底部显示 18 | cornerRadius:13, 19 | autoCancel:false 20 | }) 21 | 22 | loadingDialog: CustomDialogController = new CustomDialogController({ 23 | builder: LoadingDialog(), 24 | cornerRadius: 10, 25 | width: 110, 26 | autoCancel: true // 点击其他区域是否能取消 27 | }); 28 | 29 | @State isAgreePrivacyPolicy:boolean = false; 30 | 31 | build() { 32 | Column(){ 33 | HdNav({ 34 | title:"弹窗" 35 | }) 36 | 37 | RelativeContainer(){ 38 | Column({space:20}){ 39 | Button("警告弹窗").onClick(event=>{ 40 | this.showAlertDialog(); 41 | }) 42 | 43 | Button("自定义弹窗").onClick(event=>{ 44 | this.privacyPolicyDialog.open(); 45 | }) 46 | 47 | Button("加载中弹窗").onClick(event=>{ 48 | this.loadingDialog.open(); 49 | }) 50 | 51 | Button("全局弹窗,多组件复用").onClick(event=>{ 52 | GlobalDialog.show(this.getUIContext(),{ 53 | content:"您确定要删除这条记录吗?", 54 | onConfirm:()=>{ 55 | GlobalDialog.close(this.getUIContext())//关闭弹窗 56 | AppUtil.showToast("确定按钮点击"); 57 | }, 58 | onCancel:()=>{ 59 | GlobalDialog.close(this.getUIContext())//关闭弹窗 60 | AppUtil.showToast("取消按钮点击"); 61 | } 62 | }) 63 | }) 64 | 65 | //文章说明:https://mp.weixin.qq.com/s?__biz=MzIxODI4NzQ1Nw==&mid=2652474041&idx=1&sn=2d75e31dd16a6ac67abf4e7af4970c6a 66 | Button("模拟弹窗,点击隐私协议后,保证新页面不会被弹窗遮住").onClick(event=>{ 67 | this.isAgreePrivacyPolicy = !this.isAgreePrivacyPolicy; 68 | }) 69 | 70 | }.justifyContent(FlexAlign.Center).margin({ 71 | top:180 72 | }) 73 | 74 | if (this.isAgreePrivacyPolicy){ 75 | PrivacyPolicyDialogTwo({ 76 | cancel:()=>{ 77 | this.isAgreePrivacyPolicy=false 78 | AppUtil.showToast("拒绝隐私政策"); 79 | }, 80 | confirm:()=>{ 81 | this.isAgreePrivacyPolicy=false 82 | AppUtil.showToast("同意隐私政策"); 83 | }, 84 | }) 85 | } 86 | } 87 | 88 | 89 | }.height('100%') 90 | .width('100%') 91 | } 92 | 93 | //参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V2/ts-methods-alert-dialog-box-0000001478341185-V2 94 | showAlertDialog(){ 95 | AlertDialog.show({ 96 | title:"弹窗标题", 97 | message:"这是弹窗内容", 98 | autoCancel:true,//点击遮障层时,是否关闭弹窗。默认值:true 99 | alignment: DialogAlignment.Center,//弹窗在竖直方向上的对齐方式。默认值:DialogAlignment.Default 100 | primaryButton: { 101 | value: "取消", 102 | fontColor: '#181818', 103 | action: () => { 104 | AppUtil.showToast("点击了取消按钮"); 105 | } 106 | }, 107 | secondaryButton: { 108 | value: "确定", 109 | fontColor: $r('app.color.mainColor'), 110 | action: () => { 111 | AppUtil.showToast("点击了确定按钮"); 112 | } 113 | }, 114 | cornerRadius:12,//弹窗边框弧度 115 | width:'80%', //弹窗宽度 116 | cancel:()=>{ 117 | AppUtil.showToast("点击遮障层关闭dialog时的回调"); 118 | } 119 | }) 120 | } 121 | 122 | onCancel():void { 123 | AppUtil.showToast("拒绝隐私政策"); 124 | } 125 | 126 | onAgree():void {//同意隐私政策 127 | AppUtil.showToast("同意隐私政策"); 128 | } 129 | 130 | 131 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/HomePage.ets: -------------------------------------------------------------------------------- 1 | import { router } from '@kit.ArkUI' 2 | import { Pages } from '../utils/AppConstant' 3 | 4 | @Component 5 | export struct HomePage { 6 | build() { 7 | Column({space:10}) { 8 | Button("弹窗").onClick(event=>{ 9 | router.pushUrl({url: Pages.DialogPage}, router.RouterMode.Single) 10 | }) 11 | 12 | Button("选择图片/视频").onClick(event=>{ 13 | router.pushUrl({url: Pages.SelectMediaPage}, router.RouterMode.Single) 14 | }) 15 | 16 | Button("头像裁剪圆形遮罩").onClick(event=>{ 17 | router.pushUrl({url: Pages.CropAvatarPage}, router.RouterMode.Single) 18 | }) 19 | 20 | Button("头像裁剪圆形遮罩").onClick(event=>{ 21 | router.pushUrl({url: Pages.CropAvatarPage}, router.RouterMode.Single) 22 | }) 23 | 24 | Button("省市区三级联动").onClick(event=>{ 25 | router.pushUrl({url: Pages.SelectRegionPage}, router.RouterMode.Single) 26 | }) 27 | } 28 | .height('100%') 29 | .width('100%') 30 | .justifyContent(FlexAlign.Center) 31 | } 32 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Main.ets: -------------------------------------------------------------------------------- 1 | 2 | import { TabBarItem } from '../model/TabBarItem' 3 | import { logger } from '../utils/Logger' 4 | import { ArktsUiPage } from './ArktsUiPage' 5 | import { HomePage } from './HomePage' 6 | 7 | @Entry 8 | @Component 9 | struct Main { 10 | @State activeIndex: number = 0 11 | @State isLandscape: boolean = false 12 | 13 | tabBarItems: TabBarItem[] = [ 14 | { 15 | icon: $r('app.media.icon_birthday_normal'), 16 | selectIcon: $r('app.media.icon_birthday_select'), 17 | title: $r('app.string.home') 18 | }, 19 | { 20 | icon: $r('app.media.icon_discover_normal'), 21 | selectIcon: $r('app.media.icon_discover_select'), 22 | title: $r('app.string.arkts_ui') 23 | } 24 | // , 25 | // { 26 | // icon: $r('app.media.icon_person_normal'), 27 | // selectIcon: $r('app.media.icon_person_select'), 28 | // title: $r('app.string.person') 29 | // } 30 | ] 31 | 32 | aboutToAppear(): void { 33 | logger.isDebug=true;//debug模式 34 | } 35 | 36 | build() { 37 | Tabs({ 38 | index: this.activeIndex 39 | }) { 40 | ForEach(this.tabBarItems,(item: TabBarItem, index: number) => { 41 | TabContent() { 42 | if (index === 0) HomePage() 43 | else if (index === 1) ArktsUiPage() 44 | } 45 | .tabBar(this.TabBarBuilder(item, index)) 46 | .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) 47 | }) 48 | } 49 | .divider({ strokeWidth: $r('app.float.border_width'), color: $r('app.color.splite_color') }) 50 | .vertical(this.isLandscape) 51 | .barPosition(this.isLandscape ? BarPosition.Start : BarPosition.End) 52 | .onTabBarClick((index) => { 53 | this.activeIndex = index 54 | }) 55 | .barHeight(this.isLandscape ? $r('app.float.tab_bar_item_height_lg') : $r('app.float.tab_bar_item_height_sm')) 56 | .scrollable(false) 57 | } 58 | 59 | 60 | @Builder 61 | TabBarBuilder(item: TabBarItem, index: number) { 62 | Column({ space: 4.5 }) { 63 | Image(this.activeIndex === index ? item.selectIcon : item.icon) 64 | .width(18) 65 | Text(item.title) 66 | .fontSize(12) 67 | .fontColor(this.activeIndex === index ? $r('app.color.mainColor') : $r('app.color.body_color')) 68 | .animation({ duration: 300 }) 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/SelectMediaPage.ets: -------------------------------------------------------------------------------- 1 | import { util } from '@kit.ArkTS' 2 | import { photoAccessHelper } from '@kit.MediaLibraryKit'; 3 | import { HdNav } from '../common/HdNav'; 4 | 5 | /** 6 | * 选择图片/视频 7 | * @author Ansen 8 | * @create time 2024-09-07 9 | */ 10 | @Entry 11 | @Component 12 | struct SelectMediaPage { 13 | static readonly MAX_ALBUM_NUM=8; 14 | @State list:string[]=[] 15 | 16 | build() { 17 | Column(){ 18 | HdNav({ 19 | title:"选择图片/视频" 20 | }) 21 | 22 | Flex({ wrap: FlexWrap.Wrap }){ // 子组件多行布局 23 | ForEach(this.list, (item:string,index) => { 24 | ListItem() { 25 | Stack({alignContent:Alignment.TopEnd}){ 26 | Image(item).width('100%').height(90).onClick(event=>{ 27 | this.onAlbumClick(index); 28 | }) 29 | Image($r('app.media.icon_delete_image')).width(25).height(25).padding(5).onClick(event=>{ 30 | this.onDeleteAlbumImage(item,index); 31 | }) 32 | }.width('23.5%').height(90).margin({ 33 | left:2,right:2,top:2,bottom:2 34 | }) 35 | } 36 | }, (item:string) => util.generateRandomUUID(true)) 37 | 38 | if (this.list.length < SelectMediaPage.MAX_ALBUM_NUM){ 39 | Image($r('app.media.icon_album_add')).width(90).height(90).onClick(event=>{ 40 | this.onSelectAlbum(); 41 | }) 42 | } 43 | }.margin({ 44 | top:15,bottom:15 45 | }).width('100%').backgroundColor($r('app.color.white')) 46 | .padding({ 47 | left:10,right:10,top:5,bottom:5 48 | }) 49 | } 50 | } 51 | 52 | onSelectAlbum(){ 53 | // let preselectedUris : Array = [] 54 | // for(let album of this.list){ 55 | // preselectedUris.push(album.imageUrl); 56 | // } 57 | 58 | //此处获取的phAccessHelper实例为全局对象,后续使用到phAccessHelper的地方默认为使用此处获取的对象,如未添加此段代码报phAccessHelper未定义的错误请自行添加 59 | const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions(); 60 | photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE; // 过滤选择媒体文件类型为IMAGE 61 | photoSelectOptions.maxSelectNumber = SelectMediaPage.MAX_ALBUM_NUM - this.list.length; // 选择媒体文件的最大数目 62 | photoSelectOptions.preselectedUris = this.list; 63 | // photoSelectOptions.isOriginalSupported = true; 64 | const photoViewPicker = new photoAccessHelper.PhotoViewPicker(); 65 | photoViewPicker.select(photoSelectOptions).then((photoSelectResult: photoAccessHelper.PhotoSelectResult) => { 66 | let uris = photoSelectResult.photoUris; 67 | for(let uri of uris){ 68 | this.list.push(uri); 69 | } 70 | }) 71 | } 72 | 73 | onAlbumClick(index:number){ 74 | 75 | } 76 | 77 | onDeleteAlbumImage(item:string,index:number){ 78 | this.list.splice(index,1); 79 | } 80 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/SelectRegionPage.ets: -------------------------------------------------------------------------------- 1 | import { HdNav } from '../common/HdNav' 2 | import SelectRegionDialog from '../dialog/SelectRegionDialog' 3 | 4 | /** 5 | * 省市区三级联动 6 | * 数据来源:https://github.com/modood/Administrative-divisions-of-China/tree/master 7 | * @author Ansen 8 | * @create time 2024-09-07 9 | */ 10 | @Entry 11 | @Component 12 | struct SelectRegionPage { 13 | @State selectProvince:string="" 14 | @State selectCity:string="" 15 | 16 | selectRegionDialog: CustomDialogController = new CustomDialogController({ 17 | builder: SelectRegionDialog({ 18 | selectProvince:this.selectProvince, 19 | selectCity:this.selectCity, 20 | confirm:this.onConfirm.bind(this) 21 | }), 22 | alignment: DialogAlignment.Bottom, // 可设置dialog的对齐方式,设定显示在底部或中间等,默认为底部显示 23 | cornerRadius:{ 24 | topLeft:13,topRight:13,bottomLeft:0,bottomRight:0 25 | }, 26 | autoCancel:false, 27 | width:'100%' 28 | }) 29 | 30 | build() { 31 | Column(){ 32 | HdNav({ 33 | title:"省市区三级联动" 34 | }) 35 | 36 | Text(this.selectProvince+this.selectCity).fontColor($r('app.color.title_color')).fontSize(24).margin({ 37 | top:150 38 | }) 39 | 40 | Button("选择省市区").onClick(event=>{ 41 | this.onSelectRegion(); 42 | }).margin({ 43 | top:20 44 | }) 45 | } 46 | } 47 | 48 | onSelectRegion(){ 49 | this.selectRegionDialog.open(); 50 | } 51 | 52 | onConfirm(province:string,city:string){ 53 | this.selectProvince = province; 54 | this.selectCity = city; 55 | } 56 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/WebViewPage.ets: -------------------------------------------------------------------------------- 1 | 2 | import webView from '@ohos.web.webview' 3 | import { router } from '@kit.ArkUI'; 4 | import { HdNav } from '../common/HdNav'; 5 | import { logger } from '../utils/Logger'; 6 | 7 | @Entry 8 | @Component 9 | struct WebViewPage { 10 | @State url:string = ""; 11 | @State title:string|undefined = ""; 12 | 13 | controller: webView.WebviewController = new webView.WebviewController() 14 | 15 | aboutToAppear(): void { 16 | let params = router.getParams() as Record 17 | this.url=params["url"] as string; 18 | // this.title=params["title"] as Resource; 19 | logger.info("传递过来的url:"+this.url) 20 | } 21 | 22 | build() { 23 | Column() { 24 | HdNav({ 25 | title:this.title 26 | }) 27 | 28 | Web({ src: this.url, controller: this.controller }) 29 | .javaScriptAccess(true) 30 | .onTitleReceive((event) => { 31 | this.title = event?.title; 32 | }).layoutWeight(1).onLoadIntercept(event=>{ 33 | let url = event.data.getRequestUrl(); 34 | console.log('可以在这里拦截请求 url:' + event.data.getRequestUrl()); 35 | return false; 36 | }) 37 | 38 | }.justifyContent(FlexAlign.Start) 39 | } 40 | } 41 | 42 | class testClass { 43 | constructor() { 44 | } 45 | 46 | test(): string { 47 | return "ArkUI Web Component"; 48 | } 49 | 50 | toString(): void { 51 | console.log('Web Component toString'); 52 | } 53 | } -------------------------------------------------------------------------------- /entry/src/main/ets/utils/AppConstant.ets: -------------------------------------------------------------------------------- 1 | 2 | export class AppConstant{ 3 | static readonly SHARE_URL="https://www.srzs.top"; 4 | static readonly URL=AppConstant.SHARE_URL+"/birthday_assistant"; 5 | } 6 | 7 | export class Pages{ 8 | static readonly DialogPage="pages/DialogPage"; 9 | static readonly WebViewPage="pages/WebViewPage"; 10 | static readonly SelectMediaPage="pages/SelectMediaPage"; 11 | static readonly CropAvatarPage="pages/CropAvatarPage"; 12 | static readonly SelectRegionPage="pages/SelectRegionPage"; 13 | } -------------------------------------------------------------------------------- /entry/src/main/ets/utils/AppUtil.ets: -------------------------------------------------------------------------------- 1 | import { pasteboard } from '@kit.BasicServicesKit'; 2 | import { cryptoFramework } from '@kit.CryptoArchitectureKit'; 3 | import { promptAction, router } from '@kit.ArkUI'; 4 | import { systemShare } from '@kit.ShareKit'; 5 | import { common, Want, wantConstant } from '@kit.AbilityKit'; 6 | import { hilog } from '@kit.PerformanceAnalysisKit'; 7 | import { uniformTypeDescriptor as utd } from '@kit.ArkData'; 8 | import fileuri from '@ohos.file.fileuri'; 9 | import { AppConstant, Pages } from './AppConstant'; 10 | import { logger } from './Logger'; 11 | import { util } from '@kit.ArkTS'; 12 | 13 | export class AppUtil{ 14 | static getString(component: Object,resource: ResourceStr){ 15 | if (typeof resource === 'string') {//如果是字符串类型,直接返回 16 | return resource; 17 | } 18 | return getContext(component).resourceManager.getStringSync(resource); 19 | } 20 | 21 | //复制字符串 22 | static copy(content: string){ 23 | let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN,content); 24 | let systemPasteboard = pasteboard.getSystemPasteboard(); 25 | systemPasteboard.setDataSync(pasteData) 26 | } 27 | 28 | /** 29 | * md5 加密 30 | * @param source 31 | * @returns 32 | */ 33 | static md5(content: string){ 34 | // 创建一个MD5哈希对象 35 | let md5 = cryptoFramework.createMd('MD5'); 36 | md5.updateSync({ 37 | data: AppUtil.stringToUint8Array(content) 38 | }) 39 | let out=md5.digestSync() 40 | let str = AppUtil.uint8ArrayToHexString(out.data); 41 | return str 42 | } 43 | 44 | /** 45 | * uint8Array 转换为 hex字符串 46 | * @param uint8Array 47 | * @returns 48 | */ 49 | private static uint8ArrayToHexString(uint8Array: Uint8Array): string { 50 | return Array.from(uint8Array).map(byte => byte.toString(16).padStart(2, '0')).join(''); 51 | } 52 | 53 | private static stringToUint8Array(str: string): Uint8Array { 54 | const utf8 = unescape(encodeURIComponent(str)); 55 | const arr = new Uint8Array(utf8.length); 56 | for (let i = 0; i < utf8.length; i++) { 57 | arr[i] = utf8.charCodeAt(i); 58 | } 59 | return arr; 60 | } 61 | 62 | static showToast(message:string | Resource){ 63 | promptAction.showToast({message:message}) 64 | } 65 | 66 | //分享文本给系统 67 | static shareText(content: string){ 68 | // 构造ShareData,需配置一条有效数据信息 69 | let data: systemShare.SharedData = new systemShare.SharedData({ 70 | utd: utd.UniformDataType.PLAIN_TEXT, 71 | content: content 72 | }); 73 | 74 | // 构建ShareController 75 | let controller: systemShare.ShareController = new systemShare.ShareController(data); 76 | // 获取UIAbility上下文对象 77 | let context: common.UIAbilityContext = getContext() as common.UIAbilityContext; 78 | hilog.error(0, 'ansen', "开始分享"); 79 | // 进行分享面板显示 80 | controller.show(context, { 81 | previewMode: systemShare.SharePreviewMode.DETAIL, 82 | selectionMode: systemShare.SelectionMode.SINGLE 83 | }); 84 | } 85 | 86 | static shareFile(filePath:string){ 87 | // 将沙箱路径转换为uri 88 | let uri = fileuri.getUriFromPath(filePath); 89 | let want:Want = { 90 | // 配置被分享文件的读写权限,例如对被分享应用进行读写授权 91 | flags: wantConstant.Flags.FLAG_AUTH_WRITE_URI_PERMISSION | wantConstant.Flags.FLAG_AUTH_READ_URI_PERMISSION, 92 | // 配置分享应用的隐式拉起规则 93 | action: 'ohos.want.action.sendData', 94 | uri: uri, 95 | type: '*/*' 96 | } 97 | let context = getContext() as common.UIAbilityContext; 98 | context.startAbility(want) .then(() => { 99 | console.info('Invoke getCurrentBundleStats succeeded.'); 100 | }) 101 | .catch(() => { 102 | // console.error(`Invoke startAbility failed, code is ${err.code}, message is ${err.message}`); 103 | }); 104 | } 105 | 106 | static openWebUrl(urlSuffix:string){ 107 | let url= AppConstant.URL+urlSuffix; 108 | logger.info("url:"+url) 109 | router.pushUrl({ 110 | url: Pages.WebViewPage, 111 | params:{ 112 | data1: 'message', 113 | url: url, // 传递的 URL 参数 114 | } 115 | }, router.RouterMode.Single) 116 | } 117 | 118 | static getRawFileContent(component: Object,fileName:string){ 119 | let data=getContext(component).resourceManager.getRawFileContentSync(fileName); 120 | let result = util.TextDecoder.create('utf-8', {}).decodeWithStream(data); 121 | return result; 122 | } 123 | } -------------------------------------------------------------------------------- /entry/src/main/ets/utils/Logger.ets: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog' 2 | 3 | const DOMAIN = 0xFF09 4 | const PREFIX = 'Ansen' 5 | const FORMAT = '%{public}s, %{public}s' 6 | 7 | class Logger{ 8 | public isDebug=false; 9 | 10 | debug(...args: string[]) { 11 | if (!this.isDebug){ 12 | return ; 13 | } 14 | hilog.debug(DOMAIN, PREFIX, FORMAT, args) 15 | } 16 | 17 | info(...args: string[]) { 18 | if (!this.isDebug){ 19 | return ; 20 | } 21 | hilog.info(DOMAIN, PREFIX, FORMAT, args) 22 | } 23 | 24 | warn(...args: string[]) { 25 | if (!this.isDebug){ 26 | return ; 27 | } 28 | hilog.warn(DOMAIN, PREFIX, FORMAT, args) 29 | } 30 | 31 | error(...args: string[]) { 32 | if (!this.isDebug){ 33 | return ; 34 | } 35 | hilog.error(DOMAIN, PREFIX, FORMAT, args) 36 | } 37 | 38 | fatal(...args: string[]) { 39 | if (!this.isDebug){ 40 | return ; 41 | } 42 | hilog.fatal(DOMAIN, PREFIX, FORMAT, args) 43 | } 44 | 45 | isLoggable(level: hilog.LogLevel) { 46 | if (!this.isDebug){ 47 | return ; 48 | } 49 | hilog.isLoggable(DOMAIN, PREFIX, level) 50 | } 51 | } 52 | 53 | export const logger = new Logger() -------------------------------------------------------------------------------- /entry/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry", 4 | "type": "entry", 5 | "description": "$string:module_desc", 6 | "requestPermissions": [ 7 | { 8 | "name": "ohos.permission.INTERNET" 9 | }, 10 | ], 11 | "mainElement": "EntryAbility", 12 | "deviceTypes": [ 13 | "phone", 14 | "tablet", 15 | "2in1" 16 | ], 17 | "deliveryWithInstall": true, 18 | "installationFree": false, 19 | "pages": "$profile:main_pages", 20 | "abilities": [ 21 | { 22 | "name": "EntryAbility", 23 | "srcEntry": "./ets/entryability/EntryAbility.ets", 24 | "description": "$string:EntryAbility_desc", 25 | "icon": "$media:app_icon", 26 | "label": "$string:app_name", 27 | "startWindowIcon": "$media:app_icon", 28 | "startWindowBackground": "$color:start_window_background", 29 | "exported": true, 30 | "skills": [ 31 | { 32 | "entities": [ 33 | "entity.system.home" 34 | ], 35 | "actions": [ 36 | "action.system.home" 37 | ] 38 | } 39 | ] 40 | } 41 | ], 42 | "extensionAbilities": [ 43 | { 44 | "name": "EntryBackupAbility", 45 | "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", 46 | "type": "backup", 47 | "exported": false, 48 | "metadata": [ 49 | { 50 | "name": "ohos.extension.backup", 51 | "resource": "$profile:backup_config" 52 | } 53 | ], 54 | } 55 | ] 56 | } 57 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | }, 7 | { 8 | "name": "mainColor", 9 | "value": "#F1525A" 10 | }, 11 | { 12 | "name": "title_color", 13 | "value": "#333333" 14 | }, 15 | { 16 | "name": "body_color", 17 | "value": "#666666" 18 | }, 19 | { 20 | "name": "other_color", 21 | "value": "#999999" 22 | }, 23 | { 24 | "name": "splite_color", 25 | "value": "#E5E5E5" 26 | }, 27 | { 28 | "name": "globle_color", 29 | "value": "#F7F7F7" 30 | }, 31 | { 32 | "name": "white", 33 | "value": "#ffffff" 34 | }, 35 | { 36 | "name": "start_main_color", 37 | "value": "#FF8B59" 38 | }, 39 | { 40 | "name": "end_main_color", 41 | "value": "#F64848" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/float.json: -------------------------------------------------------------------------------- 1 | { 2 | "float": [ 3 | 4 | { 5 | "name": "border_width", 6 | "value": "0.5vp" 7 | }, 8 | { 9 | "name": "tab_bar_item_height_sm", 10 | "value": "50vp" 11 | }, 12 | { 13 | "name": "tab_bar_item_height_lg", 14 | "value": "400vp" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | }, 15 | { 16 | "name": "simple_user_policy", 17 | "value": "用户协议和隐私政策说明" 18 | }, 19 | { 20 | "name": "privacy_policy_start", 21 | "value": "请您充分阅读并理解" 22 | }, 23 | { 24 | "name": "user_agreement_two", 25 | "value": "《用户协议》" 26 | }, 27 | { 28 | "name": "and", 29 | "value": "和" 30 | }, 31 | { 32 | "name": "privacy_policy_two", 33 | "value": "《隐私政策》" 34 | }, 35 | { 36 | "name": "simple_privacy_policy", 37 | "value": "\n我们非常重视您的隐私和个人信息保护。在您使用的过程中,我们会对您的部分个人信息进行收集和使用。\n1.我们会申请您的设备信息、位置权限、软件安装列表、日志信息等用于平台安全风控及查看附近的用户。\n2.在您使用上传、分享、语音等服务时,我们需要获取您设备的摄像头、麦克风、相册、地理位置、存储空间等以上敏感权限均不会默认或强制开启收集信息。\n3.友盟SDK会收集设备Mac地址,设备序列号用于统计\n4.您使用一键登录登录时,会收集您的SIM卡信息\n5.未经您同意,我们不会出售或共享您的任何信息。您可以查询,更正、删除您的个人信息,我们也提供账户注销、举报方式。" 38 | }, 39 | { 40 | "name": "disagree_privacy_policy", 41 | "value": "不同意" 42 | }, 43 | { 44 | "name": "agree_privacy_policy", 45 | "value": "同意" 46 | }, 47 | { 48 | "name": "home", 49 | "value": "首页" 50 | }, 51 | { 52 | "name": "arkts_ui", 53 | "value": "UI组件" 54 | } 55 | ] 56 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/background.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/foreground.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_album_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_album_add.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_birthday_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_birthday_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_birthday_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_birthday_select.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_delete_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_delete_image.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_discover_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_discover_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_discover_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_discover_select.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_loading_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_loading_3.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_person_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_person_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_person_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_person_select.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon_title_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/icon_title_back.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/layered_image.json: -------------------------------------------------------------------------------- 1 | { 2 | "layered-image": 3 | { 4 | "background" : "$media:background", 5 | "foreground" : "$media:foreground" 6 | } 7 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/startIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/entry/src/main/resources/base/media/startIcon.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/profile/backup_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowToBackupRestore": true 3 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/profile/main_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "pages/Main", 4 | "pages/DialogPage", 5 | "pages/WebViewPage", 6 | "pages/SelectMediaPage", 7 | "pages/CropAvatarPage", 8 | "pages/SelectRegionPage" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /entry/src/main/resources/en_US/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "module description" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/main/resources/rawfile/pc.json: -------------------------------------------------------------------------------- 1 | {"北京市":["东城区","西城区","朝阳区","丰台区","石景山区","海淀区","门头沟区","房山区","通州区","顺义区","昌平区","大兴区","怀柔区","平谷区","密云区","延庆区"],"天津市":["和平区","河东区","河西区","南开区","河北区","红桥区","东丽区","西青区","津南区","北辰区","武清区","宝坻区","滨海新区","宁河区","静海区","蓟州区"],"河北省":["石家庄市","唐山市","秦皇岛市","邯郸市","邢台市","保定市","张家口市","承德市","沧州市","廊坊市","衡水市"],"山西省":["太原市","大同市","阳泉市","长治市","晋城市","朔州市","晋中市","运城市","忻州市","临汾市","吕梁市"],"内蒙古自治区":["呼和浩特市","包头市","乌海市","赤峰市","通辽市","鄂尔多斯市","呼伦贝尔市","巴彦淖尔市","乌兰察布市","兴安盟","锡林郭勒盟","阿拉善盟"],"辽宁省":["沈阳市","大连市","鞍山市","抚顺市","本溪市","丹东市","锦州市","营口市","阜新市","辽阳市","盘锦市","铁岭市","朝阳市","葫芦岛市"],"吉林省":["长春市","吉林市","四平市","辽源市","通化市","白山市","松原市","白城市","延边朝鲜族自治州"],"黑龙江省":["哈尔滨市","齐齐哈尔市","鸡西市","鹤岗市","双鸭山市","大庆市","伊春市","佳木斯市","七台河市","牡丹江市","黑河市","绥化市","大兴安岭地区"],"上海市":["黄浦区","徐汇区","长宁区","静安区","普陀区","虹口区","杨浦区","闵行区","宝山区","嘉定区","浦东新区","金山区","松江区","青浦区","奉贤区","崇明区"],"江苏省":["南京市","无锡市","徐州市","常州市","苏州市","南通市","连云港市","淮安市","盐城市","扬州市","镇江市","泰州市","宿迁市"],"浙江省":["杭州市","宁波市","温州市","嘉兴市","湖州市","绍兴市","金华市","衢州市","舟山市","台州市","丽水市"],"安徽省":["合肥市","芜湖市","蚌埠市","淮南市","马鞍山市","淮北市","铜陵市","安庆市","黄山市","滁州市","阜阳市","宿州市","六安市","亳州市","池州市","宣城市"],"福建省":["福州市","厦门市","莆田市","三明市","泉州市","漳州市","南平市","龙岩市","宁德市"],"江西省":["南昌市","景德镇市","萍乡市","九江市","新余市","鹰潭市","赣州市","吉安市","宜春市","抚州市","上饶市"],"山东省":["济南市","青岛市","淄博市","枣庄市","东营市","烟台市","潍坊市","济宁市","泰安市","威海市","日照市","临沂市","德州市","聊城市","滨州市","菏泽市"],"河南省":["郑州市","开封市","洛阳市","平顶山市","安阳市","鹤壁市","新乡市","焦作市","濮阳市","许昌市","漯河市","三门峡市","南阳市","商丘市","信阳市","周口市","驻马店市","济源市"],"湖北省":["武汉市","黄石市","十堰市","宜昌市","襄阳市","鄂州市","荆门市","孝感市","荆州市","黄冈市","咸宁市","随州市","恩施土家族苗族自治州","仙桃市","潜江市","天门市","神农架林区"],"湖南省":["长沙市","株洲市","湘潭市","衡阳市","邵阳市","岳阳市","常德市","张家界市","益阳市","郴州市","永州市","怀化市","娄底市","湘西土家族苗族自治州"],"广东省":["广州市","韶关市","深圳市","珠海市","汕头市","佛山市","江门市","湛江市","茂名市","肇庆市","惠州市","梅州市","汕尾市","河源市","阳江市","清远市","东莞市","中山市","潮州市","揭阳市","云浮市"],"广西壮族自治区":["南宁市","柳州市","桂林市","梧州市","北海市","防城港市","钦州市","贵港市","玉林市","百色市","贺州市","河池市","来宾市","崇左市"],"海南省":["海口市","三亚市","三沙市","儋州市","五指山市","琼海市","文昌市","万宁市","东方市","定安县","屯昌县","澄迈县","临高县","白沙黎族自治县","昌江黎族自治县","乐东黎族自治县","陵水黎族自治县","保亭黎族苗族自治县","琼中黎族苗族自治县"],"重庆市":["万州区","涪陵区","渝中区","大渡口区","江北区","沙坪坝区","九龙坡区","南岸区","北碚区","綦江区","大足区","渝北区","巴南区","黔江区","长寿区","江津区","合川区","永川区","南川区","璧山区","铜梁区","潼南区","荣昌区","开州区","梁平区","武隆区","城口县","丰都县","垫江县","忠县","云阳县","奉节县","巫山县","巫溪县","石柱土家族自治县","秀山土家族苗族自治县","酉阳土家族苗族自治县","彭水苗族土家族自治县"],"四川省":["成都市","自贡市","攀枝花市","泸州市","德阳市","绵阳市","广元市","遂宁市","内江市","乐山市","南充市","眉山市","宜宾市","广安市","达州市","雅安市","巴中市","资阳市","阿坝藏族羌族自治州","甘孜藏族自治州","凉山彝族自治州"],"贵州省":["贵阳市","六盘水市","遵义市","安顺市","毕节市","铜仁市","黔西南布依族苗族自治州","黔东南苗族侗族自治州","黔南布依族苗族自治州"],"云南省":["昆明市","曲靖市","玉溪市","保山市","昭通市","丽江市","普洱市","临沧市","楚雄彝族自治州","红河哈尼族彝族自治州","文山壮族苗族自治州","西双版纳傣族自治州","大理白族自治州","德宏傣族景颇族自治州","怒江傈僳族自治州","迪庆藏族自治州"],"西藏自治区":["拉萨市","日喀则市","昌都市","林芝市","山南市","那曲市","阿里地区"],"陕西省":["西安市","铜川市","宝鸡市","咸阳市","渭南市","延安市","汉中市","榆林市","安康市","商洛市"],"甘肃省":["兰州市","嘉峪关市","金昌市","白银市","天水市","武威市","张掖市","平凉市","酒泉市","庆阳市","定西市","陇南市","临夏回族自治州","甘南藏族自治州"],"青海省":["西宁市","海东市","海北藏族自治州","黄南藏族自治州","海南藏族自治州","果洛藏族自治州","玉树藏族自治州","海西蒙古族藏族自治州"],"宁夏回族自治区":["银川市","石嘴山市","吴忠市","固原市","中卫市"],"新疆维吾尔自治区":["乌鲁木齐市","克拉玛依市","吐鲁番市","哈密市","昌吉回族自治州","博尔塔拉蒙古自治州","巴音郭楞蒙古自治州","阿克苏地区","克孜勒苏柯尔克孜自治州","喀什地区","和田地区","伊犁哈萨克自治州","塔城地区","阿勒泰地区","石河子市","阿拉尔市","图木舒克市","五家渠市","北屯市","铁门关市","双河市","可克达拉市","昆玉市","胡杨河市","新星市","白杨市"]} -------------------------------------------------------------------------------- /entry/src/main/resources/zh_CN/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_desc", 5 | "value": "模块描述" 6 | }, 7 | { 8 | "name": "EntryAbility_desc", 9 | "value": "description" 10 | }, 11 | { 12 | "name": "EntryAbility_label", 13 | "value": "label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/mock/mock-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/test/Ability.test.ets: -------------------------------------------------------------------------------- 1 | import { hilog } from '@kit.PerformanceAnalysisKit'; 2 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; 3 | 4 | export default function abilityTest() { 5 | describe('ActsAbilityTest', () => { 6 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 7 | beforeAll(() => { 8 | // Presets an action, which is performed only once before all test cases of the test suite start. 9 | // This API supports only one parameter: preset action function. 10 | }) 11 | beforeEach(() => { 12 | // Presets an action, which is performed before each unit test case starts. 13 | // The number of execution times is the same as the number of test cases defined by **it**. 14 | // This API supports only one parameter: preset action function. 15 | }) 16 | afterEach(() => { 17 | // Presets a clear action, which is performed after each unit test case ends. 18 | // The number of execution times is the same as the number of test cases defined by **it**. 19 | // This API supports only one parameter: clear action function. 20 | }) 21 | afterAll(() => { 22 | // Presets a clear action, which is performed after all test cases of the test suite end. 23 | // This API supports only one parameter: clear action function. 24 | }) 25 | it('assertContain', 0, () => { 26 | // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. 27 | hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); 28 | let a = 'abc'; 29 | let b = 'b'; 30 | // Defines a variety of assertion methods, which are used to declare expected boolean conditions. 31 | expect(a).assertContain(b); 32 | expect(a).assertEqual(a); 33 | }) 34 | }) 35 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/test/List.test.ets: -------------------------------------------------------------------------------- 1 | import abilityTest from './Ability.test'; 2 | 3 | export default function testsuite() { 4 | abilityTest(); 5 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry_test", 4 | "type": "feature", 5 | "deviceTypes": [ 6 | "phone", 7 | "tablet", 8 | "2in1" 9 | ], 10 | "deliveryWithInstall": true, 11 | "installationFree": false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /entry/src/test/List.test.ets: -------------------------------------------------------------------------------- 1 | import localUnitTest from './LocalUnit.test'; 2 | 3 | export default function testsuite() { 4 | localUnitTest(); 5 | } -------------------------------------------------------------------------------- /entry/src/test/LocalUnit.test.ets: -------------------------------------------------------------------------------- 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 | } -------------------------------------------------------------------------------- /hvigor/hvigor-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | "modelVersion": "5.0.0", 3 | "dependencies": { 4 | }, 5 | "execution": { 6 | // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ 7 | // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ 8 | // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ 9 | // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ 10 | // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ 11 | }, 12 | "logging": { 13 | // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ 14 | }, 15 | "debugging": { 16 | // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ 17 | }, 18 | "nodeOptions": { 19 | // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ 20 | // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /hvigorfile.ts: -------------------------------------------------------------------------------- 1 | import { appTasks } from '@ohos/hvigor-ohos-plugin'; 2 | 3 | export default { 4 | system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ 5 | plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ 6 | } 7 | -------------------------------------------------------------------------------- /oh-package-lock.json5: -------------------------------------------------------------------------------- 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/hamock@1.0.0": "@ohos/hamock@1.0.0", 9 | "@ohos/hypium@1.0.18": "@ohos/hypium@1.0.18" 10 | }, 11 | "packages": { 12 | "@ohos/hamock@1.0.0": { 13 | "name": "@ohos/hamock", 14 | "version": "1.0.0", 15 | "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", 16 | "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har", 17 | "registryType": "ohpm" 18 | }, 19 | "@ohos/hypium@1.0.18": { 20 | "name": "@ohos/hypium", 21 | "version": "1.0.18", 22 | "integrity": "sha512-RGe/iLGdeywdQilMWZsHKUoiE9OJ+9QxQsorF92R2ImLNVHVhbpSePNITGpW7TnvLgOIP/jscOqfIOhk6X7XRQ==", 23 | "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.18.har", 24 | "registryType": "ohpm" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "modelVersion": "5.0.0", 3 | "description": "Please describe the basic information.", 4 | "dependencies": { 5 | }, 6 | "devDependencies": { 7 | "@ohos/hypium": "1.0.18", 8 | "@ohos/hamock": "1.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /screenshot/SelectRegionPage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/screenshot/SelectRegionPage.gif -------------------------------------------------------------------------------- /screenshot/dialog/加载中弹窗.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/screenshot/dialog/加载中弹窗.gif -------------------------------------------------------------------------------- /screenshot/dialog/自定义弹窗.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/screenshot/dialog/自定义弹窗.png -------------------------------------------------------------------------------- /screenshot/dialog/警告弹窗.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansen666/harmony_tools/c27b42441501a7c7990d532d69cea2796d84de05/screenshot/dialog/警告弹窗.png --------------------------------------------------------------------------------