├── .gitignore ├── AppScope ├── app.json5 └── resources │ └── base │ ├── element │ └── string.json │ └── media │ └── app_icon.png ├── README.md ├── build-profile.json5 ├── entry ├── .gitignore ├── build-profile.json5 ├── hvigorfile.ts ├── oh-package.json5 └── src │ ├── main │ ├── ets │ │ ├── api │ │ │ ├── HttpConfig.ets │ │ │ ├── HttpRequest.ets │ │ │ ├── HttpService.ets │ │ │ └── ResponseResult.ets │ │ ├── bean │ │ │ ├── ArticleBean.ets │ │ │ ├── BannerBean.ets │ │ │ ├── CoinBean.ets │ │ │ ├── MsgBean.ets │ │ │ ├── ProjectBean.ets │ │ │ ├── RankBean.ets │ │ │ ├── SettingItem.ets │ │ │ ├── TreeBean.ets │ │ │ └── UserInfoBean.ets │ │ ├── common │ │ │ ├── bean │ │ │ │ └── BottomTabsItem.ets │ │ │ └── constants │ │ │ │ └── Constants.ets │ │ ├── entryability │ │ │ └── EntryAbility.ets │ │ ├── pages │ │ │ ├── HomePage.ets │ │ │ ├── InfoPage.ets │ │ │ ├── LoginRegisterPage.ets │ │ │ ├── MainPage.ets │ │ │ ├── MePage.ets │ │ │ ├── MsgPage.ets │ │ │ ├── ProjectPage.ets │ │ │ ├── SettingPage.ets │ │ │ ├── Splash.ets │ │ │ ├── TreePage.ets │ │ │ ├── TreeTabPage.ets │ │ │ └── WebPage.ets │ │ ├── utils │ │ │ ├── Logger.ets │ │ │ ├── PreferencesUtils.ets │ │ │ └── Utils.ets │ │ ├── view │ │ │ ├── ArticleItem.ets │ │ │ ├── InfoItem.ets │ │ │ ├── MineFunc.ets │ │ │ ├── MineHead.ets │ │ │ ├── MsgItemView.ets │ │ │ ├── MsgReadList.ets │ │ │ ├── MsgUnreadList.ets │ │ │ ├── ProjectList.ets │ │ │ ├── TitleBar.ets │ │ │ └── TreeList.ets │ │ └── viewmodel │ │ │ ├── BottomTabsModel.ets │ │ │ ├── HomeViewModel.ets │ │ │ ├── InfoViewModel.ets │ │ │ ├── LoginRegisterViewModel.ets │ │ │ ├── MeViewModel.ets │ │ │ ├── MsgViewModel.ets │ │ │ ├── NavigatorModel.ets │ │ │ ├── ProjectViewModel.ets │ │ │ ├── SettingViewModel.ets │ │ │ └── TreeViewModel.ets │ ├── module.json5 │ └── resources │ │ ├── base │ │ ├── element │ │ │ ├── color.json │ │ │ ├── float.json │ │ │ └── string.json │ │ ├── media │ │ │ ├── ic_about.png │ │ │ ├── ic_arrow_left.png │ │ │ ├── ic_arrow_right.png │ │ │ ├── ic_clock.png │ │ │ ├── ic_home_normal.png │ │ │ ├── ic_home_selected.png │ │ │ ├── ic_language.png │ │ │ ├── ic_logo.png │ │ │ ├── ic_me_normal.png │ │ │ ├── ic_me_selected.png │ │ │ ├── ic_msg_normal.png │ │ │ ├── ic_msg_selected.png │ │ │ ├── ic_nav_normal.png │ │ │ ├── ic_nav_selected.png │ │ │ ├── ic_project_normal.png │ │ │ ├── ic_project_selected.png │ │ │ ├── ic_setting.png │ │ │ ├── ic_theme.png │ │ │ ├── ic_user.png │ │ │ ├── icon.png │ │ │ └── splash_bg.png │ │ └── profile │ │ │ └── main_pages.json │ │ ├── en_US │ │ └── element │ │ │ └── string.json │ │ └── zh_CN │ │ └── element │ │ └── string.json │ └── ohosTest │ ├── ets │ ├── test │ │ ├── Ability.test.ets │ │ └── List.test.ets │ ├── testability │ │ ├── TestAbility.ets │ │ └── pages │ │ │ └── Index.ets │ └── testrunner │ │ └── OpenHarmonyTestRunner.ts │ ├── module.json5 │ └── resources │ └── base │ ├── element │ ├── color.json │ └── string.json │ ├── media │ └── icon.png │ └── profile │ └── test_pages.json ├── hvigor └── hvigor-config.json5 ├── hvigorfile.ts ├── oh-package-lock.json5 ├── oh-package.json5 └── video └── wanandroidhm.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /local.properties 4 | /.idea 5 | **/build 6 | /.hvigor 7 | .cxx 8 | /.clangd 9 | /.clang-format 10 | /.clang-tidy 11 | **/.test -------------------------------------------------------------------------------- /AppScope/app.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "bundleName": "com.pgzxc.wanandroidhm", 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": "WanAndroidHM" 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /AppScope/resources/base/media/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/AppScope/resources/base/media/app_icon.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 一 项目演示 2 | 3 | 视频演示 4 | 5 |
8 | 9 | B站链接:[https://www.bilibili.com/video/BV1Ku4y1w7St](https://www.bilibili.com/video/BV1Ku4y1w7St) 10 | 11 | 项目预览 12 | 13 | | ![][waz-hm-1] | ![][waz-hm-2] | ![][waz-hm-3] | ![][waz-hm-4] | 14 | | :-----------: | :------------: | :------------: | :------------: | 15 | | ![][waz-hm-5] | ![][waz-hm-6] | ![][waz-hm-7] | ![][waz-hm-8] | 16 | | ![][waz-hm-9] | ![][waz-hm-10] | ![][waz-hm-11] | ![][waz-hm-12] | 17 | | ![][waz-hm-13]| ![][waz-hm-14] |![][waz-hm-15] | ![][waz-hm-16] | 18 | 19 | 20 | ## 二 开发环境 21 | 22 | * 操作系统:Windows 11 专业版 22H2 23 | * 开发工具:DevEco Studio 4.0 Release版本 24 | * 开发语言:ArkTS 25 | * node.js:v16.20.1 26 | * ohpm:1.2.5 27 | 28 | ## 三 基础知识 29 | 30 | * ArkTS语法(基本语法、状态管理、渲染控制) 31 | * UI(ArkTS声明式开发)及预览 32 | * HTTP数据请求 33 | 34 | ## 四 开发进度 35 | 36 | ### 4.1 v1.0 37 | 38 | * 启动页+底部导航框架 39 | * 底部导航框架使用:Tabs+tabBar 40 | * 在entryability/EntryAbility.ts的windowStage.loadContent中表明启动显示页面 41 | * 在resources/base/profile/main_pages.json添加Pages页面 42 | 43 | ### 4.2 v2.0 44 | 45 | * 网络请求框架(HttpService(网络请求封装)+ResponseResult(返回结果)+HttpRequest(网络请求Promise)+HttpConfig(相关配置)) 46 | * Stage模型,在module.json5配置文件中声明权限(比如网络权限`ohos.permission.INTERNET`) 47 | * bean包将接口返回结果封装成数据Bean 48 | * viewmodel中Promise+async解析结果数据为Bean 49 | * Page页面aboutToAppear(页面即将显示)方法中调用viewmodel中的接口 50 | 51 | ### 4.3 v3.0 52 | 53 | * 使用UI容器(Scroll、Row、Column、List)和常用组件Text、Image搭建UI界面 54 | * 自定义组件用于List-ListItem使用 55 | * Swiper实现轮播图 56 | * @State实现数据变化监听 57 | * 样式文件实现复杂布局 58 | * Resource:color-颜色命名文字不能大写,float-fp后缀对应文字,vp后缀对应宽度长度 59 | 60 | ### 4.4 v4.0 61 | 62 | * ForEach循环生成多组件 63 | * ListItemGroup分组列表组件 64 | * Flex弹性布局组件 65 | * 导航页面 66 | 67 | ### 4.5 v5.0 68 | 69 | * Tabs+ForEach(TabContent(TreeList-数据列表)+tabBar)组件展示各Tab标题下对应的列表 70 | * `@Watch装饰器`:状态变量更改通知。@Watch('changeTab')中声明changeTab方法 71 | * `@Link装饰器`:父子双向同步。@Link currentIndex: number中父组件ProjectPage传递当前tabIndex给ProjectList用于展示列表数据 72 | * 项目界面 73 | 74 | ### 4.6 v6.0 75 | 76 | 1- 布局 77 | 78 | * 消息 79 | * 我的 80 | * 登录、注册 81 | * 设置 82 | * 导航-知识体系下的文章 83 | * WebView显示网页内容 84 | 85 | 2-技术点 86 | 87 | * Preferences、PersistentStorage保存永久性数据 88 | * 组件:GridRow、GridCol、webview等 89 | * 页面间转场动画:pageTransition 90 | * 登录和注册成功后,保存cookie到PersistentStorage中,再次请求时,从AppStorage中获取cookie 91 | * 页面间导航: 92 | - 1-router(router.pushUrl({url: 'pages/Detail',urlparams: paramsInfo}); 93 | - 2-Navigator({ target:url, type: NavigationType.Push }) {}.params({key:value} 94 | * 参数获取 95 | - const params = router.getParams(); // 获取传递过来的参数对象 96 | - const id = params['id']; // 获取id属性的值 97 | 98 | ### 4.7 v7.0(版本升级) 99 | * OpenHarmony 3.1.1 Release 升级到OpenHarmony 4.0 Release 100 | * hvigor中hvigorVersion从2.4.2升级到3.0.9 101 | 102 | ### 4.8 v8.0 103 | * 导入下拉刷新库@ohos/pulltorefresh (V2.0.1)——说明,最新版本适配api10,不适用于当前项目 104 | * home页面添加下拉刷新和上拉加载功能 105 | 106 | ### 4.9 v9.0 107 | 108 | * 项目/导航 页添加刷新和加载更多功能 109 | * 去掉module.json5中为了预览添加的@Entry装饰器 110 | 111 | ### 4.10 v10.0 112 | 113 | * 我的-积分,跳转积分页面 114 | * 我的-排行,跳转排行页面 115 | 116 | ### 4.11 v11.0 117 | * api9升级到api10 118 | * api升级过程中出现的问题及解决办法 119 | 120 | ``` 121 | 1. 'params' is possibly 'undefined'. \ 122 | 2. Not all code paths return a value. \ 123 | 3. Use explicit types instead of "any", "unknown" (arkts-no-any-unknown) 124 | 4. Argument of type 'Tag[]' is not assignable to parameter of type 'string'. 125 | 5. Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals) 126 | 6. Array literals must contain elements of only inferrable types (arkts-no-noninferrable-arr-literals) \ 127 | 7. Type 'null' is not assignable to type 'UserData'. \ 128 | 8. try catch报错 129 | 9. 传值null问题 130 | ``` 131 | 132 | 133 | 134 | [waz-hm-1]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-splash-0.png 135 | [waz-hm-2]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-home-1.png 136 | [waz-hm-3]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-nav-2.png 137 | [waz-hm-4]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-navlist-3.png 138 | [waz-hm-5]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-project-4.png 139 | [waz-hm-6]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-msg-5.png 140 | [waz-hm-7]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-msg-6.png 141 | [waz-hm-8]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-me-7.png 142 | [waz-hm-9]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-me-8.png 143 | [waz-hm-10]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-login-9.png 144 | [waz-hm-11]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-register-10.png 145 | [waz-hm-12]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-me-set-11.png 146 | [waz-hm-13]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-home-refresh-12.png 147 | [waz-hm-14]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-home-loadmore-13.png 148 | [waz-hm-15]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-rank-14.png 149 | [waz-hm-16]:https://jsd.onmicrosoft.cn/gh/PGzxc/CDN/blog-resume/waz-hm-coin-15.png -------------------------------------------------------------------------------- /build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "app": { 3 | "signingConfigs": [], 4 | "products": [ 5 | { 6 | "name": "default", 7 | "signingConfig": "default", 8 | "compatibleSdkVersion": "4.0.0(10)", 9 | "runtimeOS": "HarmonyOS" 10 | } 11 | ] 12 | }, 13 | "modules": [ 14 | { 15 | "name": "entry", 16 | "srcPath": "./entry", 17 | "targets": [ 18 | { 19 | "name": "default", 20 | "applyToProducts": [ 21 | "default" 22 | ] 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /entry/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /oh_modules 3 | /.preview 4 | /build 5 | /.cxx 6 | /.test -------------------------------------------------------------------------------- /entry/build-profile.json5: -------------------------------------------------------------------------------- 1 | { 2 | "apiType": 'stageMode', 3 | "buildOption": { 4 | }, 5 | "targets": [ 6 | { 7 | "name": "default", 8 | "runtimeOS": "HarmonyOS" 9 | }, 10 | { 11 | "name": "ohosTest", 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /entry/hvigorfile.ts: -------------------------------------------------------------------------------- 1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 2 | export { hapTasks } from '@ohos/hvigor-ohos-plugin'; 3 | -------------------------------------------------------------------------------- /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/api/HttpConfig.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * @desc:网络请求相关配置 3 | */ 4 | 5 | export default class HttpConfig { 6 | /** 7 | * The host address of the server. 8 | */ 9 | static readonly baseURL: string = 'https://www.wanandroid.com/'; 10 | 11 | //1-首页 12 | static readonly bannerUrl: string = HttpConfig.baseURL + 'banner/json'; 13 | static readonly articleTopUrl: string = HttpConfig.baseURL + 'article/top/json'; 14 | static readonly articleListUrl: string = HttpConfig.baseURL + 'article/list'; // /0/json 15 | 16 | //2-导航 17 | static readonly treeUrl: string = HttpConfig.baseURL + 'tree/json'; 18 | static readonly treeListUrl: string = HttpConfig.baseURL + 'article/list'; 19 | 20 | //3-项目 21 | static readonly projectUrl: string = HttpConfig.baseURL + 'project/tree/json'; 22 | static readonly projectListUrl: string = HttpConfig.baseURL + 'project/list'; 23 | 24 | //4-消息-需要登录 25 | static readonly msgUnreadUrl: string = HttpConfig.baseURL + 'message/lg/count_unread/json'; 26 | static readonly msgReadUrl: string = HttpConfig.baseURL + 'message/lg/readed_list/'; //message/lg/readed_list/%s/json 27 | 28 | //5-用户信息 29 | static readonly userInfUrl: string = HttpConfig.baseURL + 'user/lg/userinfo/json'; 30 | 31 | //6-登录、注册、退出 32 | static readonly loginUrl: string = HttpConfig.baseURL + 'user/login'; 33 | static readonly registerUrl: string = HttpConfig.baseURL + 'user/register'; 34 | static readonly logoutUrl: string = HttpConfig.baseURL + 'user/logout/json'; 35 | 36 | //7-排行榜、积分 37 | static readonly rankUrl: string = HttpConfig.baseURL + 'coin/rank/'; //coin/rank/1/json 38 | static readonly coinUrl: string = HttpConfig.baseURL + 'lg/coin/list/'; //lg/coin/list/1/json 39 | /** 40 | * The request success status code. 41 | */ 42 | static readonly SERVER_CODE_SUCCESS: number = 0; 43 | 44 | /** 45 | * The off set coefficient. 46 | */ 47 | static readonly Y_OFF_SET_COEFFICIENT: number = 0.1; 48 | 49 | /** 50 | * The page size. 51 | */ 52 | static readonly PAGE_SIZE: number = 10; 53 | 54 | /** 55 | * The refresh and load height. 56 | */ 57 | static readonly CUSTOM_LAYOUT_HEIGHT: number = 70; 58 | 59 | /** 60 | * Gt tab data current page. 61 | */ 62 | static readonly GET_TAB_DATA_CURRENT_PAGE: number = 1; 63 | 64 | /** 65 | * Http request success status code. 66 | */ 67 | static readonly HTTP_CODE_200: number = 200; 68 | static readonly HTTP_CODE_0: number = 0; 69 | /** 70 | * The animation delay time. 71 | */ 72 | static readonly DELAY_ANIMATION_DURATION: number = 300; 73 | 74 | /** 75 | * The delay time. 76 | */ 77 | static readonly DELAY_TIME: number = 1000; 78 | 79 | /** 80 | * The animation duration. 81 | */ 82 | static readonly ANIMATION_DURATION: number = 2000; 83 | 84 | /** 85 | * The http timeout duration. 86 | */ 87 | static readonly HTTP_READ_TIMEOUT: number = 10000; 88 | 89 | /** 90 | * Content maxLine. 91 | */ 92 | static readonly CONTENT_MAX_LINE: number = 3; 93 | 94 | /** 95 | * List space. 96 | */ 97 | static readonly LIST_SPACE: number = 12; 98 | 99 | /** 100 | * Item img space. 101 | */ 102 | static readonly ITEM_IMG_SPACE: number = 8; 103 | 104 | /** 105 | * Type font weight. 106 | */ 107 | static readonly TYPE_FONT_WEIGHT: number = 700; 108 | 109 | /** 110 | * Title font weight. 111 | */ 112 | static readonly TITLE_FONT_WEIGHT: number = 500; 113 | 114 | /** 115 | * Desc font weight. 116 | */ 117 | static readonly DESC_FONT_WEIGHT: number = 400; 118 | 119 | /** 120 | * Type aspect ratio. 121 | */ 122 | static readonly TYPE_ASPECT_RATIO: number = 2; 123 | 124 | /** 125 | * Desc opacity. 126 | */ 127 | static readonly DESC_OPACITY: number = 0.6; 128 | 129 | /** 130 | * 100 percent. 131 | */ 132 | static readonly FULL_PERCENT: string = '100%'; 133 | 134 | /** 135 | * Divider width. 136 | */ 137 | static readonly DIVIDER_WIDTH: string = '90%'; 138 | 139 | /** 140 | * Release title. 141 | */ 142 | static readonly RELEASE_TITLE: string = '新闻发布'; 143 | } 144 | 145 | /** 146 | * The RefreshConstant constants. 147 | */ 148 | export const enum RefreshConstant { 149 | DELAY_PULL_DOWN_REFRESH = 50, 150 | CLOSE_PULL_DOWN_REFRESH_TIME = 150, 151 | DELAY_SHRINK_ANIMATION_TIME = 500 152 | } 153 | 154 | /** 155 | * The refresh state enum. 156 | */ 157 | export const enum RefreshState { 158 | DropDown = 0, 159 | Release = 1, 160 | Refreshing = 2, 161 | Success = 3, 162 | Fail = 4 163 | } 164 | 165 | /** 166 | * The newsList state enum. 167 | */ 168 | export const enum PageState { 169 | Loading = 0, 170 | Success = 1, 171 | Fail = 2 172 | } 173 | 174 | /** 175 | * The file upload state enum. 176 | */ 177 | export const enum UploadingState { 178 | COMPLETE = 'complete', 179 | FAIL = 'fail' 180 | } 181 | 182 | /** 183 | * The request method enum. 184 | */ 185 | export const enum RequestMethod { 186 | POST = 'POST', 187 | GET = 'GET' 188 | } 189 | 190 | /** 191 | * The request content type enum. 192 | */ 193 | export const enum ContentType { 194 | JSON = 'application/json', 195 | FORM = 'multipart/form-data', 196 | FORM_URLENCODED = 'application/x-www-form-urlencoded' 197 | } -------------------------------------------------------------------------------- /entry/src/main/ets/api/HttpRequest.ets: -------------------------------------------------------------------------------- 1 | import HttpConfig from './HttpConfig'; 2 | import { httpRequestGet, httpRequestPost } from './HttpService'; 3 | import ResponseResult from './ResponseResult'; 4 | 5 | /** 6 | * @desc:Http网络请求 7 | */ 8 | 9 | //1-home(首页) 10 | const bannerReq = ():Promise => httpRequestGet(HttpConfig.bannerUrl); 11 | const articleTopReq = ():Promise => httpRequestGet(HttpConfig.articleTopUrl); 12 | const articleListReq = (page: number):Promise => httpRequestGet(`${HttpConfig.articleListUrl}/${page}/json`); 13 | 14 | //2-nav(导航) 15 | const treeReq = ():Promise => httpRequestGet(HttpConfig.treeUrl); 16 | const treeListReq = (page: number, cid: number):Promise => httpRequestGet(`${HttpConfig.treeListUrl}/${page}/json?cid=${cid}`); 17 | 18 | //3-project(项目) 19 | const projectReq = ():Promise => httpRequestGet(HttpConfig.projectUrl); 20 | const projectListReq = (page: number, cid: number):Promise => httpRequestGet(`${HttpConfig.projectListUrl}/${page}/json?cid=${cid}`); 21 | 22 | //4-msg(消息) 23 | const msgUnReadReq = ():Promise =>httpRequestGet(HttpConfig.msgUnreadUrl); 24 | const msgReadReq = (page: number):Promise=>httpRequestGet(`${HttpConfig.msgReadUrl}/${page}/json`); 25 | 26 | //5-me(我的) 27 | 28 | const userReq = ():Promise => httpRequestGet(HttpConfig.userInfUrl); 29 | 30 | 31 | //6-login(登录)、register(注册) 32 | const loginReq = (username:string,password:string):Promise =>httpRequestPost(`${HttpConfig.loginUrl}?username=${username}&password=${password}`,Object({'username':username,"password":password})); 33 | const registerReq = (username:string,password:string,repassword:string):Promise =>httpRequestPost(`${HttpConfig.registerUrl}?username=${username}&password=${password}&repassword=${repassword}`,Object({'username':username,'password':password,'repassword':repassword})); 34 | const logoutReq = ():Promise => httpRequestGet(HttpConfig.logoutUrl); 35 | 36 | //7-排行榜(rank)、积分(coin-需登录) 37 | const rankReq = (page: number):Promise=>httpRequestGet(`${HttpConfig.rankUrl}/${page}/json`); 38 | const coinReq = (page: number):Promise=>httpRequestGet(`${HttpConfig.coinUrl}/${page}/json`); 39 | 40 | export { 41 | bannerReq, articleTopReq, articleListReq, 42 | treeReq,treeListReq, 43 | projectReq,projectListReq, 44 | msgUnReadReq,msgReadReq, 45 | userReq, 46 | loginReq,registerReq,logoutReq, 47 | rankReq,coinReq 48 | } -------------------------------------------------------------------------------- /entry/src/main/ets/api/HttpService.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | * @desc:网络请求工具类 15 | */ 16 | 17 | import http from '@ohos.net.http'; 18 | import ResponseResult from './ResponseResult'; 19 | import HttpConfig, { ContentType } from './HttpConfig'; 20 | import Constants from '../common/constants/Constants'; 21 | import preferences from '@ohos.data.preferences'; 22 | import * as Utils from "../utils/Utils"; 23 | import PreferencesUtils from '../utils/PreferencesUtils'; 24 | PersistentStorage.PersistProp(Constants.Cookie, ''); 25 | /** 26 | * Initiate an HTTP GET request to the specified URL. 27 | * 28 | * @param url URL for initiating an HTTP request. 29 | */ 30 | export function httpRequestGet(url: string) { 31 | return httpRequest(url, http.RequestMethod.GET); 32 | } 33 | 34 | /** 35 | * Initiate an HTTP POST request to the specified URL. 36 | * 37 | * @param url URL for initiating an HTTP request 38 | * @param newsData Additional data of the request 39 | * @returns 40 | */ 41 | export function httpRequestPost(url: string, params: Object) { 42 | return httpRequest(url, http.RequestMethod.POST, params); 43 | } 44 | 45 | /** 46 | * Initiates an HTTP request to a given URL. 47 | * 48 | * @param url URL for initiating an HTTP request 49 | * @param method Request method. 50 | * @param extraData Additional data of the request. 51 | * @returns Returns {@link ResponseResult}. 52 | * @desc:说明:玩安卓官网的post请求使用extraData传递数据时,后台无法收到,故使用拼接的方式。其他平台的接口extraData可以传递数据。具体原因待研究 53 | */ 54 | 55 | 56 | function httpRequest(url: string, method: http.RequestMethod, params?: object): Promise { 57 | 58 | let httpRequest = http.createHttp(); 59 | let cookie1 = 'loginUserName_wanandroid_com=12345678;token_pass=08937b953a34ae42f65e461149b23129'; 60 | let cookie3 = 'loginUserName_wanandroid_com=12345678;token_pass_wanandroid_com=08937b953a34ae42f65e461149b23129; Domain=wanandroid.com; Expires=Mon, 18-Dec-2023 08:20:48 GMT; Path=/' 61 | let cookie2 = 'loginUserName_wanandroid_com=12345678; token_pass_wanandroid_com=08937b953a34ae42f65e461149b23129; JSESSIONID=08D694B58BA92A81B0FA4648A9AA8E6E; loginUserName=12345678; token_pass=08937b953a34ae42f65e461149b23129'; 62 | let cookie = PreferencesUtils.getStringData(Constants.Cookie) 63 | httpRequest.on('headersReceive', (header) => { 64 | //header['set-cookie'] = cookie; 65 | //header['Cookie'] = PreferencesUtils.getStringData(Constants.Cookie) 66 | //console.info('header: ' + JSON.stringify(header)); 67 | }); 68 | 69 | let responseResult = httpRequest.request(url, { 70 | method: method, 71 | extraData: params, 72 | //header: { 'Content-Type': method === http.RequestMethod.POST ? ContentType.FORM_URLENCODED : ContentType.JSON }, 73 | header: { 74 | 'Content-Type': ContentType.JSON, 75 | //'Cookie': PreferencesUtils.getStringData(Constants.Cookie), 76 | //'Cookie': cookie, 77 | 'Cookie': AppStorage. Get(Constants.Cookie), 78 | }, 79 | readTimeout: HttpConfig.HTTP_READ_TIMEOUT, 80 | connectTimeout: HttpConfig.HTTP_READ_TIMEOUT, 81 | }); 82 | let serverData = new ResponseResult(); 83 | 84 | // Processes the data and returns. 85 | return responseResult.then((value: http.HttpResponse) => { 86 | if (value.responseCode === HttpConfig.HTTP_CODE_200) { 87 | // Obtains the returned data. 88 | let result = `${value.result}`; 89 | if (url.includes("login") && !Utils.isEmpty(value.header['set-cookie']) || url.includes("register") && !Utils.isEmpty(value.header['set-cookie'])) { 90 | AppStorage.Set(Constants.Cookie, `loginUserName_wanandroid_com=${params?['username']:''};` + value.header['set-cookie']) 91 | //AppStorage.SetOrCreate('Cookie', `loginUserName_wanandroid_com=${params['username']};` + value.header['set-cookie']) 92 | //PersistentStorage.PersistProp('Cookie', `loginUserName_wanandroid_com=${params['username']};`+value.header['set-cookie']); 93 | //PreferencesUtils.putStringData(Constants.Cookie, `loginUserName_wanandroid_com=${params['username']};`+value.header['set-cookie']) 94 | } 95 | let resultJson: ResponseResult = JSON.parse(result); 96 | if (resultJson.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 97 | serverData.data = resultJson.data; 98 | } 99 | serverData.errorCode = resultJson.errorCode; 100 | serverData.errorMsg = resultJson.errorMsg; 101 | } else { 102 | serverData.errorMsg = `${$r('app.string.http_error_message')}&${value.responseCode}`; 103 | } 104 | return serverData; 105 | }).catch(() => { 106 | serverData.errorMsg = $r('app.string.http_error_message'); 107 | return serverData; 108 | }); 109 | } 110 | -------------------------------------------------------------------------------- /entry/src/main/ets/api/ResponseResult.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:网络请求返回结果 3 | */ 4 | export default class ResponseResult { 5 | /** 6 | * Code returned by the network request: success, fail. 7 | */ 8 | errorCode: number; 9 | 10 | /** 11 | * Message returned by the network request. 12 | */ 13 | errorMsg: string | Resource; 14 | 15 | /** 16 | * Data returned by the network request. 17 | */ 18 | data: string | Object | ArrayBuffer; 19 | 20 | constructor() { 21 | this.errorCode = 0; 22 | this.errorMsg = ''; 23 | this.data = ''; 24 | } 25 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/ArticleBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:1-首页-置顶文章/列表文章 3 | */ 4 | //置顶文章 5 | export class ArticleTop { 6 | data: Article[] = []; 7 | } 8 | 9 | //文章列表 10 | export class ArticleList { 11 | curPage: number = 0; 12 | datas: Article[] = []; 13 | offset: number = 0; 14 | over: boolean = false; 15 | pageCount: number = 0; 16 | size: number = 0; 17 | total: number = 0; 18 | } 19 | 20 | 21 | export class Article { 22 | isTop:boolean = false; //是否为置顶 23 | adminAdd: boolean = false; //是否为管理员添加 24 | apkLink: string = ''; //apk链接 25 | audit: number = 0; 26 | author: string = ''; //作者 27 | canEdit: boolean = false; 28 | chapterId: number = 0; 29 | chapterName: string = ''; //chapter 30 | collect: boolean = false; 31 | courseId: number = 0; 32 | desc: string = ''; 33 | descMd: string = ''; 34 | envelopePic: string = ''; 35 | fresh: boolean = false; 36 | host: string = ''; 37 | id: number = 0; 38 | isAdminAdd: boolean = false; 39 | link: string = ''; 40 | niceDate: string = ''; 41 | niceShareDate: string = ''; 42 | origin: string = ''; 43 | prefix: string = ''; 44 | projectLink: string = ''; 45 | publishTime: number = 0; 46 | realSuperChapterId: number = 0; 47 | selfVisible: number = 0; 48 | shareDate: number = 0; 49 | shareUser: number = 0; 50 | superChapterId: number = 0; 51 | superChapterName: string = ''; 52 | tags: Tag[] = []; 53 | title: string = ''; 54 | type: number = 0; 55 | userId: number = 0; 56 | visible: number = 0; 57 | zan: number = 0; 58 | } 59 | 60 | export class Tag { 61 | name: string = ''; 62 | url: string = ''; 63 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/BannerBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:1-首页-轮播图 3 | */ 4 | 5 | export class Banner { 6 | desc: string = ''; //描述 7 | id: number = 0; //id 8 | imagePath: string = ''; //图片地址 9 | isVisible: number = 0; //是否可见 10 | order: number = 0; 11 | title: string = ''; //标题 12 | type: number = 0; //type类型 13 | url: string = ''; //文章地址 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /entry/src/main/ets/bean/CoinBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * 积分排行-数据类 3 | */ 4 | 5 | export class CoinData { 6 | curPage: number = 0; 7 | datas: CoinItem[] = []; 8 | offset: number = 0; 9 | over: boolean = false; 10 | pageCount: number = 0; 11 | size: number = 0; 12 | total: number = 0; 13 | } 14 | 15 | 16 | export class CoinItem { 17 | coinCount: number = 0; 18 | date: number = 0; 19 | desc: string = ''; 20 | id: number = 0; 21 | reason: string = ''; 22 | type: number = 0; 23 | userId: number = 0; 24 | username: string = ''; 25 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/MsgBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:4-消息 3 | */ 4 | 5 | export class MsgData { 6 | curPage: number = 0; 7 | datas: MsgItem[] = []; 8 | offset: number = 0; 9 | over: boolean = false; 10 | pageCount: number = 0; 11 | size: number = 0; 12 | total: number = 0; 13 | } 14 | 15 | export class MsgItem { 16 | category: number = 0; 17 | date: number = 0; 18 | fromUser: string = ''; //作者 19 | fromUserId: number = 0; 20 | fullLink: string = ''; //链接 21 | id: number = 0; 22 | isRead: number = 0; 23 | link: string = ''; 24 | message: string = ''; //消息内容 25 | niceDate: string = ''; //日期 26 | tag: string = ''; //标签 27 | title: string = ''; //标题 28 | userId: number = 0; 29 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/ProjectBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:4-项目 3 | */ 4 | 5 | export class ProjectData { 6 | data: ProjectItem[] = []; 7 | } 8 | 9 | export class ProjectItem { 10 | articleList: [] = []; 11 | author: string = ''; 12 | children: ProjectItem[] = []; 13 | courseId: number = 0; 14 | cover: string = ''; 15 | desc: string = ''; 16 | id: number = 0; 17 | lisense: string = ''; 18 | lisenseLink: string = ''; 19 | name: string = ''; //显示的tab标题名字 20 | order: number = 0; 21 | parentChapterId: number = 0; 22 | type: number = 0; 23 | userControlSetTop: boolean = false; 24 | visible: number = 0; 25 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/RankBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * 积分排行-数据类 3 | */ 4 | 5 | export class RankData { 6 | curPage: number = 0; 7 | datas: RankItem[] = []; 8 | offset: number = 0; 9 | over: boolean = false; 10 | pageCount: number = 0; 11 | size: number = 0; 12 | total: number = 0; 13 | } 14 | 15 | 16 | export class RankItem { 17 | coinCount: number = 0; 18 | level: number = 0; 19 | nickname: string = ''; 20 | rank: string = ''; 21 | userId: number = 0; 22 | username: string = ''; 23 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/SettingItem.ets: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * List item data entity. 5 | */ 6 | export default class PageResource { 7 | /** 8 | * Text of list item. 9 | */ 10 | title: Resource; 11 | /** 12 | * Image of list item. 13 | */ 14 | img: Resource; 15 | /** 16 | * Other resource of list item. 17 | */ 18 | others?: Resource; 19 | 20 | constructor(title: Resource, img: Resource, others?: Resource) { 21 | this.title = title; 22 | this.img = img; 23 | this.others = others; 24 | } 25 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/TreeBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:2-Tree(导航)-数据类 3 | */ 4 | import { Article } from './ArticleBean'; 5 | 6 | //体系数据 7 | export class TreeData { 8 | data: TreeItem[] = []; 9 | } 10 | 11 | export class TreeItem { 12 | articleList: [] = []; 13 | author: string = ''; 14 | children: TreeItem[] = []; 15 | courseId: number = 0; 16 | cover: string = ''; 17 | desc: string = ''; 18 | id: number = 0; 19 | lisense: string = ''; 20 | lisenseLink: string = ''; 21 | name: string = ''; 22 | order: number = 0; 23 | parentChapterId: number = 0; 24 | type: number = 0; 25 | userControlSetTop: boolean = false; 26 | visible: number = 0; 27 | } 28 | //知识体系下的文章 29 | export class TreeArticle { 30 | curPage: number = 1; 31 | offset: number = 0; 32 | over: boolean = false; 33 | pageCount: number = 0; 34 | size: number = 0; 35 | total: number = 0; 36 | datas: Article[] = []; 37 | } -------------------------------------------------------------------------------- /entry/src/main/ets/bean/UserInfoBean.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:5-用户数据 3 | */ 4 | 5 | export class UserData { 6 | coinInfo: CoinInfo = new CoinInfo(); 7 | userInfo: UserInfo = new UserInfo() 8 | } 9 | 10 | export class UserInfo { 11 | admin: boolean = false; 12 | chapterTops: [] = []; 13 | coinCount: number = 0; 14 | collectIds: [] = []; 15 | email: string = ''; 16 | icon: string = ''; 17 | id: number = 0; 18 | nickname: string = ''; 19 | password: string = ''; 20 | publicName: string = ''; 21 | token: string = ''; 22 | type: number = 0; 23 | username: string = ''; 24 | } 25 | 26 | export class CoinInfo { 27 | coinCount: number = 0; // 可用 28 | level: number = 0; // 可用 29 | nickname: string = ''; 30 | rank: string = ''; // 可用 31 | userId: number = 0; // 可用 32 | username: string = ''; 33 | } -------------------------------------------------------------------------------- /entry/src/main/ets/common/bean/BottomTabsItem.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | /** 17 | * Interface of bottom tab item. 18 | */ 19 | export interface BottomTabsItem { 20 | icon: Resource; 21 | iconSelected: Resource; 22 | text: Resource; 23 | } -------------------------------------------------------------------------------- /entry/src/main/ets/common/constants/Constants.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:定义常量 3 | */ 4 | import SettingItem from '../../bean/SettingItem'; 5 | 6 | export default class Constants { 7 | 8 | /** 9 | * route path 10 | */ 11 | static readonly Splash_Path: string = 'pages/Splash'; 12 | static readonly MainPage_Path: string = 'pages/MainPage'; 13 | static readonly LoginRegister_Path: string = 'pages/LoginRegisterPage'; 14 | static readonly WebPage_Path: string = 'pages/WebPage'; 15 | static readonly SettingPage_Path: string = 'pages/SettingPage'; 16 | static readonly TreeTabPage_Path: string = 'pages/TreeTabPage'; 17 | static readonly InfoPage_Path: string = 'pages/InfoPage'; 18 | 19 | 20 | /** 21 | * Component size. 22 | */ 23 | static readonly ICON_SIZE: string = '40%'; 24 | static readonly FULL_PERCENT: string = '100%'; 25 | static readonly BAR_HEIGHT: string = '10%'; 26 | static readonly FULL_SIZE: string = '100%'; 27 | 28 | 29 | /** 30 | * Component location. 31 | */ 32 | static readonly PERCENTAGE_15: string = '15%'; 33 | static readonly PERCENTAGE_25: string = '25%'; 34 | /** 35 | * Component constants. 36 | */ 37 | static readonly OPACITY: number = 0.6; 38 | static readonly BORDER_WIDTH: number = 0.5; 39 | 40 | /** 41 | * BottomTabIndex 42 | */ 43 | static readonly HOME_TAB_INDEX = 0; 44 | static readonly NAV_TAB_INDEX = 1; 45 | static readonly PROJECT_TAB_INDEX = 2; 46 | static readonly MSG_TAB_INDEX = 3; 47 | static readonly MINE_TAB_INDEX = 4; 48 | 49 | 50 | /** 51 | * WebView 52 | */ 53 | static readonly WebConstant_PROGRESS_MAX: number = 100; 54 | static readonly WebConstant_PROGRESS_MIN: number = 0; 55 | static readonly WebConstant_PROGRESS_STEP: number = 10; 56 | static readonly WebConstant_MILLI_SECONDS: number = 100; 57 | static readonly WebConstant_DURATION: number = 3000; 58 | static readonly WebConstant_PROGRESS_WIDTH: number = 80; 59 | static readonly WebConstant_PROGRESS_STROKE_WIDTH: number = 15; 60 | static readonly WebConstant_PROGRESS_SCALE_COUNT: number = 15; 61 | static readonly WebConstant_PROGRESS_SCALE_WIDTH: number = 5; 62 | static readonly WebConstant_PROGRESS_POSITION_X: string = '40%'; 63 | static readonly WebConstant_PROGRESS_POSITION_Y: string = '40%'; 64 | /** 65 | * The width or height of the component is spread across the parent component. 66 | */ 67 | static readonly FULL_PARENT = '100%'; 68 | 69 | /** 70 | * Shared transition duration. 71 | */ 72 | static readonly SHARED_DURATION: number = 800; 73 | /** 74 | * Page transition exit delay. 75 | */ 76 | static readonly EXIT_DELAY: number = 100; 77 | /** 78 | * Type font weight. 79 | */ 80 | static readonly TYPE_FONT_WEIGHT: number = 700; 81 | /** 82 | * Desc font weight. 83 | */ 84 | static readonly DESC_FONT_WEIGHT: number = 400; 85 | /** 86 | * Left padding of the input box 87 | */ 88 | static readonly INPUT_PADDING_LEFT = 0; 89 | /** 90 | * Delay time of simulated login 91 | */ 92 | static readonly LOGIN_DELAY_TIME = 2000; 93 | /** 94 | * Input length of the account. 95 | */ 96 | static readonly INPUT_ACCOUNT_LENGTH = 16; 97 | /** 98 | * Input length of the password. 99 | */ 100 | static readonly INPUT_PASSWORD_LENGTH = 16; 101 | /** 102 | * The width of button 103 | */ 104 | static readonly BUTTON_WIDTH = '90%'; 105 | static readonly SET_LIST_WIDTH = '42%'; 106 | 107 | /** 108 | * 常量 109 | */ 110 | static readonly RANK = 'rank'; 111 | static readonly COLLECT = 'collect'; 112 | static readonly LEVEL = 'level'; 113 | static readonly COIN = 'coin'; 114 | 115 | /** 116 | * Preference-key 117 | */ 118 | static readonly Cookie: string = 'cookie'; 119 | static readonly settingListData: SettingItem[] = [ 120 | new SettingItem($r('app.string.setting_list_theme'), $r('app.media.ic_theme'), $r(null)), 121 | new SettingItem($r('app.string.setting_list_language'), $r('app.media.ic_language'), $r(null)), 122 | new SettingItem($r('app.string.setting_list_about'), $r('app.media.ic_about'), $r(null)), 123 | ]; 124 | } -------------------------------------------------------------------------------- /entry/src/main/ets/entryability/EntryAbility.ets: -------------------------------------------------------------------------------- 1 | import UIAbility from '@ohos.app.ability.UIAbility'; 2 | import hilog from '@ohos.hilog'; 3 | import window from '@ohos.window'; 4 | import Constants from '../common/constants/Constants'; 5 | import AbilityConstant from '@ohos.app.ability.AbilityConstant'; 6 | import Want from '@ohos.app.ability.Want'; 7 | 8 | export default class EntryAbility extends UIAbility { 9 | //api10版本 10 | onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 11 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 12 | globalThis.abilityContext = this.context; 13 | } 14 | //api9版本 15 | // onCreate(want, launchParam) { 16 | // hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 17 | // globalThis.abilityContext = this.context; 18 | // //初始化不要放在这里 19 | // // if (!AppStorage.Has(Constants.Cookie)) { 20 | // // PersistentStorage.PersistProp('Cookie', ''); 21 | // // } 22 | // } 23 | // 24 | 25 | onDestroy() { 26 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 27 | } 28 | 29 | onWindowStageCreate(windowStage: window.WindowStage) { 30 | // Main window is created, set main page for this ability 31 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 32 | 33 | windowStage.loadContent(Constants.Splash_Path, (err, data) => { 34 | if (err.code) { 35 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 36 | return; 37 | } 38 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 39 | }); 40 | } 41 | 42 | onWindowStageDestroy() { 43 | // Main window is destroyed, release UI related resources 44 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 45 | } 46 | 47 | onForeground() { 48 | // Ability has brought to foreground 49 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 50 | } 51 | 52 | onBackground() { 53 | // Ability has back to background 54 | hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /entry/src/main/ets/pages/HomePage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import { Article, ArticleList, ArticleTop } from '../bean/ArticleBean'; 3 | import { Banner } from '../bean/BannerBean'; 4 | import ArticleItem from '../view/ArticleItem'; 5 | import HomeViewModel from '../viewmodel/HomeViewModel'; 6 | import { NavigatorModel } from '../viewmodel/NavigatorModel'; 7 | import { PullToRefresh } from '@ohos/pulltorefresh' 8 | import * as Utils from "../utils/Utils"; 9 | import prompt from '@ohos.promptAction'; 10 | import Constants from '../common/constants/Constants'; 11 | 12 | /** 13 | * @desc:1-首页 14 | */ 15 | @Preview 16 | @Component 17 | export default struct HomePage { 18 | private swiperController: SwiperController = new SwiperController(); 19 | @State bannerData: Banner[] = []; 20 | @State articleTopData: Article[] = []; 21 | @State articleListData: ArticleList = new ArticleList() 22 | @State articleData: Article[] = []; 23 | @State pageNum: number = 0; //分页page 24 | private scroller: Scroller = new Scroller(); // 需绑定列表或宫格组件 25 | 26 | //页面即将显示时 27 | async aboutToAppear() { 28 | //1-首页全部接口数据 29 | this.getHomeBannerData() 30 | //2-首页-文章接口数据 31 | //this.getHomeArticleListData(0) 32 | } 33 | 34 | //1-首页全部接口数据 35 | getHomeBannerData() { 36 | //1-首页全部接口数据 37 | HomeViewModel.getHomeData().then(result => { 38 | this.bannerData = result[0] as Banner[] 39 | this.articleTopData = (result[1] as Article[]) 40 | this.articleTopData.forEach(item => { 41 | item.isTop = true 42 | }) 43 | this.articleListData = result[2] 44 | 45 | this.articleData = [...this.articleTopData, ...this.articleListData.datas] 46 | console.log("result") 47 | }) 48 | } 49 | 50 | //2-首页-文章接口数据 51 | getHomeArticleListData(page: number) { 52 | //2-首页-文章接口数据 53 | HomeViewModel.getArticle(page).then(result => { 54 | this.articleData.push(...result.datas) 55 | // result.datas.forEach((article, index) => { 56 | // console.log('article>>>', article) 57 | // this.articleData.push(article) 58 | // }) 59 | console.log('article>>>', JSON.stringify(result)) 60 | }) 61 | } 62 | 63 | @Builder 64 | private homeListView() { 65 | //Article 66 | List({ scroller: this.scroller }) { 67 | //Banner轮播图 68 | ListItem() { 69 | if (this.bannerData.length > 0) { 70 | Swiper(this.swiperController) { 71 | ForEach(this.bannerData, (banner: Banner) => { 72 | Navigator({ target: Constants.WebPage_Path, type: NavigationType.Push }) { 73 | Image(banner.imagePath) 74 | }.params({ path: banner.url, title: banner.title } as NavigatorModel) 75 | }, (img: Resource) => JSON.stringify(img.id)) 76 | }.height(200).width('100%') 77 | .autoPlay(true) 78 | } 79 | } 80 | 81 | //文章条目 82 | ForEach(this.articleData, (article: Article) => { 83 | ListItem() { 84 | Navigator({ target: Constants.WebPage_Path, type: NavigationType.Push }) { 85 | ArticleItem({ article: article }) 86 | }.params({ path: article.link, title: article.title } as NavigatorModel) 87 | } 88 | }) 89 | } 90 | .edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果 91 | } 92 | 93 | build() { 94 | Column() { 95 | PullToRefresh({ 96 | data: this.articleData, 97 | scroller: this.scroller, 98 | customList: () => this.homeListView(), 99 | onRefresh: () => { 100 | return new Promise((resolve, reject) => { 101 | setTimeout(() => { 102 | resolve('刷新成功'); 103 | this.pageNum = 0; 104 | this.articleData.forEach((item, index) => { 105 | this.articleData.pop() 106 | }) 107 | this.getHomeArticleListData(this.pageNum) 108 | }, 1000); 109 | }); 110 | }, 111 | onLoadMore: () => { 112 | return new Promise((resolve, reject) => { 113 | setTimeout(() => { 114 | resolve('onLoadMore'); 115 | if (!this.articleListData.over) { 116 | this.getHomeArticleListData(++this.pageNum) 117 | } else { 118 | prompt.showToast({ message: $r('app.string.no_more_data') }) 119 | } 120 | }, 1000); 121 | }); 122 | }, 123 | customLoad: null, 124 | customRefresh: null, 125 | }) 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/InfoPage.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * 积分排行或个人积分页面 3 | */ 4 | import router from '@ohos.router'; 5 | import { CoinData, CoinItem } from '../bean/CoinBean'; 6 | import { RankData, RankItem } from '../bean/RankBean'; 7 | import Constants from '../common/constants/Constants'; 8 | import InfoItem from '../view/InfoItem'; 9 | import TitleBar from '../view/TitleBar' 10 | import InfoViewModel from '../viewmodel/InfoViewModel'; 11 | 12 | @Preview 13 | @Entry 14 | @Component 15 | struct InfoPage { 16 | @State params: object = router.getParams(); 17 | @State title: string = ''; 18 | //积分数据 19 | @State coinData: CoinData = new CoinData; 20 | @State coinListData: CoinItem[] = []; 21 | //排行榜数据 22 | @State rankData: RankData = new RankData; 23 | @State rankListData: RankItem[] = []; 24 | 25 | aboutToAppear() { 26 | switch (this.params['title']) { 27 | case Constants.RANK: 28 | this.title = '积分排行榜'; 29 | this.getRankData(1) 30 | 31 | break; 32 | case Constants.COIN: 33 | this.title = '个人积分列表'; 34 | this.getCoinData(1) 35 | break; 36 | default: 37 | this.title = '其他'; 38 | break; 39 | } 40 | } 41 | 42 | getRankData(page: number) { 43 | InfoViewModel.getRank(1).then((rankData: RankData) => { 44 | this.rankData = rankData; 45 | this.rankListData = rankData.datas 46 | }) 47 | } 48 | 49 | getCoinData(page: number) { 50 | InfoViewModel.getCoin(1).then((coinData: CoinData) => { 51 | this.coinData = coinData; 52 | this.coinListData = coinData.datas; 53 | }) 54 | } 55 | 56 | @Builder 57 | listRank() { 58 | List({ space: 10 }) { 59 | ForEach(this.rankListData, (item: RankItem, index) => { 60 | ListItem() { 61 | InfoItem({ 62 | param1: `排名:${item.rank}`, 63 | param2: `用户名:${item.username}`, 64 | param3: `用户等级:${item.level}`, 65 | param4: `用户积分:${item.coinCount}`, 66 | }) 67 | } 68 | }) 69 | } 70 | } 71 | 72 | @Builder 73 | listCoin() { 74 | List({ space: 10 }) { 75 | ForEach(this.coinListData, (item: CoinItem, index) => { 76 | ListItem() { 77 | InfoItem({ 78 | param1: `类型:${item.reason}`, 79 | param2: `获得积分:${item.coinCount}`, 80 | param3: `用户等级:${item.desc}`, 81 | }) 82 | } 83 | }) 84 | } 85 | } 86 | 87 | build() { 88 | Column() { 89 | //1-头部 90 | TitleBar({ title: this.title }) 91 | if (this.params['title'] === Constants.RANK) { 92 | this.listRank() 93 | } else if (this.params['title'] === Constants.COIN) { 94 | this.listCoin() 95 | } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/LoginRegisterPage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import router from '@ohos.router'; 3 | import prompt from '@ohos.promptAction'; 4 | import Constants from '../common/constants/Constants'; 5 | import LoginRegisterViewModel from '../viewmodel/LoginRegisterViewModel'; 6 | import { UserInfo } from '../bean/UserInfoBean'; 7 | import * as Utils from "../utils/Utils"; 8 | import ResponseResult from '../api/ResponseResult'; 9 | /** 10 | * @desc: 6-登录注册页面 11 | */ 12 | 13 | @Extend(TextInput) function inputStyle() { 14 | .placeholderColor($r('app.color.gray')) 15 | .height($r('app.float.45vp')) 16 | .fontSize($r('app.float.18fp')) 17 | .backgroundColor($r('app.color.snow')) 18 | .width(Constants.FULL_PARENT) 19 | .padding({ left: Constants.INPUT_PADDING_LEFT }) 20 | .margin({ top: $r('app.float.12vp') }) 21 | } 22 | 23 | @Extend(Line) function lineStyle() { 24 | .width(Constants.FULL_PARENT) 25 | .height($r('app.float.1vp')) 26 | .backgroundColor($r('app.color.gray')) 27 | } 28 | 29 | @Extend(Text) function blueTextStyle() { 30 | .fontColor($r('app.color.dodger_blue')) 31 | .fontSize($r('app.float.14fp')) 32 | .fontWeight(FontWeight.Medium) 33 | } 34 | 35 | @Preview 36 | @Entry 37 | @Component 38 | export default struct LoginRegisterPage { 39 | @State username: string = ''; 40 | @State password: string = ''; 41 | @State repassword: string = ''; 42 | @State isRegister: boolean = false; 43 | @State isShowProgress: boolean = false; 44 | private timeOutId: number = -1; 45 | 46 | @Builder 47 | imageButton(src: Resource) { 48 | Button({ type: ButtonType.Circle, stateEffect: true }) { 49 | Image(src) 50 | } 51 | .height($r('app.float.48vp')) 52 | .width($r('app.float.48vp')) 53 | .backgroundColor($r('app.color.snow')) 54 | } 55 | 56 | loginAndRegister(): void { 57 | this.isRegister ? this.register() : this.login() 58 | } 59 | 60 | login() { 61 | if (this.username === '' || this.password === '') { 62 | prompt.showToast({ message: $r('app.string.input_empty_tips') }) 63 | } else { 64 | //处理登录逻辑 65 | this.isShowProgress = true; 66 | LoginRegisterViewModel.userLogin(this.username, this.password).then((result: ResponseResult) => { 67 | //console.log('UserInfo>>>', userInfo) 68 | console.log('result>>>', result) 69 | if (!Utils.isEmpty(result.data as UserInfo)) { 70 | this.delay() 71 | } else { 72 | this.isShowProgress = false; 73 | prompt.showToast({ message: result.errorMsg }) 74 | } 75 | }) 76 | } 77 | } 78 | 79 | register() { 80 | if (this.username === '' || this.password === '' || this.repassword === '') { 81 | prompt.showToast({ message: $r('app.string.input_empty_tips') }) 82 | } else { 83 | //处理注册逻辑 84 | this.isShowProgress = true; 85 | LoginRegisterViewModel.userRegister(this.username, this.password, this.repassword).then(result => { 86 | console.log('UserInfo>>>', result) 87 | if (!Utils.isEmpty(result.data as UserInfo)) { 88 | this.delay() 89 | } else { 90 | this.isShowProgress = false; 91 | prompt.showToast({ message: result.errorMsg }) 92 | } 93 | }) 94 | } 95 | } 96 | 97 | //延迟加载 98 | delay() { 99 | if (this.timeOutId === -1) { 100 | this.timeOutId = setTimeout(() => { 101 | this.isShowProgress = false; 102 | this.timeOutId = -1; 103 | router.replaceUrl({ url: Constants.MainPage_Path }); 104 | }, Constants.LOGIN_DELAY_TIME); 105 | } 106 | } 107 | 108 | aboutToDisappear() { 109 | clearTimeout(this.timeOutId); 110 | this.timeOutId = -1; 111 | } 112 | 113 | build() { 114 | Column() { 115 | Flex({ justifyContent: FlexAlign.Start }) { 116 | Image($r('app.media.ic_arrow_left')) 117 | .size({ width: $r('app.float.30vp'), height: $r('app.float.30vp') }) 118 | .margin({ right: $r('app.float.10vp'), top: $r('app.float.10vp') }) 119 | .onClick(() => { 120 | router.back() 121 | }) 122 | } 123 | 124 | Image($r('app.media.ic_logo')) 125 | .width($r('app.float.70vp')) 126 | .height($r('app.float.70vp')) 127 | .margin({ top: $r('app.float.100vp'), bottom: $r('app.float.8vp') }) 128 | Text(!this.isRegister ? $r('app.string.login_page') : $r('app.string.register_page')) 129 | .fontSize($r('app.float.24fp')) 130 | .fontWeight(FontWeight.Medium) 131 | .fontColor($r('app.color.black')) 132 | Text(!this.isRegister ? $r('app.string.login_more') : $r('app.string.register_more')) 133 | .fontSize($r('app.float.16fp')) 134 | .fontColor($r('app.color.gray')) 135 | .margin({ bottom: $r('app.float.30vp'), top: $r('app.float.8vp') }) 136 | 137 | TextInput({ placeholder: $r('app.string.account'), text: this.username }) 138 | .maxLength(Constants.INPUT_ACCOUNT_LENGTH) 139 | //.type(InputType.Normal) 140 | .inputStyle() 141 | .onChange((value: string) => { 142 | this.username = value; 143 | }) 144 | Line().lineStyle() 145 | 146 | TextInput({ placeholder: $r('app.string.password'), text: this.password }) 147 | .maxLength(Constants.INPUT_PASSWORD_LENGTH) 148 | .type(InputType.Password) 149 | .inputStyle() 150 | .onChange((value: string) => { 151 | this.password = value; 152 | }) 153 | Line().lineStyle() 154 | 155 | 156 | //注册时-显示确认密码 157 | if (this.isRegister) { 158 | TextInput({ placeholder: $r('app.string.repassword'), text: this.repassword }) 159 | .maxLength(Constants.INPUT_PASSWORD_LENGTH) 160 | .type(InputType.Password) 161 | .inputStyle() 162 | .onChange((value: string) => { 163 | this.repassword = value; 164 | }) 165 | Line().lineStyle() 166 | } 167 | 168 | Button(!this.isRegister ? $r('app.string.login') : $r('app.string.register'), { type: ButtonType.Capsule }) 169 | .width(Constants.BUTTON_WIDTH) 170 | .height($r('app.float.40vp')) 171 | .fontSize($r('app.float.16fp')) 172 | .fontWeight(FontWeight.Medium) 173 | .backgroundColor($r('app.color.dodger_blue')) 174 | .margin({ top: $r('app.float.87vp'), bottom: $r('app.float.12vp') }) 175 | .onClick(() => { 176 | this.loginAndRegister(); 177 | }) 178 | Text(!this.isRegister ? $r('app.string.register_account') : $r('app.string.login_account')) 179 | .fontColor($r('app.color.dodger_blue')) 180 | .fontSize($r('app.float.16fp')) 181 | .fontWeight(FontWeight.Medium) 182 | .onFocus(() => { 183 | 184 | }) 185 | .onClick(() => { 186 | this.isRegister = !this.isRegister 187 | this.username = '' 188 | this.password = '' 189 | this.repassword = '' 190 | }) 191 | 192 | if (this.isShowProgress) { 193 | LoadingProgress() 194 | .color($r('app.color.slate_gray')) 195 | .width($r('app.float.30vp')) 196 | .height($r('app.float.30vp')) 197 | .margin({ top: $r('app.float.20vp') }) 198 | } 199 | 200 | } 201 | .backgroundColor($r('app.color.snow')) 202 | .height(Constants.FULL_PARENT) 203 | .width(Constants.FULL_PARENT) 204 | .padding({ 205 | left: $r('app.float.12vp'), 206 | right: $r('app.float.12vp'), 207 | bottom: $r('app.float.24vp') 208 | }) 209 | } 210 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/MainPage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import Constants from '../common/constants/Constants'; 3 | import { BottomTabsList } from '../viewmodel/BottomTabsModel'; 4 | import HomePage from './HomePage'; 5 | import MePage from './MePage'; 6 | import MsgPage from './MsgPage'; 7 | import TreePage from './TreePage'; 8 | import ProjectPage from './ProjectPage'; 9 | 10 | /** 11 | * @desc:主项目框架 12 | */ 13 | @Preview 14 | @Entry 15 | @Component 16 | struct MainPage { 17 | @State bottomTabIndex: number = 0; 18 | private tabsController: TabsController = new TabsController(); 19 | 20 | @Builder TabBuilder(index: number, _name: string) { 21 | Column() { 22 | Image(this.bottomTabIndex === index ? BottomTabsList[index].iconSelected : BottomTabsList[index].icon) 23 | .width(Constants.ICON_SIZE) 24 | .height(Constants.ICON_SIZE) 25 | .objectFit(ImageFit.Contain) 26 | 27 | Text(BottomTabsList[index].text) 28 | .fontSize($r('app.float.bottom_font_size')) 29 | .opacity(Constants.OPACITY) 30 | .fontColor(this.bottomTabIndex === index ? 31 | $r('app.color.bottom_tabs_font_color_selected') : $r('app.color.bottom_tabs_font_color')) 32 | } 33 | .width(Constants.FULL_PERCENT) 34 | .height(Constants.FULL_PERCENT) 35 | .justifyContent(FlexAlign.Center) 36 | .border({ width: { top: Constants.BORDER_WIDTH }, color: $r('app.color.color_border') }) 37 | .backgroundColor($r('app.color.bottom_tabs_background_color')) 38 | } 39 | 40 | /** 41 | * In low-code mode, do not add anything to the build function, as it will be 42 | * overwritten by the content generated by the .visual file in the build phase. 43 | */ 44 | build() { 45 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.End, justifyContent: FlexAlign.End }) { 46 | Tabs({ barPosition: BarPosition.End, index: 0, controller: this.tabsController }) { 47 | TabContent() { 48 | HomePage() 49 | }.tabBar(this.TabBuilder(0, 'Home')) 50 | 51 | TabContent() { 52 | TreePage() 53 | }.tabBar(this.TabBuilder(1, 'Nav')) 54 | 55 | TabContent() { 56 | ProjectPage() 57 | }.tabBar(this.TabBuilder(2, 'Project')) 58 | 59 | TabContent() { 60 | MsgPage() 61 | }.tabBar(this.TabBuilder(3, 'Msg')) 62 | 63 | TabContent() { 64 | MePage() 65 | }.tabBar(this.TabBuilder(4, 'Me')) 66 | 67 | } 68 | .width(Constants.FULL_PERCENT) 69 | .height(Constants.FULL_PERCENT) 70 | .vertical(false) 71 | .scrollable(false) 72 | .barMode(BarMode.Fixed) 73 | .barHeight(Constants.BAR_HEIGHT) 74 | .onChange((index: number) => { 75 | this.bottomTabIndex = index; 76 | }) 77 | } 78 | .width(Constants.FULL_PERCENT) 79 | } 80 | 81 | onIndexChange() { 82 | this.tabsController.changeIndex(this.bottomTabIndex); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /entry/src/main/ets/pages/MePage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import Constants from '../common/constants/Constants'; 3 | import MineFunc from '../view/MineFunc'; 4 | import MineHead from '../view/MineHead'; 5 | 6 | /** 7 | * @desc:5-我的 8 | */ 9 | 10 | @Preview 11 | @Component 12 | export default struct MePage { 13 | build() { 14 | Scroll() { 15 | Column() { 16 | MineHead() //我的-头部信息 17 | MineFunc() //我的-功能 18 | } 19 | .height(Constants.FULL_PERCENT) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/MsgPage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import router from '@ohos.router'; 3 | import MsgReadList from '../view/MsgReadList'; 4 | import MsgUnreadList from '../view/MsgUnreadList'; 5 | import MsgViewModel from '../viewmodel/MsgViewModel'; 6 | 7 | /** 8 | * @desc:4-消息 9 | */ 10 | @Preview 11 | @Component 12 | export default struct MsgPage { 13 | private tabsController: TabsController = new TabsController() 14 | @State currentIndex: number = 0; 15 | 16 | build() { 17 | Tabs({ barPosition: BarPosition.Start }) { 18 | TabContent() { 19 | MsgUnreadList() 20 | } 21 | .tabBar('未读消息') 22 | 23 | TabContent() { 24 | MsgReadList() 25 | } 26 | .tabBar('已读消息') 27 | }.onChange((index) => { 28 | this.currentIndex = index 29 | }) 30 | } 31 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/ProjectPage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import router from '@ohos.router'; 3 | import { ProjectItem } from '../bean/ProjectBean'; 4 | import Constants from '../common/constants/Constants'; 5 | import ProjectList from '../view/ProjectList'; 6 | import ProjectViewModel from '../viewmodel/ProjectViewModel'; 7 | 8 | /** 9 | * @desc:3-项目 10 | */ 11 | @Preview 12 | @Component 13 | export default struct ProjectPage { 14 | @State projectData: ProjectItem[] = []; 15 | @State currentIndex: number = 0; 16 | @State optionsDic: Map = new Map(); 17 | //页面即将显示时 18 | aboutToAppear() { 19 | //1-项目分类 20 | ProjectViewModel.getProject().then((result: ProjectItem[]) => { 21 | console.log('project>>>', result) 22 | this.projectData = result 23 | this.projectData.forEach((project, index) => { 24 | this.optionsDic[index] = project.id //将tab_index对应的cid放到map中 25 | }) 26 | }) 27 | } 28 | 29 | //自定义Tab 30 | @Builder TabBuilder(index: number) { 31 | Column() { 32 | Text(this.projectData[index].name) 33 | .height(Constants.FULL_PERCENT) 34 | .fontSize(this.currentIndex === index ? $r('app.float.24fp') : $r('app.float.18fp')) 35 | .fontWeight(this.currentIndex === index ? Constants.TYPE_FONT_WEIGHT : Constants.DESC_FONT_WEIGHT) 36 | .fontColor($r('app.color.black')) 37 | } 38 | .padding({ left: $r('app.float.8vp'), right: $r('app.float.8vp') }) 39 | .margin({ 40 | left: index === 0 ? $r('app.float.8vp') : 0, 41 | right: index === this.projectData.length - 1 ? $r('app.float.8vp') : 0 42 | }) 43 | } 44 | 45 | build() { 46 | Tabs({ barPosition: BarPosition.Start }) { 47 | ForEach(this.projectData, (projectItem: ProjectItem, index?: number) => { 48 | TabContent() { 49 | ProjectList({ index, optionsDic: this.optionsDic, currentIndex: $currentIndex }) 50 | }.tabBar(this.TabBuilder(index?index:0)) 51 | }, (tabItem: ProjectItem) => JSON.stringify(tabItem)) 52 | } 53 | .barHeight($r('app.float.50vp')) 54 | .height(Constants.FULL_PERCENT) 55 | .barMode(BarMode.Scrollable) 56 | .onChange((index: number) => { 57 | this.currentIndex = index; 58 | }) 59 | .vertical(false) 60 | } 61 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/SettingPage.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:7-设置页面 3 | */ 4 | import router from '@ohos.router' 5 | import SettingItem from '../bean/SettingItem' 6 | import Constants from '../common/constants/Constants' 7 | import TitleBar from '../view/TitleBar' 8 | import SettingViewModel from '../viewmodel/SettingViewModel' 9 | 10 | @Preview 11 | @Entry 12 | @Component 13 | export default struct SettingPage { 14 | @Builder settingCell(item: SettingItem) { 15 | Row() { 16 | Row({ space: 12 }) { 17 | Image(item.img) 18 | .width($r('app.float.22vp')) 19 | .height($r('app.float.22vp')) 20 | Text(item.title) 21 | .fontSize($r('app.float.16fp')) 22 | } 23 | 24 | if (item.others === null) { 25 | Image($r('app.media.ic_arrow_right')) 26 | .width($r('app.float.12vp')) 27 | .height($r('app.float.24vp')) 28 | } else { 29 | Toggle({ type: ToggleType.Switch, isOn: false }) 30 | } 31 | } 32 | .justifyContent(FlexAlign.SpaceBetween) 33 | .width(Constants.FULL_PERCENT) 34 | .padding({ 35 | left: $r('app.float.8vp'), 36 | right: $r('app.float.22vp') 37 | }) 38 | } 39 | 40 | build() { 41 | Column() { 42 | //1-头部 43 | TitleBar({ title: '设置' }) 44 | //2-设置 45 | List() { 46 | ForEach(SettingViewModel.getSettingListData(), (item: SettingItem) => { 47 | ListItem() { 48 | this.settingCell(item) 49 | } 50 | .height($r('app.float.48vp')) 51 | }, (item: SettingItem) => JSON.stringify(item)) 52 | } 53 | .backgroundColor(Color.White) 54 | .width(Constants.FULL_PARENT) 55 | .height(Constants.SET_LIST_WIDTH) 56 | .divider({ 57 | strokeWidth: $r('app.float.1vp'), 58 | color: Color.Grey, 59 | startMargin: $r('app.float.42vp'), 60 | endMargin: $r('app.float.24vp') 61 | }) 62 | .borderRadius($r('app.float.16vp')) 63 | .padding({ top: $r('app.float.4vp'), bottom: $r('app.float.4vp') }) 64 | 65 | //3-推出 66 | 67 | Button('退出登录', { type: ButtonType.Capsule }) 68 | .width(Constants.BUTTON_WIDTH) 69 | .height($r('app.float.40vp')) 70 | .fontSize($r('app.float.16fp')) 71 | .fontColor($r('app.color.fire_brick')) 72 | .fontWeight(FontWeight.Medium) 73 | .backgroundColor($r('app.color.gainsboro')) 74 | .margin({ bottom: $r('app.float.50vp') }) 75 | .onClick(() => { 76 | SettingViewModel.logout() 77 | }) 78 | 79 | Blank() 80 | 81 | } 82 | .height(Constants.FULL_PARENT) 83 | } 84 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/Splash.ets: -------------------------------------------------------------------------------- 1 | import Constants from '../common/constants/Constants' 2 | import router from '@ohos.router'; 3 | /** 4 | * @desc:启动页面 5 | * 6 | */ 7 | @Entry 8 | @Component 9 | struct Splash { 10 | @State countdown: number = 2; 11 | private timer: number = -1; 12 | 13 | aboutToAppear(): void { 14 | setTimeout(() => { 15 | this.startTiming(); 16 | }, 1000); 17 | } 18 | 19 | startTiming() { 20 | this.timer = setInterval(() => { 21 | this.countdown--; 22 | if (this.countdown === 0) { 23 | this.clearTiming(); 24 | this.jumpToMainPage(); 25 | } 26 | }, 1000); 27 | } 28 | 29 | clearTiming() { 30 | if (this.timer !== -1) { 31 | clearInterval(this.timer); 32 | this.timer = -1; 33 | } 34 | } 35 | 36 | jumpToMainPage() { 37 | this.clearTiming(); 38 | router.replaceUrl({ 39 | url: Constants.MainPage_Path 40 | }); 41 | } 42 | 43 | aboutToDisappear() { 44 | this.clearTiming(); 45 | } 46 | 47 | build() { 48 | Column() { 49 | Stack() { 50 | Image($r('app.media.splash_bg')) 51 | .width(Constants.FULL_PERCENT) 52 | .height(Constants.FULL_PERCENT) 53 | Image($r('app.media.ic_logo')) 54 | .width($r('app.float.192vp')) 55 | .height($r('app.float.192vp')) 56 | .offset({ 57 | y: `-${Constants.PERCENTAGE_15}` 58 | }) 59 | .objectFit(ImageFit.Contain) 60 | 61 | Column() { 62 | Text($r('app.string.app_name')) 63 | .fontColor(Color.White) 64 | .fontSize($r('app.float.font_size_24fp')) 65 | .fontWeight(FontWeight.Medium) 66 | 67 | // Text($r('app.string.app_name')) 68 | // .fontSize($r('app.float.font_size_16fp')) 69 | // .fontColor(Color.White) 70 | // .margin({ 71 | // top: $r('app.float.5') 72 | // }) 73 | } 74 | .offset({ 75 | y: Constants.PERCENTAGE_25 76 | }) 77 | } 78 | }.height(Constants.FULL_SIZE) 79 | .width(Constants.FULL_SIZE) 80 | } 81 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/TreePage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import router from '@ohos.router'; 3 | import { TreeItem } from '../bean/TreeBean'; 4 | import Constants from '../common/constants/Constants'; 5 | import TreeViewModel from '../viewmodel/TreeViewModel'; 6 | 7 | /** 8 | * @desc:2-导航 9 | */ 10 | @Preview 11 | @Component 12 | export default struct TreePage { 13 | @State treeData: TreeItem[] = []; 14 | 15 | //页面即将显示时 16 | aboutToAppear() { 17 | //1-体系数据 18 | TreeViewModel.getTreeData().then(result => { 19 | console.log('tree>>>', result) 20 | this.treeData = result 21 | }) 22 | //2-知识体系下的文章 23 | TreeViewModel.getTreeArticleList(0, 60).then(result => { 24 | console.log('treeArticle>>>', result) 25 | }) 26 | } 27 | 28 | //分组-头部组件 29 | @Builder 30 | itemHead(text: string) { 31 | // 列表分组的头部组件,对应联系人分组A、B等位置的组件 32 | Text(text) 33 | .fontSize($r('app.float.20fp')) 34 | .fontWeight(FontWeight.Bold) 35 | .backgroundColor($r('app.color.light_gray')) 36 | .width('100%') 37 | .padding(5) 38 | } 39 | 40 | build() { 41 | List() { 42 | ForEach(this.treeData, (treeItem:TreeItem) => { 43 | ListItemGroup({ header: this.itemHead(treeItem.name) }) { 44 | //循环渲染ListItem 45 | ListItem() { 46 | Flex({ wrap: FlexWrap.Wrap, alignContent: FlexAlign.SpaceEvenly }) { 47 | ForEach(treeItem.children, (item: TreeItem, index) => { 48 | Navigator({ target: Constants.TreeTabPage_Path, type: NavigationType.Push }) { 49 | Text(item.name) 50 | .textAlign(TextAlign.Center) 51 | .height($r('app.float.30vp')) 52 | .border({ width: 1 }) 53 | .borderColor(Color.Black) 54 | .borderRadius(2) 55 | .padding({ 56 | top: $r('app.float.2vp'), 57 | bottom: $r('app.float.2vp'), 58 | left: $r('app.float.5vp'), 59 | right: $r('app.float.5vp') 60 | }) 61 | .margin($r('app.float.5vp')) 62 | }.params({ index: index, data: treeItem }) 63 | }) 64 | }.width('90%').padding($r('app.float.10vp')) 65 | } 66 | } 67 | }) 68 | } 69 | .height('100%') 70 | .width('100%') 71 | } 72 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/TreeTabPage.ets: -------------------------------------------------------------------------------- 1 | // 导入页面路由模块 2 | import router from '@ohos.router'; 3 | import { ProjectItem } from '../bean/ProjectBean'; 4 | import Constants from '../common/constants/Constants'; 5 | import TitleBar from '../view/TitleBar'; 6 | import TreeList from '../view/TreeList'; 7 | /** 8 | * @desc:7-导航-条目跳转页面 9 | */ 10 | 11 | @Preview 12 | @Entry 13 | @Component 14 | export default struct TreeTabPage { 15 | private tabsController: TabsController = new TabsController() 16 | @State params: object = router.getParams(); 17 | @State projectData: ProjectItem[] = []; 18 | @State currentIndex: number = 0; 19 | @State optionsDic: Map = new Map(); 20 | //页面即将显示时 21 | aboutToAppear() { 22 | //1-项目分类 23 | this.currentIndex = this.params['index'] 24 | this.projectData = (this.params['data'] as ProjectItem).children 25 | this.projectData.forEach((project, index) => { 26 | this.optionsDic[index] = project.id //将tab_index对应的cid放到map中 27 | }) 28 | } 29 | 30 | //自定义Tab 31 | @Builder TabBuilder(index: number) { 32 | Column() { 33 | Text(this.projectData[index].name) 34 | .height(Constants.FULL_PERCENT) 35 | .fontSize(this.currentIndex === index ? $r('app.float.24fp') : $r('app.float.18fp')) 36 | .fontWeight(this.currentIndex === index ? Constants.TYPE_FONT_WEIGHT : Constants.DESC_FONT_WEIGHT) 37 | .fontColor($r('app.color.black')) 38 | } 39 | .padding({ left: $r('app.float.8vp'), right: $r('app.float.8vp') }) 40 | .margin({ 41 | left: index === 0 ? $r('app.float.8vp') : 0, 42 | right: index === this.projectData.length - 1 ? $r('app.float.8vp') : 0 43 | }) 44 | } 45 | 46 | build() { 47 | Column() { 48 | TitleBar({ title: (this.params['data'] as ProjectItem).name }) 49 | //Tabs 50 | Tabs({ barPosition: BarPosition.Start }) { 51 | ForEach(this.projectData, (projectItem: ProjectItem, index?: number) => { 52 | TabContent() { 53 | TreeList({ index, optionsDic: this.optionsDic, currentIndex: $currentIndex }) 54 | }.tabBar(this.TabBuilder(index?index:0)) 55 | }, (tabItem: ProjectItem) => JSON.stringify(tabItem)) 56 | } 57 | .barHeight($r('app.float.50vp')) 58 | .height(Constants.FULL_PERCENT) 59 | .barMode(BarMode.Scrollable) 60 | .onChange((index: number) => { 61 | this.currentIndex = index; 62 | this.tabsController.changeIndex(this.currentIndex); 63 | }) 64 | .vertical(false) 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /entry/src/main/ets/pages/WebPage.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import router from '@ohos.router'; 17 | import webview from '@ohos.web.webview'; 18 | import prompt from '@ohos.promptAction'; 19 | import Constants from '../common/constants/Constants'; 20 | import TitleBar from '../view/TitleBar'; 21 | 22 | 23 | @Preview 24 | @Entry 25 | @Component 26 | struct WebPage { 27 | webController: webview.WebviewController = new webview.WebviewController(); 28 | @State params: object = router.getParams(); 29 | @State progressVal: number = 0; 30 | @State isLoading: boolean = true; 31 | @State intervalLoading: number = -1; 32 | 33 | aboutToAppear() { 34 | this.intervalLoading = setInterval(() => { 35 | this.progressVal = this.progressVal >= Constants.WebConstant_PROGRESS_MAX ? 36 | Constants.WebConstant_PROGRESS_MIN : (this.progressVal + Constants.WebConstant_PROGRESS_STEP); 37 | }, Constants.WebConstant_MILLI_SECONDS); 38 | } 39 | 40 | build() { 41 | Stack({ alignContent: Alignment.TopStart }) { 42 | Row() { 43 | Column() { 44 | TitleBar({ title: this.params['title'] }) 45 | 46 | // Web component loading H5. 47 | Web({ src: this.params['path'], controller: this.webController }) 48 | .zoomAccess(false) 49 | .width(Constants.FULL_PERCENT) 50 | .height(Constants.FULL_PERCENT) 51 | .onConfirm((event) => { 52 | return true; 53 | }) 54 | .onErrorReceive((event) => { 55 | if (event?.error.getErrorInfo() === 'ERR_INTERNET_DISCONNECTED') { 56 | prompt.showToast({ 57 | message: $r('app.string.internet_err'), 58 | duration: Constants.WebConstant_DURATION 59 | }) 60 | } 61 | if (event?.error.getErrorInfo() === 'ERR_CONNECTION_TIMED_OUT') { 62 | prompt.showToast({ 63 | message: $r('app.string.internet_err'), 64 | duration: Constants.WebConstant_DURATION 65 | }) 66 | } 67 | }) 68 | .onProgressChange((event) => { 69 | if (event?.newProgress === Constants.WebConstant_PROGRESS_MAX) { 70 | this.isLoading = false; 71 | clearInterval(this.intervalLoading); 72 | this.intervalLoading = -1; 73 | } 74 | }) 75 | } 76 | .width(Constants.FULL_PERCENT) 77 | .height(Constants.FULL_PERCENT) 78 | }.width(Constants.FULL_PERCENT) 79 | .height(Constants.FULL_PERCENT) 80 | 81 | if (this.isLoading) { 82 | Progress({ 83 | value: Constants.WebConstant_PROGRESS_MIN, 84 | total: Constants.WebConstant_PROGRESS_MAX, 85 | type: ProgressType.ScaleRing 86 | }) 87 | .color(Color.Grey) 88 | .value(this.progressVal) 89 | .width(Constants.WebConstant_PROGRESS_WIDTH) 90 | .height(Constants.WebConstant_PROGRESS_WIDTH) 91 | .style({ 92 | strokeWidth: Constants.WebConstant_PROGRESS_STROKE_WIDTH, 93 | scaleCount: Constants.WebConstant_PROGRESS_SCALE_COUNT, 94 | scaleWidth: Constants.WebConstant_PROGRESS_SCALE_WIDTH 95 | }) 96 | .zIndex(1) 97 | .position({ 98 | x: Constants.WebConstant_PROGRESS_POSITION_X, 99 | y: Constants.WebConstant_PROGRESS_POSITION_Y 100 | }) 101 | } 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /entry/src/main/ets/utils/Logger.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import hilog from '@ohos.hilog'; 17 | 18 | const LOGGER_PREFIX: string = 'News Release'; 19 | 20 | class Logger { 21 | private domain: number; 22 | private prefix: string; 23 | 24 | // format Indicates the log format string. 25 | private format: string = '%{public}s, %{public}s'; 26 | 27 | /** 28 | * constructor. 29 | * 30 | * @param prefix Identifies the log tag. 31 | * @param domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF 32 | * @param args Indicates the log parameters. 33 | */ 34 | constructor(prefix: string = '', domain: number = 0xFF00) { 35 | this.prefix = prefix; 36 | this.domain = domain; 37 | } 38 | 39 | debug(...args: string[]): void { 40 | hilog.debug(this.domain, this.prefix, this.format, args); 41 | } 42 | 43 | info(...args: string[]): void { 44 | hilog.info(this.domain, this.prefix, this.format, args); 45 | } 46 | 47 | warn(...args: string[]): void { 48 | hilog.warn(this.domain, this.prefix, this.format, args); 49 | } 50 | 51 | error(...args: string[]): void { 52 | hilog.error(this.domain, this.prefix, this.format, args); 53 | } 54 | } 55 | 56 | export default new Logger(LOGGER_PREFIX); -------------------------------------------------------------------------------- /entry/src/main/ets/utils/PreferencesUtils.ets: -------------------------------------------------------------------------------- 1 | import preferences from '@ohos.data.preferences'; 2 | import dataPreferences from '@ohos.data.preferences'; 3 | import Logger from './Logger'; 4 | 5 | const PREFERENCES_NAME = 'theme.db' 6 | let preferenceTheme: preferences.Preferences| null = null; 7 | const TAG: string = '[Index]' 8 | 9 | class PreferencesUtils { 10 | constructor() { 11 | this.createDataPreferences() 12 | } 13 | 14 | async createDataPreferences() { 15 | preferenceTheme = await dataPreferences.getPreferences(globalThis.abilityContext, PREFERENCES_NAME); 16 | } 17 | 18 | 19 | /** 20 | * 保存String数据 21 | * @param str 22 | */ 23 | async putStringData(key: string, value: string) { 24 | Logger.info(TAG, `Put begin`) 25 | if (preferenceTheme !== null) { 26 | await preferenceTheme.put(key, value) 27 | await preferenceTheme.flush() 28 | } 29 | } 30 | 31 | /** 32 | * @returns 33 | */ 34 | async getStringData(key: string) { 35 | Logger.info(TAG, `Get begin`) 36 | if (preferenceTheme !== null) { 37 | let data: string = '' 38 | data = await preferenceTheme.get(key, '') as string 39 | return data; 40 | }else{ 41 | return '' 42 | } 43 | } 44 | 45 | async putNumberData(key: string, num: number) { 46 | Logger.info(TAG, `Put begin`) 47 | if (preferenceTheme !== null) { 48 | await preferenceTheme.put(key, num) 49 | await preferenceTheme.flush() 50 | } 51 | } 52 | 53 | /** 54 | * getNumber数据 55 | * @returns 56 | */ 57 | async getNumberData(key: string) { 58 | Logger.info(TAG, `Get begin`) 59 | if (preferenceTheme !== null) { 60 | let data: number = 0; 61 | data = await preferenceTheme.get(key, -1) as number 62 | return data; 63 | }else { 64 | return -1 65 | } 66 | } 67 | } 68 | 69 | let preferencesUtil = new PreferencesUtils(); 70 | 71 | export default preferencesUtil as PreferencesUtils; -------------------------------------------------------------------------------- /entry/src/main/ets/utils/Utils.ets: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @desc:工具类 4 | */ 5 | 6 | //1-判断是否为空字符串 7 | const isEmpty = (data:string|[]|number|object) => { 8 | return data == undefined || data == "" || (data as []).length <= 0 || data === '0' 9 | 10 | }; 11 | //2-格式化字符串 12 | const formatHtml = (html:string) => { 13 | let data = '' 14 | if (!isEmpty(html)) { 15 | let regSymbol = RegExp("&.*;") 16 | let regHtml = RegExp(/<[^>]*>/g); 17 | data = html.replace(regSymbol, '').replace(regHtml, '') 18 | } 19 | return data 20 | } 21 | 22 | export { 23 | isEmpty, 24 | formatHtml 25 | }; -------------------------------------------------------------------------------- /entry/src/main/ets/view/ArticleItem.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:文章Item 3 | */ 4 | import * as Utils from "../utils/Utils"; 5 | import { Article } from '../bean/ArticleBean' 6 | import Constants from '../common/constants/Constants'; 7 | import { when } from '@ohos/hypium'; 8 | 9 | @Component 10 | export default struct ArticleItem { 11 | article: Article = new Article() 12 | @State isTop: boolean = false; 13 | @State hasTag: boolean = false; 14 | @State tag: string = ''; 15 | @State author: string | number = ''; 16 | @State title: string = ''; 17 | @State desc: string = ''; 18 | @State hasPic: boolean = false; 19 | @State hasSuperChapterName: boolean = false; 20 | @State superChapterName: string = ''; 21 | @State hasChapterName: boolean = false; 22 | @State chapterName: string = ''; 23 | 24 | aboutToAppear() { 25 | let item = this.article 26 | 27 | this.isTop = item.isTop; 28 | this.hasTag = !Utils.isEmpty(item.tags as []); 29 | this.tag = !this.hasTag ? '暂无' : item.tags[0].name; 30 | this.author = Utils.isEmpty(item.author) ? Utils.isEmpty(item.shareUser) ? '' : item.shareUser : item.author; 31 | 32 | 33 | //this.author = Utils.isEmpty(item.author) ? Utils.isEmpty(item.shareUser) ? '' : item.shareUser : item.author; 34 | this.title = Utils.formatHtml(item.title); 35 | this.desc = Utils.formatHtml(item.desc) 36 | this.hasPic = !Utils.isEmpty(item.envelopePic) 37 | this.hasSuperChapterName = !Utils.isEmpty(item.superChapterName) 38 | this.superChapterName = Utils.isEmpty(item.superChapterName) ? '' : Utils.formatHtml(item.superChapterName) 39 | this.hasChapterName = !Utils.isEmpty(item.chapterName) 40 | this.chapterName = Utils.isEmpty(item.chapterName) ? '' : Utils.formatHtml(item.chapterName) 41 | 42 | } 43 | 44 | build() { 45 | Column() { 46 | //第一行 47 | Row() { 48 | 49 | //1-tag 50 | if (this.hasTag) { 51 | Text(this.tag) 52 | .height($r('app.float.23vp')) 53 | .fontSize($r('app.float.12fp')) 54 | .textAlign(TextAlign.Center) 55 | .borderRadius($r('app.float.3vp')) 56 | .fontColor($r('app.color.white')) 57 | .backgroundColor('red') 58 | .padding({ 59 | left: $r('app.float.10vp'), 60 | right: $r('app.float.10vp'), 61 | top: $r('app.float.5vp'), 62 | bottom: $r('app.float.5vp') 63 | }) 64 | } 65 | Row() { 66 | Image($r('app.media.ic_me_normal')).width($r('app.float.18vp')).height($r('app.float.18vp')) 67 | Text(this.author.toString()).margin({ left: $r('app.float.5vp') }) //2-author 68 | }.margin({ left: $r('app.float.5vp'), right: $r('app.float.5vp') }) 69 | 70 | Row() { 71 | Image($r('app.media.ic_clock')).width($r('app.float.18vp')).height($r('app.float.18vp')) 72 | Text(this.article.niceDate) //3-time 73 | } 74 | }.width(Constants.FULL_PERCENT).justifyContent(FlexAlign.SpaceBetween) 75 | //第2行 76 | Flex({ direction: FlexDirection.Row }) { 77 | Column() { 78 | Text(this.title) 79 | //.width(Constants.FULL_PERCENT) 80 | .maxLines(2) 81 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 82 | .fontSize($r('app.float.18fp')) 83 | .fontColor(Color.Black) 84 | .align(Alignment.TopStart) 85 | Text(this.desc) 86 | //.width(Constants.FULL_PERCENT) 87 | .maxLines(2) 88 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 89 | .fontColor(Color.Gray) 90 | .align(Alignment.TopStart) 91 | }.flexGrow(1) 92 | //.width('70%') 93 | .alignItems(HorizontalAlign.Start) 94 | 95 | if (this.hasPic) { 96 | //Image('https://img1.baidu.com/it/u=2653588009,1450076812&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1699722000&t=b4f916a93f7e4735cf7c4c69416ab2fb') 97 | Image(this.article.envelopePic) 98 | .width($r('app.float.60vp')) 99 | .height('100%') 100 | } 101 | }.height($r('app.float.80vp')).margin({ top: $r('app.float.5vp') }) 102 | //第3行 103 | Row() { 104 | //置顶 105 | if (this.isTop) { 106 | Text('置顶') 107 | .height($r('app.float.23vp')) 108 | .fontSize($r('app.float.12fp')) 109 | .borderRadius($r('app.float.3vp')) 110 | .textAlign(TextAlign.Center) 111 | .fontColor($r('app.color.white')) 112 | .backgroundColor($r('app.color.dark_orange')) 113 | .padding({ 114 | left: $r('app.float.10vp'), 115 | right: $r('app.float.10vp'), 116 | top: $r('app.float.5vp'), 117 | bottom: $r('app.float.5vp') 118 | }) 119 | .margin({ right: $r('app.float.10vp') }) 120 | } 121 | 122 | if (this.hasSuperChapterName) { 123 | Text(this.superChapterName) 124 | .height($r('app.float.23vp')) 125 | .fontSize($r('app.float.12fp')) 126 | .borderRadius($r('app.float.3vp')) 127 | .textAlign(TextAlign.Center) 128 | .fontColor($r('app.color.white')) 129 | .backgroundColor($r('app.color.olive')) 130 | .padding({ 131 | left: $r('app.float.10vp'), 132 | right: $r('app.float.10vp'), 133 | top: $r('app.float.5vp'), 134 | bottom: $r('app.float.5vp') 135 | }) 136 | .margin({ right: $r('app.float.10vp') }) 137 | } 138 | if (this.hasChapterName) { 139 | Text(this.chapterName) 140 | .height($r('app.float.23vp')) 141 | .fontSize($r('app.float.12fp')) 142 | .borderRadius($r('app.float.3vp')) 143 | .textAlign(TextAlign.Center) 144 | .fontColor($r('app.color.white')) 145 | .backgroundColor($r('app.color.teal')) 146 | .padding({ 147 | left: $r('app.float.10vp'), 148 | right: $r('app.float.10vp'), 149 | top: $r('app.float.5vp'), 150 | bottom: $r('app.float.5vp') 151 | }) 152 | .margin({ right: $r('app.float.10vp') }) 153 | } 154 | }.width(Constants.FULL_PERCENT).justifyContent(FlexAlign.Start).margin({ top: $r('app.float.5vp') }) 155 | 156 | } 157 | //.height(160) 158 | .width(Constants.FULL_PERCENT) 159 | .padding({ 160 | top: $r('app.float.5vp'), 161 | bottom: $r('app.float.5vp'), 162 | left: $r('app.float.10vp'), 163 | right: $r('app.float.10vp') 164 | }) 165 | .borderWidth(0.5) 166 | .borderColor(Color.Gray) 167 | .backgroundColor('#FFF1F3F5') 168 | } 169 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/InfoItem.ets: -------------------------------------------------------------------------------- 1 | import * as Utils from "../utils/Utils"; 2 | /** 3 | * 积分排行或个人积分——Item,三行或四行数据 4 | */ 5 | @Preview 6 | @Component 7 | export default struct InfoItem { 8 | param1: string = ''; 9 | param2: string = ''; 10 | param3: string = ''; 11 | param4: string = ''; 12 | 13 | build() { 14 | Column({ space: 5 }) { 15 | Text(this.param1) 16 | Text(this.param2) 17 | Text(this.param3) 18 | if (!Utils.isEmpty(this.param4)){ 19 | Text(this.param4) 20 | } 21 | }.width('100%') 22 | .borderRadius(5) 23 | .padding({ left: 5, right: 5, top: 10, bottom: 10 }) 24 | .backgroundColor($r('app.color.white_smoke')) 25 | } 26 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/MineFunc.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:我的-常用功能 3 | */ 4 | import Constants from '../common/constants/Constants'; 5 | 6 | interface FuncInterface { 7 | 'name': string; 8 | 'backgroundColor': Color; 9 | } 10 | 11 | @Preview 12 | @Component 13 | export default struct MineFunc { 14 | @State bgColors: Color[] = [Color.Red, Color.Orange, Color.Yellow, Color.Green, Color.Pink, Color.Grey, Color.Blue, Color.Brown]; 15 | gridData:FuncInterface[]= [ 16 | { 17 | name: "工具", 18 | backgroundColor: Color.Red 19 | }, 20 | { 21 | name: "问答", 22 | backgroundColor: Color.Orange 23 | }, 24 | { 25 | name: "消息", 26 | backgroundColor: Color.Yellow 27 | }, 28 | { 29 | name: "课程", 30 | backgroundColor: Color.Green 31 | }, 32 | { 33 | name: "待办清单", 34 | backgroundColor: Color.Pink 35 | }, 36 | { 37 | name: "分享文章", 38 | backgroundColor: Color.Grey 39 | }, 40 | ] 41 | @State currentBp: string = 'unknown'; 42 | 43 | build() { 44 | Column() { 45 | Text('常用功能') 46 | .fontSize($r('app.float.20fp')) 47 | .fontWeight(FontWeight.Bold) 48 | .margin({ bottom: $r('app.float.10vp') }) 49 | 50 | GridRow({ columns: 3 }) { 51 | ForEach(this.gridData, (item:FuncInterface, index) => { 52 | GridCol() { 53 | Row() { 54 | Text(`${item.name}`).textAlign(TextAlign.Center).width('100%') 55 | }.width('100%').height(120) 56 | }.backgroundColor(item.backgroundColor) 57 | }) 58 | } 59 | .width('100%').height('100%') 60 | .onBreakpointChange((breakpoint) => { 61 | this.currentBp = breakpoint 62 | }) 63 | } 64 | .height(600) 65 | .alignItems(HorizontalAlign.Start) 66 | .padding({ left: $r('app.float.10vp'), right: $r('app.float.10vp') }) 67 | .width(Constants.FULL_PERCENT) 68 | } 69 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/MineHead.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:我的-头部信息 3 | */ 4 | import router from '@ohos.router'; 5 | import { UserData, UserInfo } from '../bean/UserInfoBean'; 6 | import Constants from '../common/constants/Constants' 7 | import * as Utils from "../utils/Utils"; 8 | import MeViewModel from '../viewmodel/MeViewModel'; 9 | import prompt from '@ohos.promptAction'; 10 | 11 | @Preview 12 | @Component 13 | export default struct MineHead { 14 | @State userData: UserData = new UserData() 15 | @State isLogin: boolean = false 16 | @State rank: string = ''; 17 | @State collectIds: string = ''; 18 | @State level: string = ''; 19 | @State coinCount: string = ''; 20 | 21 | aboutToAppear() { 22 | MeViewModel.getUserInfo().then((data: UserData) => { 23 | this.userData = data; 24 | }).catch((err:Error) => { 25 | this.userData = new UserData(); 26 | }) 27 | } 28 | 29 | // 在Home页面中 30 | jumpLoginClick(): void { 31 | router.pushUrl({ 32 | url: Constants.LoginRegister_Path // 目标url 33 | }, router.RouterMode.Standard, (err) => { 34 | if (err) { 35 | console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); 36 | return; 37 | } 38 | console.info('Invoke pushUrl succeeded.'); 39 | }); 40 | } 41 | 42 | jumpPath(path: string) { 43 | if (Utils.isEmpty(this.userData.userInfo.username)) { 44 | prompt.showToast({ message: '请先登录账号' }) 45 | return 46 | } 47 | console.log("path=>", path); 48 | switch (path){ 49 | case Constants.COIN: 50 | case Constants.RANK: 51 | router.pushUrl({ url: Constants.InfoPage_Path, params: { title: path } }) 52 | break; 53 | } 54 | } 55 | 56 | @Builder 57 | InfoItem(title: string, content?: string, click?: () => void) { 58 | Column() { 59 | Text(title) 60 | .fontSize($r('app.float.18fp')) 61 | .fontWeight(FontWeight.Bold) 62 | Text(Utils.isEmpty(content) ? '——' : content) 63 | .margin({ top: $r('app.float.3vp') }) 64 | }.flexGrow(1) 65 | .alignItems(HorizontalAlign.Center) 66 | .onClick(click) 67 | } 68 | 69 | build() { 70 | Column() { 71 | //1-设置 72 | Flex({ alignItems: ItemAlign.Center }) { 73 | Text('') 74 | .size({ width: $r('app.float.60vp'), height: $r('app.float.60vp') }) 75 | 76 | Text('我的') 77 | .textAlign(TextAlign.Center) 78 | .fontColor(Color.White) 79 | .maxLines(1) 80 | .fontSize($r('app.float.22fp')) 81 | .flexGrow(1) 82 | 83 | Image($r('app.media.ic_setting')) 84 | .size({ width: $r('app.float.30vp'), height: $r('app.float.30vp') }) 85 | .margin({ right: $r('app.float.10vp') }) 86 | .onClick(() => { 87 | router.pushUrl({ url: Constants.SettingPage_Path }) 88 | }) 89 | }.width(Constants.FULL_PERCENT) 90 | .backgroundColor($r('app.color.dodger_blue')) 91 | 92 | //2-底部 93 | Column() { 94 | 95 | //1-头像行-信息 96 | Flex({ alignItems: ItemAlign.Center }) { 97 | Image($r('app.media.ic_user')) 98 | .margin({ left: $r('app.float.20vp') }) 99 | .size({ width: $r('app.float.60vp'), height: $r('app.float.60vp') }) 100 | 101 | Text(!Utils.isEmpty(this.userData.userInfo.username) ? `${this.userData.userInfo.username}` : '欢迎登陆') 102 | .textAlign(TextAlign.Start) 103 | .maxLines(1) 104 | .fontSize($r('app.float.18fp')) 105 | .margin({ left: $r('app.float.10vp'), right: $r('app.float.10vp') }) 106 | .flexGrow(1) 107 | 108 | Image($r('app.media.ic_arrow_right')) 109 | .size({ width: $r('app.float.30vp'), height: $r('app.float.30vp') }) 110 | 111 | }.width(Constants.FULL_PERCENT) 112 | .onClick(() => { 113 | if (Utils.isEmpty(this.userData.userInfo.username)) { 114 | this.jumpLoginClick() 115 | } 116 | }) 117 | 118 | //2-文章信息 119 | // Flex({ justifyContent: FlexAlign.SpaceBetween }) { 120 | // this.InfoItem('排名',!Utils.isEmpty(this.userData) ? `${this.userData.coinInfo.rank}}` : '——') 121 | // this.InfoItem('收藏',!Utils.isEmpty(this.userData) ? `${this.userData.userInfo.collectIds.length.toString()}` : '——') 122 | // this.InfoItem('等级',!Utils.isEmpty(this.userData) ? `${this.userData.coinInfo.level.toString()}` : '——') 123 | // this.InfoItem('积分',!Utils.isEmpty(this.userData) ? `${this.userData.coinInfo.coinCount.toString()}` : '——') 124 | // }.margin({ top: $r('app.float.40vp') }) 125 | 126 | if (!Utils.isEmpty(this.userData.userInfo.username)) { 127 | Flex({ justifyContent: FlexAlign.SpaceBetween }) { 128 | this.InfoItem('排名', this.userData.coinInfo.rank, ():void => this.jumpPath(Constants.RANK)) 129 | this.InfoItem('收藏', `${this.userData.userInfo.collectIds.length}`, ():void => this.jumpPath(Constants.COLLECT)) 130 | this.InfoItem('等级', `${this.userData.coinInfo.level}`, ():void => this.jumpPath(Constants.LEVEL)) 131 | this.InfoItem('积分', `${this.userData.coinInfo.coinCount}`, ():void => this.jumpPath(Constants.COIN)) 132 | }.margin({ top: $r('app.float.40vp') }) 133 | } else { 134 | Flex({ justifyContent: FlexAlign.SpaceBetween }) { 135 | this.InfoItem('排名', '——', ():void => this.jumpPath(Constants.RANK)) 136 | this.InfoItem('收藏', '——', ():void => this.jumpPath(Constants.COLLECT)) 137 | this.InfoItem('等级', '——', ():void => this.jumpPath(Constants.LEVEL)) 138 | this.InfoItem('积分', '——', ():void => this.jumpPath(Constants.COIN)) 139 | }.margin({ top: $r('app.float.40vp') }) 140 | } 141 | } 142 | .width(Constants.FULL_PERCENT) 143 | .padding({ 144 | left: $r('app.float.10vp'), 145 | right: $r('app.float.10vp'), 146 | top: $r('app.float.30vp'), 147 | bottom: $r('app.float.20vp') 148 | }) 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/MsgItemView.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:消息-未/已读消息Item 3 | */ 4 | import Constants from '../common/constants/Constants' 5 | 6 | import { MsgData, MsgItem } from '../bean/MsgBean' 7 | 8 | 9 | @Preview 10 | @Component 11 | export default struct MsgItemView { 12 | msgItem: MsgItem = new MsgItem() 13 | @State msg: string = '' 14 | @State content: string = 'kotlin 协程,CoroutineScope 调用 cancel() 后,无法再次执行 launch,如何理解这块。如下代码val scope = CoroutineScope(Dispatchers.Main)scope.launch Log.d(TAG, "cancelJob: before cancel")...' 15 | 16 | build() { 17 | Column() { 18 | //第一行 19 | Flex({ justifyContent: FlexAlign.SpaceBetween }) { 20 | Row() { 21 | Image($r('app.media.ic_me_normal')).width($r('app.float.18vp')).height($r('app.float.18vp')) 22 | Text(this.msgItem.fromUser).margin({ left: $r('app.float.5vp') }) //2-author 23 | }.margin({ left: $r('app.float.5vp'), right: $r('app.float.5vp') }) 24 | 25 | Text(this.msgItem.tag) 26 | .height($r('app.float.23vp')) 27 | .fontSize($r('app.float.12fp')) 28 | .textAlign(TextAlign.Center) 29 | .maxLines(1) 30 | .borderRadius($r('app.float.3vp')) 31 | .fontColor($r('app.color.white')) 32 | .backgroundColor($r('app.color.fire_brick')) 33 | .padding({ 34 | left: $r('app.float.10vp'), 35 | right: $r('app.float.10vp'), 36 | }) 37 | 38 | Row() { 39 | Image($r('app.media.ic_clock')).width($r('app.float.18vp')).height($r('app.float.18vp')) 40 | Text(this.msgItem.niceDate).margin({ left: $r('app.float.5vp') }) //2-author 41 | }.margin({ left: $r('app.float.5vp'), right: $r('app.float.5vp') }) 42 | } 43 | //第2行呢容 44 | Text(this.msgItem.title) 45 | .height($r('app.float.30vp')) 46 | .fontSize($r('app.float.12fp')) 47 | .textAlign(TextAlign.Start) 48 | .borderRadius($r('app.float.3vp')) 49 | .fontColor($r('app.color.white')) 50 | .maxLines(1) 51 | .textOverflow({ overflow: TextOverflow.Ellipsis }) 52 | .width(Constants.FULL_PERCENT) 53 | .borderRadius(6) 54 | .backgroundColor($r('app.color.royal_blue')) 55 | .margin({ top: $r('app.float.10vp') }) 56 | .padding({ 57 | left: $r('app.float.10vp'), 58 | right: $r('app.float.10vp'), 59 | top: $r('app.float.5vp'), 60 | bottom: $r('app.float.5vp') 61 | }) 62 | //第3行呢容 63 | Text(this.msgItem.message) 64 | .textAlign(TextAlign.Start) 65 | .width(Constants.FULL_PERCENT) 66 | .maxLines(3) 67 | .margin({ top: $r('app.float.10vp') }) 68 | } 69 | .padding(10) 70 | .backgroundColor($r('app.color.white_smoke')) 71 | .borderWidth(5) 72 | .borderColor($r('app.color.light_gray')) 73 | } 74 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/MsgReadList.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:消息-已读消息 3 | */ 4 | import { MsgData, MsgItem } from '../bean/MsgBean' 5 | import MsgViewModel from '../viewmodel/MsgViewModel' 6 | import MsgItemView from './MsgItemView' 7 | import * as Utils from "../utils/Utils"; 8 | import { NavigatorModel } from '../viewmodel/NavigatorModel'; 9 | import Constants from '../common/constants/Constants'; 10 | import router from '@ohos.router'; 11 | 12 | 13 | @Component 14 | export default struct MsgReadList { 15 | @State msgList: MsgItem[] = [] 16 | 17 | aboutToAppear() { 18 | MsgViewModel.getMsgRead(1).then((msgData: MsgData) => { 19 | this.msgList = msgData.datas 20 | console.log('readMsg>>>', msgData) 21 | }) 22 | } 23 | 24 | @Builder 25 | MsgDataView() { 26 | Scroll() { 27 | Column() { 28 | ForEach(this.msgList, (item: MsgItem, index) => { 29 | Navigator({ target: Constants.WebPage_Path, type: NavigationType.Push }) { 30 | MsgItemView({ msgItem: item }) 31 | }.params({ path: item.link, title: item.title } as NavigatorModel) 32 | }) 33 | } 34 | } 35 | } 36 | 37 | @Builder 38 | MsgNoLoginView() { 39 | Text('登录后查看消息') 40 | .textAlign(TextAlign.Center) 41 | //.width('100%') 42 | //.height('100%') 43 | .backgroundColor($r('app.color.deep_sky_blue')) 44 | .padding({top:$r('app.float.10vp'),bottom:$r('app.float.10vp'),left:$r('app.float.20vp'),right:$r('app.float.20vp')}) 45 | .borderRadius($r('app.float.5vp')) 46 | .onClick(() => { 47 | router.pushUrl({ 48 | url: Constants.LoginRegister_Path // 目标url 49 | }) 50 | }) 51 | } 52 | 53 | @Builder 54 | MsgNoView() { 55 | Text('暂时没有数据哦') 56 | .textAlign(TextAlign.Center) 57 | .width('100%') 58 | .height('100%') 59 | } 60 | 61 | build() { 62 | if (!Utils.isEmpty(AppStorage.Get(Constants.Cookie) as string)) { 63 | if (Utils.isEmpty(this.msgList)) { 64 | this.MsgNoView() 65 | } else { 66 | this.MsgDataView() 67 | } 68 | } else { 69 | this.MsgNoLoginView() 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/MsgUnreadList.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:消息-未读消息 3 | */ 4 | 5 | import MsgViewModel from '../viewmodel/MsgViewModel' 6 | import MsgItemView from './MsgItemView' 7 | import { MsgData, MsgItem } from '../bean/MsgBean' 8 | import * as Utils from "../utils/Utils"; 9 | import Constants from '../common/constants/Constants'; 10 | import router from '@ohos.router'; 11 | 12 | @Component 13 | export default struct MsgUnreadList { 14 | @State msgList: MsgItem[] = [] 15 | 16 | aboutToAppear() { 17 | // MsgViewModel.getMsgUnRead().then(result => { 18 | // console.log('unreadMsg>>>', result) 19 | // }) 20 | 21 | MsgViewModel.getMsgUnRead().then((msgData: MsgData) => { 22 | this.msgList = msgData.datas 23 | console.log('readMsg>>>', msgData) 24 | }) 25 | } 26 | 27 | @Builder 28 | MsgDataView() { 29 | Scroll() { 30 | Column() { 31 | ForEach(this.msgList, (item:MsgItem, index) => { 32 | MsgItemView({ msgItem: item }) 33 | }) 34 | } 35 | } 36 | } 37 | 38 | @Builder 39 | MsgNoLoginView() { 40 | Text('登录后查看消息') 41 | .textAlign(TextAlign.Center) 42 | //.width('100%') 43 | //.height('100%') 44 | .backgroundColor($r('app.color.deep_sky_blue')) 45 | .padding({top:$r('app.float.10vp'),bottom:$r('app.float.10vp'),left:$r('app.float.20vp'),right:$r('app.float.20vp')}) 46 | .borderRadius($r('app.float.5vp')) 47 | .onClick(() => { 48 | router.pushUrl({ 49 | url: Constants.LoginRegister_Path // 目标url 50 | }) 51 | }) 52 | } 53 | 54 | @Builder 55 | MsgNoView() { 56 | Text('暂时没有数据哦') 57 | .textAlign(TextAlign.Center) 58 | .width('100%') 59 | .height('100%') 60 | } 61 | 62 | build() { 63 | if (!Utils.isEmpty(AppStorage.Get(Constants.Cookie) as string)) { 64 | if (Utils.isEmpty(this.msgList)) { 65 | this.MsgNoView() 66 | } else { 67 | this.MsgDataView() 68 | } 69 | } else { 70 | this.MsgNoLoginView() 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/ProjectList.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:项目-列表 3 | */ 4 | import { Article, ArticleList } from '../bean/ArticleBean'; 5 | import Constants from '../common/constants/Constants'; 6 | import { NavigatorModel } from '../viewmodel/NavigatorModel'; 7 | import ProjectViewModel from '../viewmodel/ProjectViewModel'; 8 | import ArticleItem from './ArticleItem'; 9 | import * as Utils from "../utils/Utils"; 10 | import { PullToRefresh } from '@ohos/pulltorefresh' 11 | import prompt from '@ohos.promptAction'; 12 | 13 | @Component 14 | export default struct ProjectList { 15 | index: number = 0; 16 | optionsDic: Map = new Map 17 | @Watch('changeTab') @Link currentIndex: number; 18 | @State articleListData: ArticleList = new ArticleList(); 19 | @State articleData: Article[] = []; 20 | @State pageNum: number = 1; //分页page 21 | private scroller: Scroller = new Scroller(); // 需绑定列表或宫格组件 22 | 23 | aboutToAppear() { 24 | // Load data. 25 | this.changeTab() 26 | } 27 | 28 | //与@Watch('changeTab')中的changeTab保持一致。 @Watch装饰器:状态变量更改通知 @Link装饰器:父子双向同步 29 | changeTab() { 30 | if (this.currentIndex !== this.index) { 31 | return; 32 | } 33 | this.getProjectListData(this.pageNum); 34 | } 35 | 36 | getProjectListData(page: number) { 37 | //2-项目列表,page从1开始 38 | ProjectViewModel.getProjectList(page, this.optionsDic[this.currentIndex]).then((data: ArticleList) => { 39 | console.log('getProjectList>>>', data) 40 | this.articleListData = data 41 | this.articleData.push(...data.datas) 42 | }) 43 | // ProjectViewModel.getProjectList(1, 294).then((data: ArticleList) => { 44 | // this.articleListData = data 45 | // }) 46 | } 47 | 48 | @Builder 49 | NoView() { 50 | Text('暂时没有数据哦') 51 | .textAlign(TextAlign.Center) 52 | .width('100%') 53 | .height('100%') 54 | } 55 | 56 | @Builder 57 | ListView() { 58 | List({ scroller: this.scroller }) { 59 | ForEach(this.articleData, (article: Article) => { 60 | ListItem() { 61 | Navigator({ target: Constants.WebPage_Path, type: NavigationType.Push }) { 62 | ArticleItem({ article: article }) 63 | }.params({ path: article.link, title: article.title } as NavigatorModel) 64 | } 65 | }) 66 | }.width(Constants.FULL_PERCENT) 67 | .height(Constants.FULL_PERCENT) 68 | .edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果 69 | } 70 | 71 | build() { 72 | if (Utils.isEmpty(this.articleListData.datas)) { 73 | this.NoView() 74 | } else { 75 | Column() { 76 | PullToRefresh({ 77 | data: this.articleData, 78 | scroller: this.scroller, 79 | customList: () => this.ListView(), 80 | onRefresh: () => { 81 | return new Promise((resolve, reject) => { 82 | setTimeout(() => { 83 | resolve('刷新成功'); 84 | //this.articleData = null 85 | this.pageNum = 1; 86 | this.getProjectListData(this.pageNum) 87 | }, 1000); 88 | }); 89 | }, 90 | onLoadMore: () => { 91 | return new Promise((resolve, reject) => { 92 | setTimeout(() => { 93 | resolve('onLoadMore'); 94 | if (!this.articleListData.over) { 95 | this.getProjectListData(++this.pageNum) 96 | } else { 97 | prompt.showToast({ message: $r('app.string.no_more_data') }) 98 | } 99 | }, 1000); 100 | }); 101 | }, 102 | customLoad: null, 103 | customRefresh: null, 104 | }) 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /entry/src/main/ets/view/TitleBar.ets: -------------------------------------------------------------------------------- 1 | import router from '@ohos.router' 2 | import Constants from '../common/constants/Constants' 3 | /** 4 | * TitleBar-标题栏 5 | */ 6 | @Component 7 | export default struct TitleBar { 8 | title: string = '' //中间标题 9 | 10 | build() { 11 | 12 | //1-头部 13 | Flex({ alignItems: ItemAlign.Center }) { 14 | Image($r('app.media.ic_arrow_left')) 15 | .size({ width: $r('app.float.30vp'), height: $r('app.float.30vp') }) 16 | .margin({ right: $r('app.float.10vp') }) 17 | .onClick(() => { 18 | router.back() 19 | }) 20 | Text(this.title) 21 | .textAlign(TextAlign.Center) 22 | .fontColor(Color.White) 23 | .maxLines(1) 24 | .fontSize($r('app.float.22fp')) 25 | .flexGrow(1) 26 | 27 | Text('') 28 | .width($r('app.float.30vp')) 29 | .height($r('app.float.30vp')) 30 | }.width(Constants.FULL_PERCENT) 31 | .height($r('app.float.60vp')) 32 | .backgroundColor($r('app.color.dodger_blue')) 33 | } 34 | } -------------------------------------------------------------------------------- /entry/src/main/ets/view/TreeList.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:体系-知识导航-列表 3 | */ 4 | import { Article, ArticleList } from '../bean/ArticleBean'; 5 | import Constants from '../common/constants/Constants'; 6 | import { NavigatorModel } from '../viewmodel/NavigatorModel'; 7 | import TreeViewModel from '../viewmodel/TreeViewModel'; 8 | import ArticleItem from './ArticleItem'; 9 | import * as Utils from "../utils/Utils"; 10 | import { PullToRefresh } from '@ohos/pulltorefresh' 11 | import prompt from '@ohos.promptAction'; 12 | 13 | @Component 14 | export default struct TreeList { 15 | index: number = 0; 16 | optionsDic: Map = new Map 17 | @Watch('changeTab') @Link currentIndex: number; 18 | @State articleListData: ArticleList = new ArticleList(); 19 | @State articleData: Article[] = []; 20 | @State pageNum: number = 0; //分页page 21 | private scroller: Scroller = new Scroller(); // 需绑定列表或宫格组件 22 | 23 | aboutToAppear() { 24 | // Load data. 25 | this.changeTab() 26 | } 27 | 28 | //与@Watch('changeTab')中的changeTab保持一致。 @Watch装饰器:状态变量更改通知 @Link装饰器:父子双向同步 29 | changeTab() { 30 | //2-项目列表,page从0开始 31 | this.getTreeListData(this.pageNum); 32 | 33 | } 34 | 35 | getTreeListData(page: number) { 36 | //2-项目列表,page从1开始 37 | TreeViewModel.getTreeArticleList(page, this.optionsDic[this.currentIndex]).then((data: ArticleList) => { 38 | console.log('getProjectList>>>', data) 39 | this.articleListData = data 40 | this.articleData.push(...data.datas) 41 | }) 42 | } 43 | 44 | @Builder 45 | NoView() { 46 | Text('暂时没有数据哦') 47 | .textAlign(TextAlign.Center) 48 | .width('100%') 49 | .height('100%') 50 | } 51 | 52 | @Builder 53 | ListView() { 54 | List({ scroller: this.scroller }) { 55 | ForEach(this.articleData, (article: Article) => { 56 | ListItem() { 57 | Navigator({ target: Constants.WebPage_Path, type: NavigationType.Push }) { 58 | ArticleItem({ article: article }) 59 | }.params({ path: article.link, title: article.title } as NavigatorModel) 60 | } 61 | }) 62 | } 63 | .width(Constants.FULL_PERCENT) 64 | .height(Constants.FULL_PERCENT) 65 | .edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果 66 | } 67 | 68 | build() { 69 | if (Utils.isEmpty(this.articleData)) { 70 | this.NoView() 71 | } else { 72 | Column() { 73 | PullToRefresh({ 74 | data: this.articleData, 75 | scroller: this.scroller, 76 | customList: () => this.ListView(), 77 | onRefresh: () => { 78 | return new Promise((resolve, reject) => { 79 | setTimeout(() => { 80 | resolve('刷新成功'); 81 | //this.articleData = null 82 | this.pageNum = 0; 83 | this.getTreeListData(this.pageNum) 84 | }, 1000); 85 | }); 86 | }, 87 | onLoadMore: () => { 88 | return new Promise((resolve, reject) => { 89 | setTimeout(() => { 90 | resolve('onLoadMore'); 91 | if (!this.articleListData.over) { 92 | this.getTreeListData(++this.pageNum) 93 | } else { 94 | prompt.showToast({ message: $r('app.string.no_more_data') }) 95 | } 96 | }, 1000); 97 | }); 98 | }, 99 | customLoad: null, 100 | customRefresh: null, 101 | }) 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/BottomTabsModel.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2023 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | import { BottomTabsItem } from '../common/bean/BottomTabsItem'; 17 | 18 | export const BottomTabsList: Array = [ 19 | { 20 | icon: $r('app.media.ic_home_normal'), 21 | iconSelected: $r('app.media.ic_home_selected'), 22 | text: $r('app.string.bottom_text_home') 23 | }, 24 | { 25 | icon: $r('app.media.ic_nav_normal'), 26 | iconSelected: $r('app.media.ic_nav_selected'), 27 | text: $r('app.string.bottom_text_nav') 28 | }, 29 | { 30 | icon: $r('app.media.ic_project_normal'), 31 | iconSelected: $r('app.media.ic_project_selected'), 32 | text: $r('app.string.bottom_text_project') 33 | }, 34 | { 35 | icon: $r('app.media.ic_msg_normal'), 36 | iconSelected: $r('app.media.ic_msg_selected'), 37 | text: $r('app.string.bottom_text_msg') 38 | }, 39 | { 40 | icon: $r('app.media.ic_me_normal'), 41 | iconSelected: $r('app.media.ic_me_selected'), 42 | text: $r('app.string.bottom_text_me') 43 | } 44 | ] -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/HomeViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:1-home-数据逻辑类 3 | */ 4 | import List from '@ohos.util.List'; 5 | import HttpConfig from '../api/HttpConfig'; 6 | import { articleListReq, articleTopReq, bannerReq } from '../api/HttpRequest'; 7 | import ResponseResult from '../api/ResponseResult'; 8 | import { Article, ArticleList, ArticleTop } from '../bean/ArticleBean'; 9 | import { Banner } from '../bean/BannerBean'; 10 | import Logger from '../utils/Logger'; 11 | /** 12 | * @desc:HomePage对应数据处理类 13 | */ 14 | class HomeViewModel { 15 | // bannerData: List 16 | // articleTopData: ArticleTop 17 | // articleListData: ArticleList 18 | 19 | 20 | //首页-banner轮播图、置顶文章、文章三个接口 21 | getHomeData() { 22 | return Promise.all([this.getBanner(), this.getArticleTop(), this.getArticle(0)]) 23 | // .then(result => { 24 | // //1-result[0]-Banner 25 | // this.bannerData = result[0] 26 | // //2-result[1]-置顶文章 27 | // this.articleTopData = result[1] 28 | // //3-result[3] - 文章列表 29 | // this.articleListData = result[2] 30 | // }) 31 | } 32 | //1-首页-Banner 33 | getBanner(): Promise { 34 | return new Promise(async (resolve: Function, reject: Function) => { 35 | bannerReq().then((data: ResponseResult) => { 36 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 37 | resolve(data.data); 38 | } else { 39 | Logger.error('getBanner failed', JSON.stringify(data)); 40 | reject($r('app.string.page_none_msg')); 41 | } 42 | }) 43 | }) 44 | } 45 | //2-首页-置顶 46 | getArticleTop(): Promise { 47 | return new Promise(async (resolve: Function, reject: Function) => { 48 | articleTopReq().then((data: ResponseResult) => { 49 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 50 | resolve(data.data); 51 | } else { 52 | Logger.error('getArticleTop failed', JSON.stringify(data)); 53 | reject($r('app.string.page_none_msg')); 54 | } 55 | }) 56 | }) 57 | } 58 | //3-首页-文章 59 | getArticle(page: number): Promise { 60 | return new Promise(async (resolve: Function, reject: Function) => { 61 | articleListReq(page).then((data: ResponseResult) => { 62 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 63 | resolve(data.data); 64 | } else { 65 | Logger.error('getArticle failed', JSON.stringify(data)); 66 | reject($r('app.string.page_none_msg')); 67 | } 68 | }) 69 | }) 70 | } 71 | } 72 | 73 | let homeViewModel = new HomeViewModel(); 74 | 75 | export default homeViewModel as HomeViewModel; 76 | 77 | -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/InfoViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * 积分排行或个人积分-数据类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { coinReq, rankReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { CoinData } from '../bean/CoinBean'; 8 | import { RankData } from '../bean/RankBean'; 9 | import Logger from '../utils/Logger'; 10 | 11 | class InfoViewModel { 12 | //1-排行榜 13 | getRank(page: number): Promise { 14 | return new Promise(async (resolve: Function, reject: Function) => { 15 | rankReq(page).then((data: ResponseResult) => { 16 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 17 | resolve(data.data); 18 | } else { 19 | Logger.error('getRank failed', JSON.stringify(data)); 20 | reject($r('app.string.page_none_msg')); 21 | } 22 | }) 23 | }) 24 | } 25 | 26 | //2-签到积分 27 | getCoin(page: number): Promise { 28 | return new Promise(async (resolve: Function, reject: Function) => { 29 | coinReq(page).then((data: ResponseResult) => { 30 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 31 | resolve(data.data); 32 | } else { 33 | Logger.error('getRank failed', JSON.stringify(data)); 34 | reject($r('app.string.page_none_msg')); 35 | } 36 | }) 37 | }) 38 | } 39 | } 40 | 41 | let infoViewModel = new InfoViewModel(); 42 | 43 | export default infoViewModel as InfoViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/LoginRegisterViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:5-登录、注册-数据逻辑类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { loginReq, registerReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { UserInfo } from '../bean/UserInfoBean'; 8 | import Logger from '../utils/Logger'; 9 | 10 | 11 | class LoginRegisterViewModel { 12 | 13 | //用户登录 14 | userLogin(username: string, password: string): Promise { 15 | return new Promise(async (resolve: Function, reject: Function) => { 16 | loginReq(username, password).then((data: ResponseResult) => { 17 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 18 | resolve(data); 19 | } else { 20 | Logger.error('userLogin failed', JSON.stringify(data)); 21 | resolve(data); 22 | //reject($r('app.string.page_none_msg')); 23 | } 24 | }) 25 | }) 26 | } 27 | //用户注册 28 | userRegister(username: string, password: string, repassword: string): Promise { 29 | return new Promise(async (resolve: Function, reject: Function) => { 30 | registerReq(username, password, repassword).then((data: ResponseResult) => { 31 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 32 | resolve(data); 33 | } else { 34 | Logger.error('userRegister failed', JSON.stringify(data)); 35 | resolve(data); 36 | //reject($r('app.string.page_none_msg')); 37 | } 38 | }) 39 | }) 40 | } 41 | } 42 | 43 | let loginRegisterViewModel = new LoginRegisterViewModel(); 44 | 45 | export default loginRegisterViewModel as LoginRegisterViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/MeViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:5-Me-数据逻辑类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { userReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { UserData, UserInfo } from '../bean/UserInfoBean'; 8 | import Logger from '../utils/Logger'; 9 | 10 | class MeViewModel { 11 | 12 | 13 | //5-我的页面数据接口 14 | getUserInfo(): Promise { 15 | return new Promise(async (resolve: Function, reject: Function) => { 16 | userReq().then((data: ResponseResult) => { 17 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 18 | resolve(data.data); 19 | } else { 20 | Logger.error('getUserInfo failed', JSON.stringify(data)); 21 | reject($r('app.string.page_none_msg')); 22 | } 23 | }) 24 | }) 25 | } 26 | } 27 | 28 | let meViewModel = new MeViewModel(); 29 | 30 | export default meViewModel as MeViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/MsgViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:4-Msg-数据逻辑类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { msgReadReq, msgUnReadReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { MsgData } from '../bean/MsgBean'; 8 | import Logger from '../utils/Logger'; 9 | 10 | class MsgViewModel { 11 | 12 | //1-导航页面数据接口 13 | 14 | //未读消息 15 | getMsgUnRead(): Promise { 16 | return new Promise(async (resolve: Function, reject: Function) => { 17 | msgUnReadReq().then((data: ResponseResult) => { 18 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 19 | resolve(data.data); 20 | } else { 21 | Logger.error('getMsgUnRead failed', JSON.stringify(data)); 22 | reject($r('app.string.page_none_msg')); 23 | } 24 | }) 25 | }) 26 | } 27 | 28 | //已读消息 29 | getMsgRead(page:number): Promise { 30 | return new Promise(async (resolve: Function, reject: Function) => { 31 | msgReadReq(page).then((data: ResponseResult) => { 32 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 33 | resolve(data.data); 34 | } else { 35 | Logger.error('getMsgUnRead failed', JSON.stringify(data)); 36 | reject($r('app.string.page_none_msg')); 37 | } 38 | }) 39 | }) 40 | } 41 | } 42 | 43 | let msgViewModel = new MsgViewModel(); 44 | 45 | export default msgViewModel as MsgViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/NavigatorModel.ets: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Huawei Device Co., Ltd. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | /** 17 | * NewsData params info. 18 | */ 19 | export class NavigatorModel { 20 | /** 21 | * Jumping Path. 22 | */ 23 | path: Resource | string = ''; 24 | 25 | /** 26 | * Prompt message. 27 | */ 28 | title: Resource | string = ''; 29 | } -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/ProjectViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:3-Project-数据逻辑类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { projectListReq, projectReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { ArticleList } from '../bean/ArticleBean'; 8 | import { ProjectData, ProjectItem } from '../bean/ProjectBean'; 9 | import Logger from '../utils/Logger'; 10 | 11 | class ProjectViewModel { 12 | 13 | //3-项目页面数据接口 14 | 15 | //项目分类 16 | getProject(): Promise { 17 | return new Promise(async (resolve: Function, reject: Function) => { 18 | projectReq().then((data: ResponseResult) => { 19 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 20 | resolve(data.data); 21 | } else { 22 | Logger.error('getProject failed', JSON.stringify(data)); 23 | reject($r('app.string.page_none_msg')); 24 | } 25 | }) 26 | }) 27 | } 28 | //项目列表数据 29 | getProjectList(page: number, cid: number): Promise { 30 | return new Promise(async (resolve: Function, reject: Function) => { 31 | projectListReq(page = page, cid = cid).then((data: ResponseResult) => { 32 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 33 | resolve(data.data); 34 | } else { 35 | Logger.error('getProjectList failed', JSON.stringify(data)); 36 | reject($r('app.string.page_none_msg')); 37 | } 38 | }) 39 | }) 40 | } 41 | } 42 | 43 | let projectViewModel = new ProjectViewModel(); 44 | 45 | export default projectViewModel as ProjectViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/SettingViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:7-Setting-数据逻辑类 3 | */ 4 | import router from '@ohos.router'; 5 | import { logoutReq } from '../api/HttpRequest'; 6 | import SettingItem from '../bean/SettingItem'; 7 | import Constants from '../common/constants/Constants'; 8 | 9 | class SettingViewModel { 10 | logout() { 11 | logoutReq().then(result => { 12 | //清除cookie等数据 13 | if (AppStorage.Has(Constants.Cookie)) { 14 | AppStorage.Set(Constants.Cookie, '') 15 | PersistentStorage.DeleteProp(Constants.Cookie) 16 | //AppStorage.Clear() 17 | //router.back() 18 | router.replaceUrl({ url: Constants.MainPage_Path }); 19 | } 20 | }) 21 | } 22 | 23 | /** 24 | * @desc:设置Items数据 25 | * @returns 26 | */ 27 | getSettingListData(): Array { 28 | let settingListData: SettingItem[] = [ 29 | new SettingItem($r('app.string.setting_list_theme'), $r('app.media.ic_theme'), $r(null)), 30 | new SettingItem($r('app.string.setting_list_language'), $r('app.media.ic_language'), $r(null)), 31 | new SettingItem($r('app.string.setting_list_about'), $r('app.media.ic_about'), $r(null)), 32 | ]; 33 | return settingListData; 34 | } 35 | } 36 | 37 | let settingViewModel = new SettingViewModel(); 38 | 39 | export default settingViewModel as SettingViewModel; -------------------------------------------------------------------------------- /entry/src/main/ets/viewmodel/TreeViewModel.ets: -------------------------------------------------------------------------------- 1 | /** 2 | * @desc:2-Nav-数据逻辑类 3 | */ 4 | import HttpConfig from '../api/HttpConfig'; 5 | import { treeListReq, treeReq } from '../api/HttpRequest'; 6 | import ResponseResult from '../api/ResponseResult'; 7 | import { TreeArticle, TreeData, TreeItem } from '../bean/TreeBean'; 8 | import Logger from '../utils/Logger'; 9 | 10 | class TreeViewModel { 11 | 12 | //2-Tree(导航)页面数据接口 13 | 14 | getTreeData(): Promise { 15 | return new Promise(async (resolve: Function, reject: Function) => { 16 | treeReq().then((data: ResponseResult) => { 17 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 18 | resolve(data.data); 19 | } else { 20 | Logger.error('getTreeData failed', JSON.stringify(data)); 21 | reject($r('app.string.page_none_msg')); 22 | } 23 | }) 24 | }) 25 | } 26 | //2-知识体系下的文章 27 | getTreeArticleList(page: number, cid: number): Promise { 28 | return new Promise(async (resolve: Function, reject: Function) => { 29 | treeListReq(page = page, cid = cid).then((data: ResponseResult) => { 30 | if (data.errorCode === HttpConfig.SERVER_CODE_SUCCESS) { 31 | resolve(data.data); 32 | } else { 33 | Logger.error('getTreeArticleList failed', JSON.stringify(data)); 34 | reject($r('app.string.page_none_msg')); 35 | } 36 | }) 37 | }) 38 | } 39 | } 40 | 41 | let treeViewModel = new TreeViewModel(); 42 | 43 | export default treeViewModel as TreeViewModel; -------------------------------------------------------------------------------- /entry/src/main/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry", 4 | "type": "entry", 5 | "description": "$string:module_desc", 6 | "mainElement": "EntryAbility", 7 | "deviceTypes": [ 8 | "phone", 9 | "tablet" 10 | ], 11 | "deliveryWithInstall": true, 12 | "installationFree": false, 13 | 14 | "pages": "$profile:main_pages", 15 | "abilities": [ 16 | { 17 | "name": "EntryAbility", 18 | "srcEntry": "./ets/entryability/EntryAbility.ets", 19 | "description": "$string:EntryAbility_desc", 20 | "icon": "$media:ic_logo", 21 | "label": "$string:EntryAbility_label", 22 | "startWindowIcon": "$media:ic_logo", 23 | "startWindowBackground": "$color:start_window_background", 24 | "exported": true, 25 | "skills": [ 26 | { 27 | "entities": [ 28 | "entity.system.home" 29 | ], 30 | "actions": [ 31 | "action.system.home" 32 | ] 33 | } 34 | ] 35 | } 36 | ], 37 | "requestPermissions": [ 38 | { 39 | "name": "ohos.permission.INTERNET", 40 | "usedScene": { 41 | "when": "always" 42 | } 43 | } 44 | ] 45 | } 46 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | }, 7 | { 8 | "name": "bottom_tabs_font_color_selected", 9 | "value": "#007DFF" 10 | }, 11 | { 12 | "name": "color_border", 13 | "value": "#FF182431" 14 | }, 15 | { 16 | "name": "bottom_tabs_background_color", 17 | "value": "#F1F3F5" 18 | }, 19 | { 20 | "name": "bottom_tabs_font_color", 21 | "value": "#000000" 22 | }, 23 | { 24 | "name": "light_pink", 25 | "value": "#FFB6C1" 26 | }, 27 | { 28 | "name": "pink", 29 | "value": "#FFC0CB" 30 | }, 31 | { 32 | "name": "crimson", 33 | "value": "#DC143C" 34 | }, 35 | { 36 | "name": "lavender_blush", 37 | "value": "#FF0F50" 38 | }, 39 | { 40 | "name": "paleViolet_red", 41 | "value": "#DB7093" 42 | }, 43 | { 44 | "name": "hot_pink", 45 | "value": "#FF69B4" 46 | }, 47 | { 48 | "name": "deep_pink", 49 | "value": "#FF1493" 50 | }, 51 | { 52 | "name": "medium_violet_red", 53 | "value": "#C71585" 54 | }, 55 | { 56 | "name": "orchid", 57 | "value": "#DA70D6" 58 | }, 59 | { 60 | "name": "thistle", 61 | "value": "#D8BFD8" 62 | }, 63 | { 64 | "name": "plum", 65 | "value": "#DDA0DD" 66 | }, 67 | { 68 | "name": "violet", 69 | "value": "#EE82EE" 70 | }, 71 | { 72 | "name": "magenta", 73 | "value": "#FF00FF" 74 | }, 75 | { 76 | "name": "fuchsia", 77 | "value": "#FF00FF" 78 | }, 79 | { 80 | "name": "dark_magenta", 81 | "value": "#8B008B" 82 | }, 83 | { 84 | "name": "purple", 85 | "value": "#800080" 86 | }, 87 | { 88 | "name": "medium_orchid", 89 | "value": "#BA55D3" 90 | }, 91 | { 92 | "name": "dark_violet", 93 | "value": "#9400D3" 94 | }, 95 | { 96 | "name": "dark_orchid", 97 | "value": "#9932CC" 98 | }, 99 | { 100 | "name": "indigo", 101 | "value": "#4B0082" 102 | }, 103 | { 104 | "name": "blue_violet", 105 | "value": "#8A2BE2" 106 | }, 107 | { 108 | "name": "medium_purple", 109 | "value": "#9370DB" 110 | }, 111 | { 112 | "name": "medium_slate_blue", 113 | "value": "#7B68EE" 114 | }, 115 | { 116 | "name": "slate_blue", 117 | "value": "#6A5ACD" 118 | }, 119 | { 120 | "name": "dark_slate_blue", 121 | "value": "#483D8B" 122 | }, 123 | { 124 | "name": "lavender", 125 | "value": "#E6E6FA" 126 | }, 127 | { 128 | "name": "ghost_white", 129 | "value": "#F8F8FF" 130 | }, 131 | { 132 | "name": "blue", 133 | "value": "#0000FF" 134 | }, 135 | { 136 | "name": "indigo_blue", 137 | "value": "#0000FF" 138 | }, 139 | { 140 | "name": "medium_blue", 141 | "value": "#0000CD" 142 | }, 143 | { 144 | "name": "midnight_blue", 145 | "value": "#191970" 146 | }, 147 | { 148 | "name": "dark_blue", 149 | "value": "#00008B" 150 | }, 151 | { 152 | "name": "navy", 153 | "value": "#000080" 154 | }, 155 | { 156 | "name": "royal_blue", 157 | "value": "#4169E1" 158 | }, 159 | { 160 | "name": "cornflower_blue", 161 | "value": "#6495ED" 162 | }, 163 | { 164 | "name": "light_steel_blue", 165 | "value": "#B0C4DE" 166 | }, 167 | { 168 | "name": "light_slate_gray", 169 | "value": "#778899" 170 | }, 171 | { 172 | "name": "slate_gray", 173 | "value": "#708090" 174 | }, 175 | { 176 | "name": "dodger_blue", 177 | "value": "#1E90FF" 178 | }, 179 | { 180 | "name": "steel_blue", 181 | "value": "#4682B4" 182 | }, 183 | { 184 | "name": "light_sky_blue", 185 | "value": "#87CEFA" 186 | }, 187 | { 188 | "name": "sky_blue", 189 | "value": "#87CEEB" 190 | }, 191 | { 192 | "name": "deep_sky_blue", 193 | "value": "#00BFFF" 194 | }, 195 | { 196 | "name": "light_blue", 197 | "value": "#ADD8E6" 198 | }, 199 | { 200 | "name": "powder_blue", 201 | "value": "#B0E0E6" 202 | }, 203 | { 204 | "name": "cadet_blue", 205 | "value": "#5F9EA0" 206 | }, 207 | { 208 | "name": "azure", 209 | "value": "#F0FFFF" 210 | }, 211 | { 212 | "name": "light_cyan", 213 | "value": "#E1FFFF" 214 | }, 215 | { 216 | "name": "pale_turquoise", 217 | "value": "#AFEEEE" 218 | }, 219 | { 220 | "name": "cyan", 221 | "value": "#00FFFF" 222 | }, 223 | { 224 | "name": "aqua", 225 | "value": "#00FFFF" 226 | }, 227 | { 228 | "name": "dark_turquoise", 229 | "value": "#00CED1" 230 | }, 231 | { 232 | "name": "dark_slate_gray", 233 | "value": "#2F4F4F" 234 | }, 235 | { 236 | "name": "dark_cyan", 237 | "value": "#008B8B" 238 | }, 239 | { 240 | "name": "teal", 241 | "value": "#008080" 242 | }, 243 | { 244 | "name": "medium_turquoise", 245 | "value": "#48D1CC" 246 | }, 247 | { 248 | "name": "light_sea_green", 249 | "value": "#20B2AA" 250 | }, 251 | { 252 | "name": "turquoise", 253 | "value": "#40E0D0" 254 | }, 255 | { 256 | "name": "auqamarin", 257 | "value": "#7FFFAA" 258 | }, 259 | { 260 | "name": "medium_aquamarine", 261 | "value": "#00FA9A" 262 | }, 263 | { 264 | "name": "medium_spring_green", 265 | "value": "#00FF7F" 266 | }, 267 | { 268 | "name": "mint_cream", 269 | "value": "#F5FFFA" 270 | }, 271 | { 272 | "name": "spring_green", 273 | "value": "#3CB371" 274 | }, 275 | { 276 | "name": "sea_green", 277 | "value": "#2E8B57" 278 | }, 279 | { 280 | "name": "honeydew", 281 | "value": "#F0FFF0" 282 | }, 283 | { 284 | "name": "light_green", 285 | "value": "#90EE90" 286 | }, 287 | { 288 | "name": "pale_green", 289 | "value": "#98FB98" 290 | }, 291 | { 292 | "name": "dark_sea_green", 293 | "value": "#8FBC8F" 294 | }, 295 | { 296 | "name": "lime_green", 297 | "value": "#32CD32" 298 | }, 299 | { 300 | "name": "lime", 301 | "value": "#00FF00" 302 | }, 303 | { 304 | "name": "forest_green", 305 | "value": "#228B22" 306 | }, 307 | { 308 | "name": "green", 309 | "value": "#008000" 310 | }, 311 | { 312 | "name": "dark_green", 313 | "value": "#006400" 314 | }, 315 | { 316 | "name": "chartreuse", 317 | "value": "#7FFF00" 318 | }, 319 | { 320 | "name": "lawn_green", 321 | "value": "#7CFC00" 322 | }, 323 | { 324 | "name": "green_yellow", 325 | "value": "#ADFF2F" 326 | }, 327 | { 328 | "name": "olive_drab", 329 | "value": "#556B2F" 330 | }, 331 | { 332 | "name": "beige", 333 | "value": "#F5F5DC" 334 | }, 335 | { 336 | "name": "light_goldenrod_yellow", 337 | "value": "#FAFAD2" 338 | }, 339 | { 340 | "name": "ivory", 341 | "value": "#FFFFF0" 342 | }, 343 | { 344 | "name": "light_yellow", 345 | "value": "#FFFFE0" 346 | }, 347 | { 348 | "name": "yellow", 349 | "value": "#FFFF00" 350 | }, 351 | { 352 | "name": "olive", 353 | "value": "#808000" 354 | }, 355 | { 356 | "name": "dark_khaki", 357 | "value": "#BDB76B" 358 | }, 359 | { 360 | "name": "lemon_chiffon", 361 | "value": "#FFFACD" 362 | }, 363 | { 364 | "name": "pale_godenrod", 365 | "value": "#EEE8AA" 366 | }, 367 | { 368 | "name": "khaki", 369 | "value": "#F0E68C" 370 | }, 371 | { 372 | "name": "gold", 373 | "value": "#FFD700" 374 | }, 375 | { 376 | "name": "cornislk", 377 | "value": "#FFF8DC" 378 | }, 379 | { 380 | "name": "gold_enrod", 381 | "value": "#DAA520" 382 | }, 383 | { 384 | "name": "floral_white", 385 | "value": "#FFFAF0" 386 | }, 387 | { 388 | "name": "oldLace", 389 | "value": "#FDF5E6" 390 | }, 391 | { 392 | "name": "wheat", 393 | "value": "#F5DEB3" 394 | }, 395 | { 396 | "name": "moccasin", 397 | "value": "#FFE4B5" 398 | }, 399 | { 400 | "name": "orange", 401 | "value": "#FFA500" 402 | }, 403 | { 404 | "name": "papaya_whip", 405 | "value": "#FFEFD5" 406 | }, 407 | { 408 | "name": "blanched_almond", 409 | "value": "#FFEBCD" 410 | }, 411 | { 412 | "name": "navajo_white", 413 | "value": "#FFDEAD" 414 | }, 415 | { 416 | "name": "antique_white", 417 | "value": "#FAEBD7" 418 | }, 419 | { 420 | "name": "tan", 421 | "value": "#D2B48C" 422 | }, 423 | { 424 | "name": "bruly_wood", 425 | "value": "#DEB887" 426 | }, 427 | { 428 | "name": "bisque", 429 | "value": "#FFE4C4" 430 | }, 431 | { 432 | "name": "dark_orange", 433 | "value": "#FF8C00" 434 | }, 435 | { 436 | "name": "linen", 437 | "value": "#FAF0E6" 438 | }, 439 | { 440 | "name": "peru", 441 | "value": "#CD853F" 442 | }, 443 | { 444 | "name": "peach_puff", 445 | "value": "#FFDAB9" 446 | }, 447 | { 448 | "name": "sandy_brown", 449 | "value": "#F4A460" 450 | }, 451 | { 452 | "name": "chocolate", 453 | "value": "#D2691E" 454 | }, 455 | { 456 | "name": "saddle_brown", 457 | "value": "#8B4513" 458 | }, 459 | { 460 | "name": "sea_shell", 461 | "value": "#FFF5EE" 462 | }, 463 | { 464 | "name": "sienna", 465 | "value": "#A0522D" 466 | }, 467 | { 468 | "name": "light_salmon", 469 | "value": "#FFA07A" 470 | }, 471 | { 472 | "name": "coral", 473 | "value": "#FF7F50" 474 | }, 475 | { 476 | "name": "orange_red", 477 | "value": "#FF4500" 478 | }, 479 | { 480 | "name": "dark_salmon", 481 | "value": "#E9967A" 482 | }, 483 | { 484 | "name": "tomato", 485 | "value": "#FF6347" 486 | }, 487 | { 488 | "name": "misty_rose", 489 | "value": "#FFE4E1" 490 | }, 491 | { 492 | "name": "salmon", 493 | "value": "#FA8072" 494 | }, 495 | { 496 | "name": "snow", 497 | "value": "#FFFAFA" 498 | }, 499 | { 500 | "name": "light_coral", 501 | "value": "#FFFAFA" 502 | }, 503 | { 504 | "name": "rosy_brown", 505 | "value": "#BC8F8F" 506 | }, 507 | { 508 | "name": "indian_red", 509 | "value": "#CD5C5C" 510 | }, 511 | { 512 | "name": "red", 513 | "value": "#FF0000" 514 | }, 515 | { 516 | "name": "brown", 517 | "value": "#A52A2A" 518 | }, 519 | { 520 | "name": "fire_brick", 521 | "value": "#B22222" 522 | }, 523 | { 524 | "name": "dark_red", 525 | "value": "#8B0000" 526 | }, 527 | { 528 | "name": "maroon", 529 | "value": "#800000" 530 | }, 531 | { 532 | "name": "white", 533 | "value": "#FFFFFF" 534 | }, 535 | { 536 | "name": "white_smoke", 537 | "value": "#F5F5F5" 538 | }, 539 | { 540 | "name": "gainsboro", 541 | "value": "#DCDCDC" 542 | }, 543 | { 544 | "name": "light_gray", 545 | "value": "#D3D3D3" 546 | }, 547 | { 548 | "name": "silver", 549 | "value": "#C0C0C0" 550 | }, 551 | { 552 | "name": "dark_gray", 553 | "value": "#A9A9A9" 554 | }, 555 | { 556 | "name": "gray", 557 | "value": "#808080" 558 | }, 559 | { 560 | "name": "dim_gray", 561 | "value": "#696969" 562 | }, 563 | { 564 | "name": "black", 565 | "value": "#000000" 566 | } 567 | ] 568 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/element/float.json: -------------------------------------------------------------------------------- 1 | { 2 | "float": [ 3 | { 4 | "name": "bottom_font_size", 5 | "value": "10fp" 6 | }, 7 | { 8 | "name": "font_size_24fp", 9 | "value": "24fp" 10 | }, 11 | { 12 | "name": "font_size_16fp", 13 | "value": "16fp" 14 | }, 15 | { 16 | "name": "1fp", 17 | "value": "1fp" 18 | }, 19 | { 20 | "name": "2fp", 21 | "value": "2fp" 22 | }, 23 | { 24 | "name": "3fp", 25 | "value": "3fp" 26 | }, 27 | { 28 | "name": "4fp", 29 | "value": "4fp" 30 | }, 31 | { 32 | "name": "5fp", 33 | "value": "5fp" 34 | }, 35 | { 36 | "name": "6fp", 37 | "value": "6fp" 38 | }, 39 | { 40 | "name": "7fp", 41 | "value": "7fp" 42 | }, 43 | { 44 | "name": "8fp", 45 | "value": "8fp" 46 | }, 47 | { 48 | "name": "9fp", 49 | "value": "9fp" 50 | }, 51 | { 52 | "name": "10fp", 53 | "value": "10fp" 54 | }, 55 | { 56 | "name": "11fp", 57 | "value": "11fp" 58 | }, 59 | { 60 | "name": "12fp", 61 | "value": "12fp" 62 | }, 63 | { 64 | "name": "13fp", 65 | "value": "13fp" 66 | }, 67 | { 68 | "name": "14fp", 69 | "value": "14fp" 70 | }, 71 | { 72 | "name": "15fp", 73 | "value": "15fp" 74 | }, 75 | { 76 | "name": "16fp", 77 | "value": "16fp" 78 | }, 79 | { 80 | "name": "17fp", 81 | "value": "17fp" 82 | }, 83 | { 84 | "name": "18fp", 85 | "value": "18fp" 86 | }, 87 | { 88 | "name": "19fp", 89 | "value": "19fp" 90 | }, 91 | { 92 | "name": "20fp", 93 | "value": "20fp" 94 | }, 95 | { 96 | "name": "21fp", 97 | "value": "21fp" 98 | }, 99 | { 100 | "name": "22fp", 101 | "value": "22fp" 102 | }, 103 | { 104 | "name": "23fp", 105 | "value": "23fp" 106 | }, 107 | { 108 | "name": "24fp", 109 | "value": "24fp" 110 | }, 111 | { 112 | "name": "25fp", 113 | "value": "25fp" 114 | }, 115 | { 116 | "name": "1vp", 117 | "value": "1vp" 118 | }, 119 | { 120 | "name": "2vp", 121 | "value": "2vp" 122 | }, 123 | { 124 | "name": "3vp", 125 | "value": "3vp" 126 | }, 127 | { 128 | "name": "4vp", 129 | "value": "4vp" 130 | }, 131 | { 132 | "name": "5vp", 133 | "value": "5vp" 134 | }, 135 | { 136 | "name": "6vp", 137 | "value": "6vp" 138 | }, 139 | { 140 | "name": "7vp", 141 | "value": "7vp" 142 | }, 143 | { 144 | "name": "8vp", 145 | "value": "8vp" 146 | }, 147 | { 148 | "name": "9vp", 149 | "value": "9vp" 150 | }, 151 | { 152 | "name": "10vp", 153 | "value": "10vp" 154 | }, 155 | { 156 | "name": "11vp", 157 | "value": "11vp" 158 | }, 159 | { 160 | "name": "12vp", 161 | "value": "12vp" 162 | }, 163 | { 164 | "name": "13vp", 165 | "value": "13vp" 166 | }, 167 | { 168 | "name": "14vp", 169 | "value": "14vp" 170 | }, 171 | { 172 | "name": "15vp", 173 | "value": "15vp" 174 | }, 175 | { 176 | "name": "16vp", 177 | "value": "16vp" 178 | }, 179 | { 180 | "name": "17vp", 181 | "value": "17vp" 182 | }, 183 | { 184 | "name": "18vp", 185 | "value": "18vp" 186 | }, 187 | { 188 | "name": "19vp", 189 | "value": "19vp" 190 | }, 191 | { 192 | "name": "20vp", 193 | "value": "20vp" 194 | }, 195 | { 196 | "name": "21vp", 197 | "value": "21vp" 198 | }, 199 | { 200 | "name": "22vp", 201 | "value": "22vp" 202 | }, 203 | { 204 | "name": "23vp", 205 | "value": "24vp" 206 | }, 207 | { 208 | "name": "24vp", 209 | "value": "24vp" 210 | }, 211 | { 212 | "name": "25vp", 213 | "value": "25vp" 214 | }, 215 | { 216 | "name": "26vp", 217 | "value": "26vp" 218 | }, 219 | { 220 | "name": "27vp", 221 | "value": "27vp" 222 | }, 223 | { 224 | "name": "28vp", 225 | "value": "28vp" 226 | }, 227 | { 228 | "name": "29vp", 229 | "value": "29vp" 230 | }, 231 | { 232 | "name": "30vp", 233 | "value": "30vp" 234 | }, 235 | { 236 | "name": "40vp", 237 | "value": "40vp" 238 | }, 239 | { 240 | "name": "41vp", 241 | "value": "41vp" 242 | }, 243 | { 244 | "name": "42vp", 245 | "value": "42vp" 246 | }, 247 | { 248 | "name": "43vp", 249 | "value": "43vp" 250 | }, 251 | { 252 | "name": "44vp", 253 | "value": "44vp" 254 | }, 255 | { 256 | "name": "45vp", 257 | "value": "45vp" 258 | }, 259 | { 260 | "name": "46vp", 261 | "value": "46vp" 262 | }, 263 | { 264 | "name": "47vp", 265 | "value": "47vp" 266 | }, 267 | { 268 | "name": "48vp", 269 | "value": "48vp" 270 | }, 271 | { 272 | "name": "50vp", 273 | "value": "50vp" 274 | }, 275 | { 276 | "name": "60vp", 277 | "value": "60vp" 278 | }, 279 | { 280 | "name": "70vp", 281 | "value": "70vp" 282 | }, 283 | { 284 | "name": "80vp", 285 | "value": "80vp" 286 | }, 287 | { 288 | "name": "81vp", 289 | "value": "81vp" 290 | }, 291 | { 292 | "name": "82vp", 293 | "value": "82vp" 294 | }, 295 | { 296 | "name": "83vp", 297 | "value": "83vp" 298 | }, 299 | { 300 | "name": "84vp", 301 | "value": "84vp" 302 | }, 303 | { 304 | "name": "85vp", 305 | "value": "85vp" 306 | }, 307 | { 308 | "name": "86vp", 309 | "value": "86vp" 310 | }, 311 | { 312 | "name": "87vp", 313 | "value": "87vp" 314 | }, 315 | { 316 | "name": "88vp", 317 | "value": "88vp" 318 | }, 319 | { 320 | "name": "89vp", 321 | "value": "89vp" 322 | }, 323 | { 324 | "name": "90vp", 325 | "value": "90vp" 326 | }, 327 | { 328 | "name": "100vp", 329 | "value": "100vp" 330 | }, 331 | { 332 | "name": "110vp", 333 | "value": "110vp" 334 | }, 335 | { 336 | "name": "120vp", 337 | "value": "120vp" 338 | }, 339 | { 340 | "name": "130vp", 341 | "value": "130vp" 342 | }, 343 | { 344 | "name": "140vp", 345 | "value": "140vp" 346 | }, 347 | { 348 | "name": "150vp", 349 | "value": "150vp" 350 | }, 351 | { 352 | "name": "192vp", 353 | "value": "192vp" 354 | } 355 | ] 356 | } -------------------------------------------------------------------------------- /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": "WanAndroidHM" 14 | }, 15 | { 16 | "name": "bottom_text_home", 17 | "value": "首页" 18 | }, 19 | { 20 | "name": "bottom_text_nav", 21 | "value": "导航" 22 | }, 23 | { 24 | "name": "bottom_text_project", 25 | "value": "项目" 26 | }, 27 | { 28 | "name": "bottom_text_msg", 29 | "value": "消息" 30 | }, 31 | { 32 | "name": "bottom_text_me", 33 | "value": "我的" 34 | }, 35 | { 36 | "name": "http_error_message", 37 | "value": "网络请求失败,请稍后尝试!" 38 | }, 39 | { 40 | "name": "page_none_msg", 41 | "value": "网络加载失败" 42 | }, 43 | { 44 | "name": "input_empty_tips", 45 | "value": "输入不能为空" 46 | }, 47 | { 48 | "name": "login_page", 49 | "value": "登录界面" 50 | }, 51 | { 52 | "name": "register_page", 53 | "value": "注册界面" 54 | }, 55 | { 56 | "name": "login_more", 57 | "value": "登录帐号以使用更多服务" 58 | }, 59 | { 60 | "name": "register_more", 61 | "value": "注册帐号以使用更多服务" 62 | }, 63 | { 64 | "name": "account", 65 | "value": "帐号" 66 | }, 67 | { 68 | "name": "password", 69 | "value": "密码" 70 | }, 71 | { 72 | "name": "repassword", 73 | "value": "确认密码" 74 | }, 75 | { 76 | "name": "login", 77 | "value": "登录" 78 | }, 79 | { 80 | "name": "register", 81 | "value": "注册" 82 | }, 83 | { 84 | "name": "register_account", 85 | "value": "没有账号,去注册" 86 | }, 87 | { 88 | "name": "login_account", 89 | "value": "已有账号,去登录" 90 | }, 91 | { 92 | "name": "internet_err", 93 | "value": "网络加载失败!" 94 | }, 95 | { 96 | "name": "setting_list_theme", 97 | "value": "主题" 98 | }, 99 | { 100 | "name": "setting_list_language", 101 | "value": "语言" 102 | }, 103 | { 104 | "name": "setting_list_about", 105 | "value": "关于" 106 | }, 107 | { 108 | "name": "no_more_data", 109 | "value": "没有更多数据了" 110 | } 111 | ] 112 | } -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_about.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_arrow_left.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_arrow_right.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_clock.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_home_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_home_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_home_selected.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_language.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_logo.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_me_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_me_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_me_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_me_selected.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_msg_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_msg_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_msg_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_msg_selected.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_nav_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_nav_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_nav_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_nav_selected.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_project_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_project_normal.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_project_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_project_selected.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_setting.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_theme.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/ic_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/ic_user.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/icon.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/media/splash_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/main/resources/base/media/splash_bg.png -------------------------------------------------------------------------------- /entry/src/main/resources/base/profile/main_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "pages/Splash", 4 | "pages/MainPage", 5 | "pages/LoginRegisterPage", 6 | "pages/WebPage", 7 | "pages/SettingPage", 8 | "pages/TreeTabPage", 9 | "pages/InfoPage" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /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": "WanAndroidHM" 14 | }, 15 | { 16 | "name": "bottom_text_home", 17 | "value": "Home" 18 | }, 19 | { 20 | "name": "bottom_text_nav", 21 | "value": "Nav" 22 | }, 23 | { 24 | "name": "bottom_text_project", 25 | "value": "Project" 26 | }, 27 | { 28 | "name": "bottom_text_msg", 29 | "value": "Msg" 30 | }, 31 | { 32 | "name": "bottom_text_me", 33 | "value": "Me" 34 | }, 35 | { 36 | "name": "http_error_message", 37 | "value": "Network request failed, please try later!" 38 | }, 39 | { 40 | "name": "page_none_msg", 41 | "value": "Network loading failure" 42 | }, 43 | { 44 | "name": "input_empty_tips", 45 | "value": "The input cannot be empty." 46 | }, 47 | { 48 | "name": "login_page", 49 | "value": "Login page" 50 | }, 51 | { 52 | "name": "register_page", 53 | "value": "register page" 54 | }, 55 | { 56 | "name": "login_more", 57 | "value": "Log in to your account to use more services" 58 | }, 59 | { 60 | "name": "register_more", 61 | "value": "register to your account to use more services" 62 | }, 63 | { 64 | "name": "account", 65 | "value": "Account" 66 | }, 67 | { 68 | "name": "password", 69 | "value": "Password" 70 | }, 71 | { 72 | "name": "repassword", 73 | "value": "repassword" 74 | }, 75 | { 76 | "name": "login", 77 | "value": "Log in" 78 | }, 79 | { 80 | "name": "register", 81 | "value": "register" 82 | }, 83 | { 84 | "name": "register_account", 85 | "value": "Registering an Account" 86 | }, 87 | { 88 | "name": "login_account", 89 | "value": "login an Account" 90 | }, 91 | { 92 | "name": "internet_err", 93 | "value": "Failed to load the network." 94 | }, 95 | { 96 | "name": "setting_list_theme", 97 | "value": "Theme" 98 | }, 99 | { 100 | "name": "setting_list_language", 101 | "value": "Language" 102 | }, 103 | { 104 | "name": "setting_list_about", 105 | "value": "About" 106 | }, 107 | { 108 | "name": "no_more_data", 109 | "value": "No More Data" 110 | } 111 | ] 112 | } -------------------------------------------------------------------------------- /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": "WanAndroidHM" 14 | }, 15 | { 16 | "name": "bottom_text_home", 17 | "value": "首页" 18 | }, 19 | { 20 | "name": "bottom_text_nav", 21 | "value": "导航" 22 | }, 23 | { 24 | "name": "bottom_text_project", 25 | "value": "项目" 26 | }, 27 | { 28 | "name": "bottom_text_msg", 29 | "value": "消息" 30 | }, 31 | { 32 | "name": "bottom_text_me", 33 | "value": "我的" 34 | }, 35 | { 36 | "name": "http_error_message", 37 | "value": "网络请求失败,请稍后尝试!" 38 | }, 39 | { 40 | "name": "page_none_msg", 41 | "value": "网络加载失败" 42 | }, 43 | { 44 | "name": "input_empty_tips", 45 | "value": "输入不能为空" 46 | }, 47 | { 48 | "name": "login_page", 49 | "value": "登录界面" 50 | }, 51 | { 52 | "name": "register_page", 53 | "value": "注册界面" 54 | }, 55 | { 56 | "name": "login_more", 57 | "value": "登录帐号以使用更多服务" 58 | }, 59 | { 60 | "name": "register_more", 61 | "value": "注册帐号以使用更多服务" 62 | }, 63 | { 64 | "name": "account", 65 | "value": "帐号" 66 | }, 67 | { 68 | "name": "password", 69 | "value": "密码" 70 | }, 71 | { 72 | "name": "repassword", 73 | "value": "确认密码" 74 | }, 75 | { 76 | "name": "login", 77 | "value": "登录" 78 | }, 79 | { 80 | "name": "register", 81 | "value": "注册" 82 | }, 83 | { 84 | "name": "register_account", 85 | "value": "没有账号,去注册" 86 | }, 87 | { 88 | "name": "login_account", 89 | "value": "已有账号,去登录" 90 | }, 91 | { 92 | "name": "internet_err", 93 | "value": "网络加载失败!" 94 | }, 95 | { 96 | "name": "setting_list_theme", 97 | "value": "主题" 98 | }, 99 | { 100 | "name": "setting_list_language", 101 | "value": "语言" 102 | }, 103 | { 104 | "name": "setting_list_about", 105 | "value": "关于" 106 | }, 107 | { 108 | "name": "no_more_data", 109 | "value": "没有更多数据了" 110 | } 111 | ] 112 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/test/Ability.test.ets: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' 3 | 4 | export default function abilityTest() { 5 | describe('ActsAbilityTest', function () { 6 | // Defines a test suite. Two parameters are supported: test suite name and test suite function. 7 | beforeAll(function () { 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(function () { 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(function () { 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(function () { 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, function () { 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/ets/testability/TestAbility.ets: -------------------------------------------------------------------------------- 1 | import UIAbility from '@ohos.app.ability.UIAbility'; 2 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 3 | import hilog from '@ohos.hilog'; 4 | import { Hypium } from '@ohos/hypium'; 5 | import testsuite from '../test/List.test'; 6 | import window from '@ohos.window'; 7 | 8 | export default class TestAbility extends UIAbility { 9 | onCreate(want, launchParam) { 10 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); 11 | hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); 12 | hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); 13 | var abilityDelegator: any 14 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 15 | var abilityDelegatorArguments: any 16 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 17 | hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); 18 | Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) 19 | } 20 | 21 | onDestroy() { 22 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); 23 | } 24 | 25 | onWindowStageCreate(windowStage: window.WindowStage) { 26 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); 27 | windowStage.loadContent('testability/pages/Index', (err, data) => { 28 | if (err.code) { 29 | hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 30 | return; 31 | } 32 | hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', 33 | JSON.stringify(data) ?? ''); 34 | }); 35 | } 36 | 37 | onWindowStageDestroy() { 38 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); 39 | } 40 | 41 | onForeground() { 42 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); 43 | } 44 | 45 | onBackground() { 46 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); 47 | } 48 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/testability/pages/Index.ets: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | 3 | @Entry 4 | @Component 5 | struct Index { 6 | aboutToAppear() { 7 | hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); 8 | } 9 | @State message: string = 'Hello World' 10 | build() { 11 | Row() { 12 | Column() { 13 | Text(this.message) 14 | .fontSize(50) 15 | .fontWeight(FontWeight.Bold) 16 | Button() { 17 | Text('next page') 18 | .fontSize(20) 19 | .fontWeight(FontWeight.Bold) 20 | }.type(ButtonType.Capsule) 21 | .margin({ 22 | top: 20 23 | }) 24 | .backgroundColor('#0D9FFB') 25 | .width('35%') 26 | .height('5%') 27 | .onClick(()=>{ 28 | }) 29 | } 30 | .width('100%') 31 | } 32 | .height('100%') 33 | } 34 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts: -------------------------------------------------------------------------------- 1 | import hilog from '@ohos.hilog'; 2 | import TestRunner from '@ohos.application.testRunner'; 3 | import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; 4 | 5 | var abilityDelegator = undefined 6 | var abilityDelegatorArguments = undefined 7 | 8 | async function onAbilityCreateCallback() { 9 | hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); 10 | } 11 | 12 | async function addAbilityMonitorCallback(err: any) { 13 | hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); 14 | } 15 | 16 | export default class OpenHarmonyTestRunner implements TestRunner { 17 | constructor() { 18 | } 19 | 20 | onPrepare() { 21 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); 22 | } 23 | 24 | async onRun() { 25 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); 26 | abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() 27 | abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() 28 | var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' 29 | let lMonitor = { 30 | abilityName: testAbilityName, 31 | onAbilityCreate: onAbilityCreateCallback, 32 | }; 33 | abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) 34 | var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName 35 | var debug = abilityDelegatorArguments.parameters['-D'] 36 | if (debug == 'true') 37 | { 38 | cmd += ' -D' 39 | } 40 | hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); 41 | abilityDelegator.executeShellCommand(cmd, 42 | (err: any, d: any) => { 43 | hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); 44 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); 45 | hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); 46 | }) 47 | hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); 48 | } 49 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/module.json5: -------------------------------------------------------------------------------- 1 | { 2 | "module": { 3 | "name": "entry_test", 4 | "type": "feature", 5 | "description": "$string:module_test_desc", 6 | "mainElement": "TestAbility", 7 | "deviceTypes": [ 8 | "phone", 9 | "tablet" 10 | ], 11 | "deliveryWithInstall": true, 12 | "installationFree": false, 13 | "pages": "$profile:test_pages", 14 | "abilities": [ 15 | { 16 | "name": "TestAbility", 17 | "srcEntry": "./ets/testability/TestAbility.ets", 18 | "description": "$string:TestAbility_desc", 19 | "icon": "$media:icon", 20 | "label": "$string:TestAbility_label", 21 | "exported": true, 22 | "startWindowIcon": "$media:icon", 23 | "startWindowBackground": "$color:start_window_background", 24 | "skills": [ 25 | { 26 | "actions": [ 27 | "action.system.home" 28 | ], 29 | "entities": [ 30 | "entity.system.home" 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/element/color.json: -------------------------------------------------------------------------------- 1 | { 2 | "color": [ 3 | { 4 | "name": "start_window_background", 5 | "value": "#FFFFFF" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/element/string.json: -------------------------------------------------------------------------------- 1 | { 2 | "string": [ 3 | { 4 | "name": "module_test_desc", 5 | "value": "test ability description" 6 | }, 7 | { 8 | "name": "TestAbility_desc", 9 | "value": "the test ability" 10 | }, 11 | { 12 | "name": "TestAbility_label", 13 | "value": "test label" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/media/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/entry/src/ohosTest/resources/base/media/icon.png -------------------------------------------------------------------------------- /entry/src/ohosTest/resources/base/profile/test_pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "src": [ 3 | "testability/pages/Index" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /hvigor/hvigor-config.json5: -------------------------------------------------------------------------------- 1 | { 2 | "modelVersion": "5.0.0", 3 | "dependencies": { 4 | } 5 | } -------------------------------------------------------------------------------- /hvigorfile.ts: -------------------------------------------------------------------------------- 1 | // Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. 2 | export { appTasks } from '@ohos/hvigor-ohos-plugin'; -------------------------------------------------------------------------------- /oh-package-lock.json5: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", 4 | "specifiers": { 5 | "@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6", 6 | "@ohos/pulltorefresh@^2.0.1": "@ohos/pulltorefresh@2.0.1" 7 | }, 8 | "packages": { 9 | "@ohos/hypium@1.0.6": { 10 | "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz", 11 | "integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==" 12 | }, 13 | "@ohos/pulltorefresh@2.0.1": { 14 | "resolved": "https://repo.harmonyos.com/ohpm/@ohos/pulltorefresh/-/pulltorefresh-2.0.1.har", 15 | "integrity": "sha512-DlbqTMgMvSPqZTpRFM19w081z+BV/ZPGlctfwwSNtbm3ZolM883Nmon3n/O+AGtkPagbcyPNedwqS8CBI4wNPg==" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /oh-package.json5: -------------------------------------------------------------------------------- 1 | { 2 | "modelVersion": "5.0.0", 3 | "name": "wanandroidhm", 4 | "version": "1.0.0", 5 | "description": "Please describe the basic information.", 6 | "main": "", 7 | "author": "", 8 | "license": "", 9 | "dependencies": { 10 | "@ohos/pulltorefresh": "^2.0.1" 11 | }, 12 | "devDependencies": { 13 | "@ohos/hypium": "1.0.6" 14 | }, 15 | "dynamicDependencies": {} 16 | } -------------------------------------------------------------------------------- /video/wanandroidhm.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PGzxc/WanAndroidHM/42f43eab4d5bf79b0b3218906a3c77f498d0cb11/video/wanandroidhm.mp4 --------------------------------------------------------------------------------