From 4641e9ecee4f16017e714bef2f99df04faa61351 Mon Sep 17 00:00:00 2001 From: xiaoxiao Date: Thu, 10 Jul 2025 09:27:17 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E3=80=81=E6=82=A3=E8=80=85?= =?UTF-8?q?=E5=88=86=E7=BB=84=E3=80=81=E5=AE=A1=E6=A0=B8=E3=80=81=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RefreshLib/.gitignore | 6 + RefreshLib/BuildProfile.ets | 17 + RefreshLib/CHANGELOG.md | 5 + RefreshLib/Index.ets | 15 + RefreshLib/README.md | 22 + RefreshLib/build-profile.json5 | 31 + RefreshLib/consumer-rules.txt | 0 RefreshLib/hvigorfile.ts | 6 + RefreshLib/obfuscation-rules.txt | 23 + RefreshLib/oh-package.json5 | 9 + RefreshLib/src/main/ets/PullDown.ets | 20 + RefreshLib/src/main/ets/PullStatus.ets | 34 + .../src/main/ets/PullToRefreshConfig.ets | 95 + .../src/main/ets/PullToRefreshLayout.ets | 806 +++++++++ RefreshLib/src/main/ets/RefreshController.ets | 80 + RefreshLib/src/main/ets/RefreshLayout.ets | 1610 +++++++++++++++++ .../src/main/ets/RefreshLayoutConfig.ets | 68 + .../src/main/ets/RefreshLayoutHelper.ets | 67 + RefreshLib/src/main/module.json5 | 11 + .../main/resources/base/element/string.json | 72 + .../main/resources/base/element/string_h.json | 72 + .../base/media/icon_refresh_arrow.svg | 7 + .../main/resources/en_US/element/string.json | 8 + .../main/resources/zh_CN/element/string.json | 8 + .../src/ohosTest/ets/test/Ability.test.ets | 35 + .../src/ohosTest/ets/test/List.test.ets | 5 + RefreshLib/src/ohosTest/module.json5 | 13 + RefreshLib/src/test/List.test.ets | 5 + RefreshLib/src/test/LocalUnit.test.ets | 33 + build-profile.json5 | 8 + commons/basic/Index.ets | 17 +- .../src/main/ets/Views/SignPopWindow.ets | 75 + .../src/main/ets/components/HdHomeNav.ets | 59 + .../basic/src/main/ets/components/HdNav.ets | 20 +- .../src/main/ets/components/HdSearchNav.ets | 1 - .../src/main/ets/constants/BasicConstant.ets | 17 +- .../main/ets/models/RequestDefaultModel.ets | 1 - .../src/main/ets/utils/PatientsEntity.ets | 862 +++++++++ .../src/main/ets/utils/TimestampUtil.ets | 50 + .../base/media/home_no_qiandao_icon.png | Bin 0 -> 1987 bytes .../base/media/home_qiandao_icon.png | Bin 0 -> 1864 bytes .../base/media/sign_back_leftright_icon.png | Bin 0 -> 984 bytes .../base/media/sign_back_news_icon.png | Bin 0 -> 4896 bytes .../base/media/sign_popwindow_close.png | Bin 0 -> 3621 bytes .../src/main/ets/components/FlipperComp.ets | 108 ++ .../src/main/ets/components/HomeIconComp.ets | 65 + .../ets/components/HomeReplayVideoComp.ets | 56 + .../main/ets/components/HomeSwiperComp.ets | 57 + .../ets/components/SpeciallyEStandingComp.ets | 92 + .../Home/src/main/ets/model/HomeModel.ets | 126 ++ features/Home/src/main/ets/pages/HomePage.ets | 186 +- .../main/resources/base/element/float.json | 2 +- .../base/media/course_invoice_to_details.png | Bin 0 -> 968 bytes .../base/media/meeting_back_icon.png | Bin 0 -> 10804 bytes .../base/media/meeting_begin_play_icon.gif | Bin 0 -> 1304 bytes .../base/media/meeting_live_icon.png | Bin 0 -> 3599 bytes .../base/media/meeting_noPlay_icon.png | Bin 0 -> 2184 bytes .../base/media/meeting_timeBack_icon.png | Bin 0 -> 7318 bytes .../base/media/new_home_choose_icon.png | Bin 0 -> 1683 bytes .../src/main/ets/model/MyPageSectionClass.ets | 4 +- .../mypage/src/main/ets/pages/MyHomePage.ets | 69 +- .../mypage/src/main/ets/view/FourSection.ets | 10 +- .../mypage/src/main/ets/view/HeaderView.ets | 163 +- .../src/main/ets/view/MyPageSectionItem.ets | 24 +- .../mypage/src/main/ets/view/OneSection.ets | 73 +- .../mypage/src/main/ets/view/ThreeSection.ets | 12 +- .../mypage/src/main/ets/view/TwoSection.ets | 8 +- .../resources/base/media/my_page_QrCode.png | Bin 0 -> 1927 bytes .../resources/base/media/my_page_message.png | Bin 0 -> 2454 bytes .../base/media/my_page_patientAudit.png | Bin 0 -> 2981 bytes .../base/media/my_page_patientList.png | Bin 0 -> 3759 bytes .../base/media/my_page_visitPlan.png | Bin 0 -> 2371 bytes features/patient/.gitignore | 6 + features/patient/BuildProfile.ets | 17 + features/patient/Index.ets | 17 + features/patient/build-profile.json5 | 31 + features/patient/consumer-rules.txt | 0 features/patient/hvigorfile.ts | 6 + features/patient/obfuscation-rules.txt | 23 + features/patient/oh-package-lock.json5 | 25 + features/patient/oh-package.json5 | 12 + .../ets/components/BuildOrEditGroupPage.ets | 304 ++++ .../src/main/ets/components/MainPage.ets | 19 + .../main/ets/components/PatientApplyPage.ets | 222 +++ .../ets/components/PatientDetailsComp.ets | 198 ++ .../main/ets/components/PatientSetMsgPage.ets | 197 ++ .../src/main/ets/components/PatientsGroup.ets | 315 ++++ .../main/ets/components/PatientsListComp.ets | 179 ++ .../src/main/ets/models/ApplyModel.ets | 47 + .../main/ets/models/PatientsGroupModel.ets | 62 + .../patient/src/main/ets/views/ApplyViews.ets | 72 + features/patient/src/main/module.json5 | 11 + .../main/resources/base/element/float.json | 8 + .../main/resources/base/element/string.json | 8 + .../media/Patients_Apply_History_Status.png | Bin 0 -> 1112 bytes .../media/addPatientApply_reminder_icon.png | Bin 0 -> 2602 bytes .../base/media/add_patients_to_roup.png | Bin 0 -> 5715 bytes .../main/resources/base/media/chose_card.png | Bin 0 -> 1399 bytes .../base/media/dele_patient_inThe_group.png | Bin 0 -> 2062 bytes .../base/media/fuifangPlan_blackBtn.png | Bin 0 -> 2151 bytes .../resources/base/media/group_turnDown.png | Bin 0 -> 1165 bytes .../resources/base/media/group_turnRight.png | Bin 0 -> 1196 bytes .../main/resources/base/media/group_vip.png | Bin 0 -> 1370 bytes .../base/media/lifetime_right_icon.png | Bin 0 -> 11505 bytes .../base/media/listBing_blackBtn.png | Bin 0 -> 2807 bytes .../base/media/patiemts_list_selected.png | Bin 0 -> 2304 bytes .../patient_details_navigation_right.png | Bin 0 -> 5907 bytes .../base/media/patients_list_noSelect.png | Bin 0 -> 3033 bytes .../base/media/sendMessage_blackBtn.png | Bin 0 -> 3129 bytes .../base/media/triangle_green_theme.png | Bin 0 -> 1122 bytes .../resources/base/media/triangle_normal.png | Bin 0 -> 1111 bytes .../src/ohosTest/ets/test/Ability.test.ets | 35 + .../src/ohosTest/ets/test/List.test.ets | 5 + features/patient/src/ohosTest/module.json5 | 13 + features/patient/src/test/List.test.ets | 5 + features/patient/src/test/LocalUnit.test.ets | 33 + oh-package-lock.json5 | 16 + oh-package.json5 | 3 +- products/expert/oh-package-lock.json5 | 254 +++ products/expert/oh-package.json5 | 1 + .../src/main/ets/contants/TabBarItems.ets | 5 + .../main/ets/entryability/EntryAbility.ets | 12 + products/expert/src/main/ets/pages/Home.ets | 22 +- .../PatientsPage/BuildOrEditGroupPage.ets | 19 + .../pages/PatientsPage/PatientDetailsPage.ets | 19 + .../pages/PatientsPage/PatientMsgSetPage.ets | 14 + .../ets/pages/PatientsPage/PatientPages.ets | 13 + .../pages/PatientsPage/PatientsGroupPage.ets | 19 + .../pages/PatientsPage/PatientsListPage.ets | 12 + .../src/main/ets/pages/Tabbar/TabBarComp.ets | 10 +- .../ets/pages/VideoPage/VideoGandanPage.ets | 4 +- .../src/main/ets/pages/WebView/WebPage.ets | 96 +- products/expert/src/main/module.json5 | 5 + .../base/media/home_default_icon.png | Bin 0 -> 2714 bytes .../base/media/home_selected_icon.png | Bin 0 -> 1519 bytes .../resources/base/profile/main_pages.json | 6 + 136 files changed, 7577 insertions(+), 171 deletions(-) create mode 100644 RefreshLib/.gitignore create mode 100644 RefreshLib/BuildProfile.ets create mode 100644 RefreshLib/CHANGELOG.md create mode 100644 RefreshLib/Index.ets create mode 100644 RefreshLib/README.md create mode 100644 RefreshLib/build-profile.json5 create mode 100644 RefreshLib/consumer-rules.txt create mode 100644 RefreshLib/hvigorfile.ts create mode 100644 RefreshLib/obfuscation-rules.txt create mode 100644 RefreshLib/oh-package.json5 create mode 100644 RefreshLib/src/main/ets/PullDown.ets create mode 100644 RefreshLib/src/main/ets/PullStatus.ets create mode 100644 RefreshLib/src/main/ets/PullToRefreshConfig.ets create mode 100644 RefreshLib/src/main/ets/PullToRefreshLayout.ets create mode 100644 RefreshLib/src/main/ets/RefreshController.ets create mode 100644 RefreshLib/src/main/ets/RefreshLayout.ets create mode 100644 RefreshLib/src/main/ets/RefreshLayoutConfig.ets create mode 100644 RefreshLib/src/main/ets/RefreshLayoutHelper.ets create mode 100644 RefreshLib/src/main/module.json5 create mode 100644 RefreshLib/src/main/resources/base/element/string.json create mode 100644 RefreshLib/src/main/resources/base/element/string_h.json create mode 100644 RefreshLib/src/main/resources/base/media/icon_refresh_arrow.svg create mode 100644 RefreshLib/src/main/resources/en_US/element/string.json create mode 100644 RefreshLib/src/main/resources/zh_CN/element/string.json create mode 100644 RefreshLib/src/ohosTest/ets/test/Ability.test.ets create mode 100644 RefreshLib/src/ohosTest/ets/test/List.test.ets create mode 100644 RefreshLib/src/ohosTest/module.json5 create mode 100644 RefreshLib/src/test/List.test.ets create mode 100644 RefreshLib/src/test/LocalUnit.test.ets create mode 100644 commons/basic/src/main/ets/Views/SignPopWindow.ets create mode 100644 commons/basic/src/main/ets/components/HdHomeNav.ets create mode 100644 commons/basic/src/main/ets/utils/PatientsEntity.ets create mode 100644 commons/basic/src/main/ets/utils/TimestampUtil.ets create mode 100644 commons/basic/src/main/resources/base/media/home_no_qiandao_icon.png create mode 100644 commons/basic/src/main/resources/base/media/home_qiandao_icon.png create mode 100644 commons/basic/src/main/resources/base/media/sign_back_leftright_icon.png create mode 100644 commons/basic/src/main/resources/base/media/sign_back_news_icon.png create mode 100644 commons/basic/src/main/resources/base/media/sign_popwindow_close.png create mode 100644 features/Home/src/main/ets/components/FlipperComp.ets create mode 100644 features/Home/src/main/ets/components/HomeIconComp.ets create mode 100644 features/Home/src/main/ets/components/HomeReplayVideoComp.ets create mode 100644 features/Home/src/main/ets/components/HomeSwiperComp.ets create mode 100644 features/Home/src/main/ets/components/SpeciallyEStandingComp.ets create mode 100644 features/Home/src/main/ets/model/HomeModel.ets create mode 100644 features/Home/src/main/resources/base/media/course_invoice_to_details.png create mode 100644 features/Home/src/main/resources/base/media/meeting_back_icon.png create mode 100644 features/Home/src/main/resources/base/media/meeting_begin_play_icon.gif create mode 100644 features/Home/src/main/resources/base/media/meeting_live_icon.png create mode 100644 features/Home/src/main/resources/base/media/meeting_noPlay_icon.png create mode 100644 features/Home/src/main/resources/base/media/meeting_timeBack_icon.png create mode 100644 features/Home/src/main/resources/base/media/new_home_choose_icon.png create mode 100644 features/mypage/src/main/resources/base/media/my_page_QrCode.png create mode 100644 features/mypage/src/main/resources/base/media/my_page_message.png create mode 100644 features/mypage/src/main/resources/base/media/my_page_patientAudit.png create mode 100644 features/mypage/src/main/resources/base/media/my_page_patientList.png create mode 100644 features/mypage/src/main/resources/base/media/my_page_visitPlan.png create mode 100644 features/patient/.gitignore create mode 100644 features/patient/BuildProfile.ets create mode 100644 features/patient/Index.ets create mode 100644 features/patient/build-profile.json5 create mode 100644 features/patient/consumer-rules.txt create mode 100644 features/patient/hvigorfile.ts create mode 100644 features/patient/obfuscation-rules.txt create mode 100644 features/patient/oh-package-lock.json5 create mode 100644 features/patient/oh-package.json5 create mode 100644 features/patient/src/main/ets/components/BuildOrEditGroupPage.ets create mode 100644 features/patient/src/main/ets/components/MainPage.ets create mode 100644 features/patient/src/main/ets/components/PatientApplyPage.ets create mode 100644 features/patient/src/main/ets/components/PatientDetailsComp.ets create mode 100644 features/patient/src/main/ets/components/PatientSetMsgPage.ets create mode 100644 features/patient/src/main/ets/components/PatientsGroup.ets create mode 100644 features/patient/src/main/ets/components/PatientsListComp.ets create mode 100644 features/patient/src/main/ets/models/ApplyModel.ets create mode 100644 features/patient/src/main/ets/models/PatientsGroupModel.ets create mode 100644 features/patient/src/main/ets/views/ApplyViews.ets create mode 100644 features/patient/src/main/module.json5 create mode 100644 features/patient/src/main/resources/base/element/float.json create mode 100644 features/patient/src/main/resources/base/element/string.json create mode 100644 features/patient/src/main/resources/base/media/Patients_Apply_History_Status.png create mode 100644 features/patient/src/main/resources/base/media/addPatientApply_reminder_icon.png create mode 100644 features/patient/src/main/resources/base/media/add_patients_to_roup.png create mode 100644 features/patient/src/main/resources/base/media/chose_card.png create mode 100644 features/patient/src/main/resources/base/media/dele_patient_inThe_group.png create mode 100644 features/patient/src/main/resources/base/media/fuifangPlan_blackBtn.png create mode 100644 features/patient/src/main/resources/base/media/group_turnDown.png create mode 100644 features/patient/src/main/resources/base/media/group_turnRight.png create mode 100644 features/patient/src/main/resources/base/media/group_vip.png create mode 100644 features/patient/src/main/resources/base/media/lifetime_right_icon.png create mode 100644 features/patient/src/main/resources/base/media/listBing_blackBtn.png create mode 100644 features/patient/src/main/resources/base/media/patiemts_list_selected.png create mode 100644 features/patient/src/main/resources/base/media/patient_details_navigation_right.png create mode 100644 features/patient/src/main/resources/base/media/patients_list_noSelect.png create mode 100644 features/patient/src/main/resources/base/media/sendMessage_blackBtn.png create mode 100644 features/patient/src/main/resources/base/media/triangle_green_theme.png create mode 100644 features/patient/src/main/resources/base/media/triangle_normal.png create mode 100644 features/patient/src/ohosTest/ets/test/Ability.test.ets create mode 100644 features/patient/src/ohosTest/ets/test/List.test.ets create mode 100644 features/patient/src/ohosTest/module.json5 create mode 100644 features/patient/src/test/List.test.ets create mode 100644 features/patient/src/test/LocalUnit.test.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/BuildOrEditGroupPage.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/PatientDetailsPage.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/PatientMsgSetPage.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/PatientPages.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/PatientsGroupPage.ets create mode 100644 products/expert/src/main/ets/pages/PatientsPage/PatientsListPage.ets create mode 100644 products/expert/src/main/resources/base/media/home_default_icon.png create mode 100644 products/expert/src/main/resources/base/media/home_selected_icon.png diff --git a/RefreshLib/.gitignore b/RefreshLib/.gitignore new file mode 100644 index 0000000..e2713a2 --- /dev/null +++ b/RefreshLib/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/RefreshLib/BuildProfile.ets b/RefreshLib/BuildProfile.ets new file mode 100644 index 0000000..3a501e5 --- /dev/null +++ b/RefreshLib/BuildProfile.ets @@ -0,0 +1,17 @@ +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/RefreshLib/CHANGELOG.md b/RefreshLib/CHANGELOG.md new file mode 100644 index 0000000..7cba6e5 --- /dev/null +++ b/RefreshLib/CHANGELOG.md @@ -0,0 +1,5 @@ +# 版本更新记录 + +## [v1.0.0] 2024-09-24 + +# 首次上传 \ No newline at end of file diff --git a/RefreshLib/Index.ets b/RefreshLib/Index.ets new file mode 100644 index 0000000..83c3253 --- /dev/null +++ b/RefreshLib/Index.ets @@ -0,0 +1,15 @@ +export { PullToRefreshLayout } from './src/main/ets/PullToRefreshLayout' + +export { RefreshLayout } from './src/main/ets/RefreshLayout' + +export { RefreshLayoutConfig } from './src/main/ets/RefreshLayoutConfig' + +export { PullDown } from './src/main/ets/PullDown' + +export { PullStatus } from './src/main/ets/PullStatus' + +export { RefreshLayoutHelper } from './src/main/ets/RefreshLayoutHelper' + +export { RefreshController } from './src/main/ets/RefreshController' + +export { PullToRefreshConfig } from './src/main/ets/PullToRefreshConfig' diff --git a/RefreshLib/README.md b/RefreshLib/README.md new file mode 100644 index 0000000..7e28ca9 --- /dev/null +++ b/RefreshLib/README.md @@ -0,0 +1,22 @@ +PullToRefresh + +简介 +PullToRefresh 实现垂直列表下拉刷新,上拉加载,横向列表左拉刷新,右拉加载 + +特点 +1.无入侵性,不需要传数据源 + +2.不限制组件,支持任意布局(List,Grid,Web,Scroll,Text,Row,Column等布局) + +3.支持header和footer定制(支持Lottie动画) + +4.支持垂直列表和横向列表的刷新和加载 + +5.支持下拉(或者上拉)打开其他页面 + +提供了RefreshLayout和PullToRefreshLayout +1.RefreshLayout支持各种定制化 +2.PullToRefreshLayout是在RefreshLayout基础上定制的,实现常用刷新和加载功能 +3.如果没有个性化需求,可以直接使用PullToRefreshLayout + +详细使用Dome:https://gitee.com/myspace01/refresh-lib \ No newline at end of file diff --git a/RefreshLib/build-profile.json5 b/RefreshLib/build-profile.json5 new file mode 100644 index 0000000..e6773f9 --- /dev/null +++ b/RefreshLib/build-profile.json5 @@ -0,0 +1,31 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} diff --git a/RefreshLib/consumer-rules.txt b/RefreshLib/consumer-rules.txt new file mode 100644 index 0000000..e69de29 diff --git a/RefreshLib/hvigorfile.ts b/RefreshLib/hvigorfile.ts new file mode 100644 index 0000000..4218707 --- /dev/null +++ b/RefreshLib/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/RefreshLib/obfuscation-rules.txt b/RefreshLib/obfuscation-rules.txt new file mode 100644 index 0000000..272efb6 --- /dev/null +++ b/RefreshLib/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/RefreshLib/oh-package.json5 b/RefreshLib/oh-package.json5 new file mode 100644 index 0000000..6e36506 --- /dev/null +++ b/RefreshLib/oh-package.json5 @@ -0,0 +1,9 @@ +{ + "name": "refreshlib", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "os_space", + "license": "Apache-2.0", + "dependencies": {} +} diff --git a/RefreshLib/src/main/ets/PullDown.ets b/RefreshLib/src/main/ets/PullDown.ets new file mode 100644 index 0000000..995dc2a --- /dev/null +++ b/RefreshLib/src/main/ets/PullDown.ets @@ -0,0 +1,20 @@ +import { PullStatus } from './PullStatus' + +export interface PullDown { + /*是否是下拉*/ + isPullDown: boolean + /*是否是上拉*/ + isPullUp: boolean + /*是否触摸*/ + isTouch: boolean + /*下拉距离*/ + distance: number + /*上拉距离*/ + distanceLoad: number + /*headerView高度*/ + headerViewSize: number + /*footerView高度*/ + footerViewSize: number + /*状态*/ + status: PullStatus +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/PullStatus.ets b/RefreshLib/src/main/ets/PullStatus.ets new file mode 100644 index 0000000..d93f4be --- /dev/null +++ b/RefreshLib/src/main/ets/PullStatus.ets @@ -0,0 +1,34 @@ +export enum PullStatus { + /*0默认状态(无下拉距离)*/ + DEF, + /***********************************下拉状态*******************************************/ + /*1下拉状态*/ + PullDown, + /*2下拉至可以刷新的状态(准备刷新)*/ + PreRefresh, + /*3刷新中*/ + Refresh, + /*4下拉超过刷新高度时准备打开其他页面*/ + PreOpenPage, + /*5下拉超过刷新高度时松手打开其他页面*/ + OpenPage, + /*6刷新成功*/ + RefreshSuccess, + /*7刷新失败*/ + RefreshError, + /***********************************上拉状态*******************************************/ + /*8上拉状态*/ + PullUp, + /*9上拉至可以加载的状态(准备加载)*/ + PreLoad, + /*10加载中*/ + Load, + /*11上拉超过加载高度时准备打开其他页面*/ + PreLoadOpenPage, + /*12上拉超过加载高度时松手打开其他页面*/ + LoadOpenPage, + /*13加载成功*/ + LoadSuccess, + /*14加载失败*/ + LoadError +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/PullToRefreshConfig.ets b/RefreshLib/src/main/ets/PullToRefreshConfig.ets new file mode 100644 index 0000000..8fb5b9e --- /dev/null +++ b/RefreshLib/src/main/ets/PullToRefreshConfig.ets @@ -0,0 +1,95 @@ +export class PullToRefreshConfig { + /*刷新loading宽度*/ + public refreshLoadingWidth: Length = 25 + /*刷新loading高度*/ + public refreshLoadingHeight: Length = 25 + /*刷新loading颜色*/ + public refreshLoadingColor: ResourceColor = "#333333" + /*下拉刷新箭头*/ + public arrowImage: Resource = $r("app.media.icon_refresh_arrow") + /*下拉刷新箭头颜色*/ + public arrowImageColor: ResourceColor = "#333333" + /*下拉刷新箭头宽度*/ + public arrowImageWidth: Length = 20 + /*下拉刷新箭头高度*/ + public arrowImageHeight: Length = 20 + + + /*上拉刷新箭头*/ + public arrowLoadImage: Resource = $r("app.media.icon_refresh_arrow") + /*上拉刷新箭头颜色*/ + public arrowLoadImageColor: ResourceColor = "#333333" + /*上拉刷新箭头宽度*/ + public arrowLoadImageWidth: Length = 20 + /*上拉刷新箭头高度*/ + public arrowLoadImageHeight: Length = 20 + + /*暂无更多提示*/ + public noMoreTips: string | Resource = $r("app.string.zr_load_no_more") + /*暂无更多提示大小*/ + public noMoreTipsSize: number | string | Resource = 13 + /*暂无更多提示颜色*/ + public noMoreTipsColor: ResourceColor = "#666666" + /*暂无更多提示——横向列表*/ + public noMoreTipsHorizontal: string | Resource = $r("app.string.h_zr_load_no_more") + /*是否显示刷新时间*/ + public showRefreshTime: boolean = false + /*刷新时间tips大小*/ + public refreshTimeTipsSize: number | string | Resource = 11 + /*刷新时间tips颜色*/ + public refreshTimeTipsColor: ResourceColor = "#999999" + /*下拉刷新提示*/ + public pullDownTips: string | Resource = $r("app.string.zr_pull_down_to_refresh") + /*下拉刷新提示——横向列表*/ + public pullDownTipsHorizontal: string | Resource = $r("app.string.h_zr_pull_down_to_refresh") + /*下拉刷新提示大小*/ + public pullDownTipsSize: number | string | Resource = 13 + /*下拉刷新提示颜色*/ + public pullDownTipsColor: ResourceColor = "#666666" + /*释放立即刷新tips*/ + public releaseRefreshTips: string | Resource = $r("app.string.zr_release_to_refresh") + /*释放立即刷新tips——横向列表*/ + public releaseRefreshTipsHorizontal: string | Resource = $r("app.string.h_zr_release_to_refresh") + /*正在刷新tips*/ + public refreshTips: string | Resource = $r("app.string.zr_refreshing") + /*正在刷新tips——横向列表*/ + public refreshTipsHorizontal: string | Resource = $r("app.string.h_zr_refreshing") + /*刷新成功tips*/ + public refreshSuccessTips: string | Resource = $r("app.string.zr_refresh_success") + /*刷新成功tips——横向列表*/ + public refreshSuccessTipsHorizontal: string | Resource = $r("app.string.h_zr_refresh_success") + /*刷新失败tips*/ + public refreshErrorTips: string | Resource = $r("app.string.zr_refresh_error") + /*刷新失败tips——横向列表*/ + public refreshErrorTipsHorizontal: string | Resource = $r("app.string.h_zr_refresh_error") + /*下拉打开其他页面tips*/ + public pullOpenPageTips: string | Resource = $r("app.string.zr_release_to_open_age") + /*下拉打开其他页面tips——横向列表*/ + public pullOpenPageTipsHorizontal: string | Resource = $r("app.string.h_zr_release_to_open_age") + /*上拉加载提示*/ + public pullUpTips: string | Resource = $r("app.string.zr_pull_up_to_load") + /*上拉加载提示——横向列表*/ + public pullUpTipsHorizontal: string | Resource = $r("app.string.h_zr_pull_up_to_load") + /*上拉加载提示大小*/ + public pullUpTipsSize: number | string | Resource = 13 + /*上拉加载提示颜色*/ + public pullUpTipsColor: ResourceColor = "#333333" + /*释放立即加载tips*/ + public releaseLoadTips: string | Resource = $r("app.string.zr_release_to_load") + /*释放立即加载tips——横向列表*/ + public releaseLoadTipsHorizontal: string | Resource = $r("app.string.h_zr_release_to_load") + /*正在加载tips*/ + public loadTips: string | Resource = $r("app.string.zr_loading") + /*正在加载tips——横向列表*/ + public loadTipsHorizontal: string | Resource = $r("app.string.h_zr_loading") + /*加载成功tips*/ + public loadSuccessTips: string | Resource = $r("app.string.zr_load_success") + /*加载成功tips——横向列表*/ + public loadSuccessTipsHorizontal: string | Resource = $r("app.string.h_zr_load_success") + /*加载失败tips*/ + public loadErrorTips: string | Resource = $r("app.string.zr_load_error") + /*加载失败tips——横向列表*/ + public loadErrorTipsHorizontal: string | Resource = $r("app.string.h_zr_load_error") + /*下拉打开其他页面tips*/ + public loadOpenPageTips: string | Resource = $r("app.string.zr_release_to_open_age") +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/PullToRefreshLayout.ets b/RefreshLib/src/main/ets/PullToRefreshLayout.ets new file mode 100644 index 0000000..5455d38 --- /dev/null +++ b/RefreshLib/src/main/ets/PullToRefreshLayout.ets @@ -0,0 +1,806 @@ +import { PullDown } from './PullDown'; +import { PullStatus } from './PullStatus'; +import { RefreshController } from './RefreshController'; +import { RefreshLayout } from './RefreshLayout'; +import { RefreshLayoutConfig } from './RefreshLayoutConfig'; +import { _ContentView } from './RefreshLayoutHelper'; +import web from '@ohos.web.webview'; +import preferences from '@ohos.data.preferences'; +import { PullToRefreshConfig } from './PullToRefreshConfig'; + +@ComponentV2 +export struct PullToRefreshLayout { + /*下拉箭头旋转角度*/ + @Local arrowRotate: number = 0; + /*上拉箭头旋转角度*/ + @Local arrowRotateLoad: number = 180; + @Local status: PullStatus = PullStatus.DEF + /********************************************************************************************************/ + @Param public pullConfig: PullToRefreshConfig = new PullToRefreshConfig() + /*记录组件刷新时间*/ + @Param public viewKey: string = "" + @Param public scroller: Scroller | undefined = undefined + @Param public webviewController: web.WebviewController | undefined = undefined + @Param public controller: RefreshController = new RefreshController() + /*是否可以下拉*/ + @Param public onCanPullRefresh: () => boolean = () => true + /*是否可以下拉*/ + @Param public onCanPullLoad: () => boolean = () => false + /*刷新通知*/ + @Param public onRefresh: () => void = () => { + } + /*刷新通知*/ + @Param public onLoad: () => void = () => { + } + /*打开页面通知*/ + @Param public onOpenPage: () => void = () => { + } + /*打开页面通知*/ + @Param public onLoadOpenPage: () => void = () => { + } + /*下拉状态监听*/ + @Param public onPullListener: (pullDown: PullDown) => void = () => { + } + //内容视图 + @BuilderParam contentView: () => void = _ContentView + //loading视图 + @BuilderParam headerLoadIngView?: () => void + //loading视图 + @BuilderParam footerLoadIngView?: () => void + /*下拉刷新配置*/ + @Param public config: RefreshLayoutConfig = new RefreshLayoutConfig() + //正在加载视图 + @BuilderParam viewLoading?: () => void + //空视图 + @BuilderParam viewEmpty?: () => void + //加载错误视图 + @BuilderParam viewError?: () => void + //无网络视图 + @BuilderParam viewNoNetwork?: () => void + + /********************************************************************************************************/ + + aboutToAppear(): void { + this.resetArrowRotate() + this.resetArrowRotateLoad() + } + + /*************垂直列表和水平列表feader视图箭头角度**************/ + private isPullRotate(): boolean { + if (this.isVerticalLayout()) { + return this.arrowRotate == 0 + } else { + return this.arrowRotate == -90 + } + } + + private isPreRefreshRotate(): boolean { + if (this.isVerticalLayout()) { + return this.arrowRotate == 180 + } else { + return this.arrowRotate == 90 + } + } + + private setPreRefreshRotate() { + if (this.isVerticalLayout()) { + this.arrowRotate = 180 + } else { + this.arrowRotate = 90 + } + } + + private resetArrowRotate() { + if (this.isVerticalLayout()) { + this.arrowRotate = 0 + } else { + this.arrowRotate = -90 + } + } + + /*************垂直列表和水平列表footer视图箭头角度**************/ + private isPullLoadRotate(): boolean { + if (this.isVerticalLayout()) { + return this.arrowRotateLoad == 180 + } else { + return this.arrowRotateLoad == 90 + } + } + + private isPreLoadRotate(): boolean { + if (this.isVerticalLayout()) { + return this.arrowRotateLoad == 0 + } else { + return this.arrowRotateLoad == -90 + } + } + + private setPreLoadRotate() { + if (this.isVerticalLayout()) { + this.arrowRotateLoad = 180 + } else { + this.arrowRotateLoad = 90 + } + } + + private resetArrowRotateLoad() { + if (this.isVerticalLayout()) { + this.arrowRotateLoad = 0 + } else { + this.arrowRotateLoad = -90 + } + } + + build() { + RefreshLayout({ + scroller: this.scroller, + webviewController: this.webviewController, + controller: this.controller, + config: this.config, + headerView: () => { + if (this.isVerticalLayout()) { + this.defHeaderView() + } else { + this.defHeaderViewHorizontal() + } + }, + onCanPullRefresh: () => { + return this.onCanPullRefresh() + }, + onCanPullLoad: () => { + return this.onCanPullLoad() + }, + onRefresh: () => { + this.onRefresh() + }, + onLoad: () => { + this.onLoad() + }, + onOpenPage: () => { + this.onOpenPage() + }, + onLoadOpenPage: () => { + this.onLoadOpenPage() + }, + contentView: () => { + this.contentView() + }, + loadView: () => { + if (this.isVerticalLayout()) { + this.defFooterView() + } else { + this.defFooterViewHorizontal() + } + }, + noMoreView: () => { + if (this.isVerticalLayout()) { + this.defFooterNoMoreView() + } else { + this.defFooterNoMoreViewHorizontal() + } + }, + onPullListener: (pullDown: PullDown) => { + this.status = pullDown.status + this.updateRefreshTime(this.status) + this.onPullListener?.(pullDown) + if (this.config.pullHeaderHeightRefresh <= pullDown.headerViewSize) { + this.config.pullHeaderHeightRefresh = pullDown.headerViewSize * 1.5 + } + if (this.config.pullHeaderHeightOpenPage <= this.config.pullHeaderHeightRefresh) { + this.config.pullHeaderHeightOpenPage = pullDown.headerViewSize * 2.6 + } + + if (this.config.pullFooterHeightLoad <= pullDown.footerViewSize) { + this.config.pullFooterHeightLoad = pullDown.footerViewSize * 1.1 + } + if (this.config.pullFooterHeightOpenPage <= this.config.pullFooterHeightLoad) { + this.config.pullFooterHeightOpenPage = pullDown.footerViewSize * 2.6 + } + if (this.status == PullStatus.PullDown) { + if (this.isPullRotate()) { + return + } + /*进入下拉状态*/ + animateTo({ duration: 10, curve: Curve.Linear }, () => { + this.resetArrowRotate() + }) + } else if (this.status == PullStatus.PreRefresh) { + if (this.isPreRefreshRotate()) { + return + } + /*进入释放刷新状态*/ + animateTo({ duration: 150, curve: Curve.Linear }, () => { + this.setPreRefreshRotate() + }) + } else if (this.status == PullStatus.PullUp) { + if (this.isPullLoadRotate()) { + return + } + /*进入上拉状态*/ + animateTo({ duration: 10, curve: Curve.Linear }, () => { + this.setPreLoadRotate() + }) + } else if (this.status == PullStatus.PreLoad) { + if (this.isPreLoadRotate()) { + return + } + /*进入释放加载状态*/ + animateTo({ duration: 150, curve: Curve.Linear }, () => { + + this.resetArrowRotateLoad() + }) + } + + }, + /*正在加载视图*/ + viewLoading: this.viewLoading, + /*空数据视图*/ + viewEmpty: this.viewEmpty, + /*加载失败视图*/ + viewError: this.viewError, + /*无网络视图*/ + viewNoNetwork: this.viewNoNetwork + }).clip(true) + + } + + private isVerticalLayout(): boolean { + return (this.config.isVertical || this.config.horizontalMode == 1 || this.config.horizontalMode == 2) + } + + @Local private zrLastUpdate: string = getContext().resourceManager.getStringByNameSync(("zr_last_update")) + + @Builder + defHeaderViewHorizontal() { + RelativeContainer() { + Stack() { + Image(this.pullConfig.arrowImage) + .width(this.pullConfig.arrowImageWidth) + .height(this.pullConfig.arrowImageHeight) + .visibility(this.getPullArrowVisibility()) + .fillColor(this.pullConfig.arrowImageColor) + .margin({ top: 5 }) + .rotate({ + x: 0, + y: 0, + z: 1, + angle: this.arrowRotate + }) + if (this.headerLoadIngView) { + Stack() { + this.headerLoadIngView() + } + .visibility(this.getRefreshVisibility()) + } else { + LoadingProgress() + .color(this.pullConfig.refreshLoadingColor) + .width(this.pullConfig.refreshLoadingWidth) + .height(this.pullConfig.refreshLoadingHeight) + .visibility(this.getRefreshVisibility()) + } + }.alignRules({ + top: { anchor: "title", align: VerticalAlign.Bottom }, + middle: { anchor: "__container__", align: HorizontalAlign.Center } + }).id("arrow") + + Row() { + Text(this.getTips()) + .fontSize(this.pullConfig.pullDownTipsSize) + .fontColor(this.pullConfig.pullDownTipsColor) + .textAlign(TextAlign.Center) + Text(this.zrLastUpdate) + .fontSize(this.pullConfig.refreshTimeTipsSize) + .fontColor(this.pullConfig.refreshTimeTipsColor) + .margin({ left: 2 }) + .visibility(this.pullConfig.showRefreshTime ? this.getTimeTipsVisibility() : Visibility.None) + .textAlign(TextAlign.Center) + }.id("title").alignRules({ + middle: { anchor: "__container__", align: HorizontalAlign.Center }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).alignItems(VerticalAlign.Center) + + }.width(50).height("100%") + } + + @Builder + defHeaderView() { + RelativeContainer() { + Stack() { + Image(this.pullConfig.arrowImage) + .width(this.pullConfig.arrowImageWidth) + .height(this.pullConfig.arrowImageHeight) + .visibility(this.getPullArrowVisibility()) + .fillColor(this.pullConfig.arrowImageColor) + .margin({ right: 10 }) + .rotate({ + x: 0, + y: 0, + z: 1, + angle: this.arrowRotate + }) + if (this.headerLoadIngView) { + Stack() { + this.headerLoadIngView() + } + .visibility(this.getRefreshVisibility()) + } else { + LoadingProgress() + .color(this.pullConfig.refreshLoadingColor) + .width(this.pullConfig.refreshLoadingWidth) + .height(this.pullConfig.refreshLoadingHeight) + .visibility(this.getRefreshVisibility()) + } + }.alignRules({ + right: { anchor: "title", align: HorizontalAlign.Start }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).id("arrow") + + Column() { + Text(this.getTips()).fontSize(this.pullConfig.pullDownTipsSize) + .fontColor(this.pullConfig.pullDownTipsColor) + Text(this.zrLastUpdate).fontSize(this.pullConfig.refreshTimeTipsSize) + .fontColor(this.pullConfig.refreshTimeTipsColor).margin({ top: 2 }) + .visibility(this.pullConfig.showRefreshTime ? this.getTimeTipsVisibility() : Visibility.None) + }.id("title").alignRules({ + middle: { anchor: "__container__", align: HorizontalAlign.Center }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).constraintSize({ minWidth: 80 }).alignItems(HorizontalAlign.Center) + + }.height(50).width("100%") + + } + + @Builder + defFooterViewHorizontal() { + RelativeContainer() { + Stack() { + Image(this.pullConfig.arrowLoadImage) + .width(this.pullConfig.arrowLoadImageWidth) + .height(this.pullConfig.arrowLoadImageHeight) + .visibility(this.getPullUpArrowVisibility()) + .fillColor(this.pullConfig.arrowLoadImageColor) + .margin({ top: 5 }) + .rotate({ + x: 0, + y: 0, + z: 1, + angle: this.arrowRotateLoad + }) + if (this.footerLoadIngView) { + Stack() { + this.footerLoadIngView() + } + .visibility(this.getLoadVisibility()) + } else { + LoadingProgress() + .color(this.pullConfig.refreshLoadingColor) + .width(this.pullConfig.refreshLoadingWidth) + .height(this.pullConfig.refreshLoadingHeight).visibility(this.getLoadVisibility()) + } + }.alignRules({ + top: { anchor: "title", align: VerticalAlign.Bottom }, + middle: { anchor: "__container__", align: HorizontalAlign.Center } + }).id("arrow") + + Row() { + Text(this.getLoadTips()) + .fontSize(this.pullConfig.pullUpTipsSize) + .fontColor(this.pullConfig.pullUpTipsColor) + .textAlign(TextAlign.Center) + }.id("title").alignRules({ + middle: { anchor: "__container__", align: HorizontalAlign.Center }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).alignItems(VerticalAlign.Center) + + }.width(50).height("100%") + } + + @Builder + defFooterNoMoreViewHorizontal() { + Text(this.pullConfig.noMoreTipsHorizontal) + .textAlign(TextAlign.Center) + .height("100%") + .width(50) + .fontSize(this.pullConfig.noMoreTipsSize) + .fontColor(this.pullConfig.noMoreTipsColor) + } + + @Builder + defFooterView() { + RelativeContainer() { + Stack() { + Image(this.pullConfig.arrowLoadImage) + .width(this.pullConfig.arrowLoadImageWidth) + .height(this.pullConfig.arrowLoadImageHeight) + .visibility(this.getPullUpArrowVisibility()) + .fillColor(this.pullConfig.arrowLoadImageColor) + .margin({ right: 10 }) + .rotate({ + x: 0, + y: 0, + z: 1, + angle: this.arrowRotateLoad + }) + if (this.footerLoadIngView) { + Stack() { + this.footerLoadIngView() + } + .visibility(this.getLoadVisibility()) + } else { + + LoadingProgress() + .color(this.pullConfig.refreshLoadingColor) + .width(this.pullConfig.refreshLoadingWidth) + .height(this.pullConfig.refreshLoadingHeight) + .visibility(this.getLoadVisibility()) + } + }.alignRules({ + right: { anchor: "title", align: HorizontalAlign.Start }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).id("arrow") + + Column() { + Text(this.getLoadTips()).fontSize(this.pullConfig.pullUpTipsSize).fontColor(this.pullConfig.pullUpTipsColor) + }.id("title").alignRules({ + middle: { anchor: "__container__", align: HorizontalAlign.Center }, + center: { anchor: "__container__", align: VerticalAlign.Center } + }).constraintSize({ minWidth: 80 }).alignItems(HorizontalAlign.Center) + + }.height(50).width("100%") + + } + + @Builder + defFooterNoMoreView() { + Text(this.pullConfig.noMoreTips) + .textAlign(TextAlign.Center) + .height(50) + .width("100%") + .fontSize(this.pullConfig.noMoreTipsSize) + .fontColor(this.pullConfig.noMoreTipsColor) + } + + private getTips(): Resource | string { + // todo + if (!this.config.isVertical && this.config.horizontalMode != 1 && this.config.horizontalMode != 2) { + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullDown: + return this.pullConfig.pullDownTipsHorizontal + case PullStatus.PreRefresh: + return this.pullConfig.releaseRefreshTipsHorizontal + case PullStatus.Refresh: + return this.pullConfig.refreshTipsHorizontal + case PullStatus.PreOpenPage: + case PullStatus.OpenPage: + return this.pullConfig.pullOpenPageTipsHorizontal + case PullStatus.RefreshSuccess: + if (this.controller.getConfig().refreshShowSuccess) { + return this.pullConfig.refreshSuccessTipsHorizontal + } else { + /*如果不显示刷新成功的视图*/ + return this.pullConfig.refreshTipsHorizontal + } + case PullStatus.RefreshError: + if (this.controller.getConfig().refreshShowError) { + return this.pullConfig.refreshErrorTipsHorizontal + } else { + /*如果不显示刷新失败的视图*/ + return this.pullConfig.refreshTipsHorizontal + } + default: + return this.pullConfig.pullDownTipsHorizontal + } + } + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullDown: + return this.pullConfig.pullDownTips + case PullStatus.PreRefresh: + return this.pullConfig.releaseRefreshTips + case PullStatus.Refresh: + return this.pullConfig.refreshTips + case PullStatus.PreOpenPage: + case PullStatus.OpenPage: + return this.pullConfig.pullOpenPageTips + case PullStatus.RefreshSuccess: + if (this.controller.getConfig().refreshShowSuccess) { + return this.pullConfig.refreshSuccessTips + } else { + /*如果不显示刷新成功的视图*/ + return this.pullConfig.refreshTips + } + case PullStatus.RefreshError: + if (this.controller.getConfig().refreshShowError) { + return this.pullConfig.refreshErrorTips + } else { + /*如果不显示刷新失败的视图*/ + return this.pullConfig.refreshTips + } + default: + return this.pullConfig.pullDownTips + } + } + + private getLoadTips(): Resource | string { + if (!this.config.isVertical && this.config.horizontalMode != 1 && this.config.horizontalMode != 2) { + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullUp: + return this.pullConfig.pullUpTipsHorizontal + case PullStatus.PreLoad: + return this.pullConfig.releaseLoadTipsHorizontal + case PullStatus.Load: + return this.pullConfig.loadTipsHorizontal + case PullStatus.PreLoadOpenPage: + case PullStatus.LoadOpenPage: + return this.pullConfig.pullOpenPageTipsHorizontal + case PullStatus.LoadSuccess: + if (this.controller.getConfig().loadShowSuccess) { + return this.pullConfig.loadSuccessTipsHorizontal + } else { + /*如果不显示刷新成功的视图*/ + return this.pullConfig.loadTipsHorizontal + } + case PullStatus.LoadError: + if (this.controller.getConfig().loadShowError) { + return this.pullConfig.loadErrorTipsHorizontal + } else { + /*如果不显示刷新失败的视图*/ + return this.pullConfig.loadTipsHorizontal + } + default: + return this.pullConfig.pullUpTipsHorizontal + } + } + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullUp: + return this.pullConfig.pullUpTips + case PullStatus.PreLoad: + return this.pullConfig.releaseLoadTips + case PullStatus.Load: + return this.pullConfig.loadTips + case PullStatus.PreLoadOpenPage: + case PullStatus.LoadOpenPage: + return this.pullConfig.loadOpenPageTips + case PullStatus.LoadSuccess: + if (this.controller.getConfig().loadShowSuccess) { + return this.pullConfig.loadSuccessTips + } else { + /*如果不显示刷新成功的视图*/ + return this.pullConfig.loadTips + } + case PullStatus.LoadError: + if (this.controller.getConfig().loadShowError) { + return this.pullConfig.loadErrorTips + } else { + /*如果不显示刷新失败的视图*/ + return this.pullConfig.loadTips + } + default: + return this.pullConfig.pullUpTips + } + } + + /*下拉箭头显示逻辑*/ + private getPullArrowVisibility(): Visibility { + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullDown: + case PullStatus.PreRefresh: + return Visibility.Visible + } + return Visibility.Hidden + } + + private getRefreshVisibility(): Visibility { + switch (this.status) { + case PullStatus.Refresh: + return Visibility.Visible + case PullStatus.RefreshSuccess: + if (this.controller.getConfig().refreshShowSuccess) { + return Visibility.Hidden + } else { + /*如果不显示刷新成功的视图*/ + return Visibility.Visible + } + case PullStatus.RefreshError: + if (this.controller.getConfig().refreshShowError) { + return Visibility.Hidden + } else { + /*如果不显示刷新失败的视图*/ + return Visibility.Visible + } + } + return Visibility.Hidden + } + + private getPullUpArrowVisibility(): Visibility { + switch (this.status) { + case PullStatus.DEF: + case PullStatus.PullUp: + case PullStatus.PreLoad: + return Visibility.Visible + } + return Visibility.Hidden + } + + private getLoadVisibility(): Visibility { + switch (this.status) { + case PullStatus.Load: + return Visibility.Visible + case PullStatus.LoadSuccess: + if (this.controller.getConfig().loadShowSuccess) { + return Visibility.Hidden + } else { + /*如果不显示刷新成功的视图*/ + return Visibility.Visible + } + case PullStatus.LoadError: + if (this.controller.getConfig().loadShowError) { + return Visibility.Hidden + } else { + /*如果不显示刷新失败的视图*/ + return Visibility.Visible + } + } + return Visibility.Hidden + } + + private getTimeTipsVisibility(): Visibility { + switch (this.status) { + case PullStatus.Refresh: + case PullStatus.PreOpenPage: + case PullStatus.OpenPage: + case PullStatus.RefreshSuccess: + case PullStatus.RefreshError: + return Visibility.None + } + if (this.getLastRefreshTime() <= 0) { + return Visibility.None + } + return Visibility.Visible + } + + private sp: preferences.Preferences | undefined = undefined + private lastRefreshTime: number | undefined = undefined + + private getLastRefreshTime(): number { + if (!this.sp) { + this.sp = preferences.getPreferencesSync(getContext(), { + name: "PullToRefreshLayoutTime" + }) + } + if (this.lastRefreshTime) { + return this.lastRefreshTime + } + let preTime: number = this.sp.getSync("lastRefreshTimeKey" + this.viewKey, 0) as number + this.lastRefreshTime = preTime + return preTime; + } + + private saveRefreshTime() { + if (!this.sp) { + this.sp = preferences.getPreferencesSync(getContext(), { + name: "PullToRefreshLayoutTime" + }) + } + this.lastRefreshTime = new Date().getTime() + this.sp.put("lastRefreshTimeKey" + this.viewKey, this.lastRefreshTime) + this.sp.flush() + } + + private lineBreak(): string { + if (this.isVerticalLayout()) { + return "" + } else { + return "\n" + } + } + + private getLastFormatRefreshTime(): string { + const time = this.getLastRefreshTime(); + if (time <= 0) { + return "" + } + const currentTime = new Date().getTime() + const timeIntervalSecond = Math.floor(((currentTime - time) / 1000)) + let timeFormat: string = "" + if (!this.zr_last_update) { + if (this.isVerticalLayout()) { + this.zr_last_update = getContext().resourceManager.getStringByNameSync(("zr_last_update")) + } else { + this.zr_last_update = "\n" + getContext().resourceManager.getStringByNameSync(("h_zr_last_update")) + } + } + timeFormat = timeFormat + this.zr_last_update + if (timeIntervalSecond < 60) { + timeFormat = timeFormat + this.lineBreak() + timeIntervalSecond; + if (!this.zr_seconds_ago) { + if (this.isVerticalLayout()) { + this.zr_seconds_ago = getContext().resourceManager.getStringByNameSync(("zr_seconds_ago")) + } else { + this.zr_seconds_ago = "\n" + getContext().resourceManager.getStringByNameSync(("h_zr_seconds_ago")) + } + } + timeFormat = timeFormat + this.zr_seconds_ago + } else if (timeIntervalSecond < 3600) { + /*小于一小时*/ + timeFormat = timeFormat + this.lineBreak() + Math.floor((timeIntervalSecond / 60)); + + if (!this.zr_minutes_ago) { + if (this.isVerticalLayout()) { + this.zr_minutes_ago = getContext().resourceManager.getStringByNameSync(("zr_minutes_ago")) + } else { + this.zr_minutes_ago = "\n" + getContext().resourceManager.getStringByNameSync(("h_zr_minutes_ago")) + } + } + timeFormat = timeFormat + this.zr_minutes_ago + } else if (timeIntervalSecond < 86400) { + /*小于一天*/ + timeFormat = timeFormat + this.lineBreak() + Math.floor((timeIntervalSecond / 3600)); + if (!this.zr_hours_ago) { + if (this.isVerticalLayout()) { + this.zr_hours_ago = getContext().resourceManager.getStringByNameSync(("zr_hours_ago")) + } else { + this.zr_hours_ago = "\n" + getContext().resourceManager.getStringByNameSync(("h_zr_hours_ago")) + } + } + timeFormat = timeFormat + this.zr_hours_ago + } else { + /*大于一天*/ + timeFormat = timeFormat + this.lineBreak() + Math.floor((timeIntervalSecond / 86400)); + if (!this.zr_days_ago) { + + if (this.isVerticalLayout()) { + this.zr_days_ago = getContext().resourceManager.getStringByNameSync(("zr_days_ago")) + } else { + this.zr_days_ago = "\n" + getContext().resourceManager.getStringByNameSync(("h_zr_days_ago")) + } + } + timeFormat = timeFormat + this.zr_days_ago + } + return timeFormat + } + + private zr_last_update: string | undefined = undefined + private zr_seconds_ago: string | undefined = undefined + private zr_minutes_ago: string | undefined = undefined + private zr_hours_ago: string | undefined = undefined + private zr_days_ago: string | undefined = undefined + private isUpdateTime = false; + + private updateRefreshTime(status: PullStatus) { + if (status == PullStatus.RefreshSuccess) { + this.saveRefreshTime() + } + if (status == PullStatus.PullDown || status == PullStatus.PreRefresh) { + if (this.isUpdateTime) { + return; + } + this.isUpdateTime = true + setTimeout(() => { + this.zrLastUpdate = this.getLastFormatRefreshTime() + this.startUpdateTime(); + }) + } else { + this.stopUpdateTime(); + } + } + + private startUpdateTime() { + if (!this.isUpdateTime) { + return + } + setTimeout(() => { + this.zrLastUpdate = this.getLastFormatRefreshTime() + // console.log(this.zrLastUpdate + "=======startUpdateTime=====" + (1000 - new Date().getTime() % 1000)); + this.startUpdateTime(); + }, 1000 - new Date().getTime() % 1000) + } + + private stopUpdateTime() { + this.isUpdateTime = false + } +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/RefreshController.ets b/RefreshLib/src/main/ets/RefreshController.ets new file mode 100644 index 0000000..9696ba2 --- /dev/null +++ b/RefreshLib/src/main/ets/RefreshController.ets @@ -0,0 +1,80 @@ +import { PullStatus } from './PullStatus' +import { RefreshLayoutConfig } from './RefreshLayoutConfig' +export enum ViewState{ + success=0, + loading, + empty, + error, + noNetwork +} +export class RefreshController { + /*刷新成功*/ + refreshSuccess: (ignoreViewTips?:boolean) => void = (ignoreViewTips?:boolean) => { + } + /*刷新失败*/ + refreshError: () => void = () => { + } + /*刷新完成,true:成功,false:失败*/ + refreshComplete: (isSuccess: boolean,ignoreViewTips?:boolean) => void = (isSuccess: boolean,ignoreViewTips?:boolean) => { + } + /*取消刷新*/ + refreshCancel: () => void = () => { + } + /*加载成功*/ + loadSuccess: (hasMore?: boolean) => void = (hasMore?: boolean) => { + } + /*加载失败*/ + loadError: () => void = () => { + } + /*加载完成,true:成功,false:失败*/ + loadComplete: (isSuccess: boolean, hasMore?: boolean) => void = (isSuccess: boolean, hasMore?: boolean) => { + } + /*取消加载*/ + loadCancel: () => void = () => { + } + /*显示加载中*/ + viewLoading: () => void = () => { + this.viewState=ViewState.loading + } + /*显示空布局*/ + viewEmpty: () => void = () => { + this.viewState=ViewState.empty + } + /*显示加载失败布局*/ + viewError: () => void = () => { + this.viewState=ViewState.error + } + /*显示无网络布局*/ + viewNoNetwork: () => void = () => { + this.viewState=ViewState.noNetwork + } + /**/ + /*获取当前状态*/ + getStatus: () => PullStatus = () => PullStatus.DEF + /*设置是否还有更多数据*/ + hasMore: (hasMore: boolean) => void = (hasMore: boolean) => { + } + /*手动触发下拉刷新*/ + refresh: () => void = () => { + } + /*手动触发上拉*/ + load: () => void = () => { + } + /*下拉刷新开关是否打开*/ + refreshIsEnable: () => boolean = () => true + /*下拉刷新开关是否打开*/ + loadIsEnable: () => boolean = () => false + /*设置配置*/ + /*由于v2装饰器@Param限制原因,setConfig方法在v2版本上不提供*/ + /*@Param装饰的变量在子组件中无法进行修改。但当装饰的变量类型为对象时,在子组件中修改对象中属性是允许的。*/ + // setConfig: (config: RefreshLayoutConfig) => void = () => {} + /*获取配置*/ + getConfig: () => RefreshLayoutConfig = () => new RefreshLayoutConfig() + /*webview专用*/ + onWebviewScroll: (xOffset: number, yOffset: number) => void = (xOffset: number, yOffset: number) => { + } + private viewState:ViewState=ViewState.success + public getViewState():ViewState{ + return this.viewState + } +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/RefreshLayout.ets b/RefreshLib/src/main/ets/RefreshLayout.ets new file mode 100644 index 0000000..914ba97 --- /dev/null +++ b/RefreshLib/src/main/ets/RefreshLayout.ets @@ -0,0 +1,1610 @@ +import { RefreshLayoutConfig } from './RefreshLayoutConfig' +import { _ContentView, _headerView, _loadMoreView, _noMoreView, RefreshLayoutHelper } from './RefreshLayoutHelper' +import { AnimatorResult } from '@kit.ArkUI' +import animator from '@ohos.animator' +import { PullStatus } from './PullStatus' +import { RefreshController, ViewState } from './RefreshController' +import { PullDown } from './PullDown' +import web from '@ohos.web.webview' + +@ComponentV2 +export struct RefreshLayout { + /*下拉刷新逻辑处理*/ + private helper: RefreshLayoutHelper = new RefreshLayoutHelper() + /*****************************************对外提供属性******************************************************/ + @Param public scroller: Scroller | undefined = undefined + @Param public webviewController: web.WebviewController | undefined = undefined + @Param public controller: RefreshController = new RefreshController() + //头布局视图 + @BuilderParam headerView: () => void = _headerView + //内容视图 + @BuilderParam contentView: () => void = _ContentView + //加载更多视图 + @BuilderParam loadView: () => void = _loadMoreView + //暂无更多视图 + @BuilderParam noMoreView: () => void = _noMoreView + //正在加载视图 + @BuilderParam viewLoading?: () => void + //空视图 + @BuilderParam viewEmpty?: () => void + //加载错误视图 + @BuilderParam viewError?: () => void + //无网络视图 + @BuilderParam viewNoNetwork?: () => void + /*是否可以下拉刷新*/ + @Param public onCanPullRefresh: () => boolean = () => true + /*刷新通知*/ + @Param public onRefresh: () => void = () => { + } + /*加载通知*/ + @Param public onLoad: () => void = () => { + } + /*是否可以上拉加载*/ + @Param public onCanPullLoad: () => boolean = () => false //todo + /*下拉状态监听*/ + @Param public onPullListener: (pullDown: PullDown) => void = () => { + } + /*打开页面通知*/ + @Param public onOpenPage: () => void = () => { + } + /*打开页面通知*/ + @Param public onLoadOpenPage: () => void = () => { + } + /*下拉刷新配置*/ + @Param public config: RefreshLayoutConfig = new RefreshLayoutConfig() + /***********************************************************************************************/ + /*下拉或右拉总偏移量*/ + @Local currentOffset: number = 0 + /*当前状态*/ + @Local status: PullStatus = PullStatus.DEF + /*上拉或左拉总偏移量*/ + @Local currentOffsetLoad: number = 0 + /*是否有更多数据*/ + @Local hasMore: boolean = true + /*防止重复挂载,这里通过多个变量动态控制挂载,显示,隐藏*/ + @Local viewSuccessState: number = 1 + @Local viewLoadingState: number = 0 + @Local viewEmptyState: number = 0 + @Local viewErrorState: number = 0 + @Local viewNoNetworkState: number = 0 + + /***********************************************************************************************/ + //region onMeasureSize + private log(str: string, tag: string = "RefreshLayout") { + console.debug(tag + "==" + str) + } + + private setViewLoadSuccessState() { + this.viewSuccessState = 1 + /*不等于0才修改值*/ + if (this.viewLoadingState) { + this.viewLoadingState = 2 + } + if (this.viewEmptyState) { + this.viewEmptyState = 2 + } + if (this.viewErrorState) { + this.viewErrorState = 2 + } + if (this.viewNoNetworkState) { + this.viewNoNetworkState = 2 + } + } + + private setViewLoadingState() { + this.viewLoadingState = 1 + if (this.viewEmptyState) { + this.viewEmptyState = 2 + } + if (this.viewErrorState) { + this.viewErrorState = 2 + } + if (this.viewNoNetworkState) { + this.viewNoNetworkState = 2 + } + this.viewSuccessState = 0 + } + + private setViewLoadEmptyState() { + if (this.viewLoadingState) { + this.viewLoadingState = 2 + } + this.viewEmptyState = 1 + if (this.viewErrorState) { + this.viewErrorState = 2 + } + if (this.viewNoNetworkState) { + this.viewNoNetworkState = 2 + } + this.viewSuccessState = 0 + } + + private setViewLoadErrorState() { + if (this.viewLoadingState) { + this.viewLoadingState = 2 + } + if (this.viewEmptyState) { + this.viewEmptyState = 2 + } + this.viewErrorState = 1 + if (this.viewNoNetworkState) { + this.viewNoNetworkState = 2 + } + this.viewSuccessState = 0 + } + + private setViewLoadNoNetworkState() { + if (this.viewLoadingState) { + this.viewLoadingState = 2 + } + if (this.viewEmptyState) { + this.viewEmptyState = 2 + } + if (this.viewErrorState) { + this.viewErrorState = 2 + } + this.viewNoNetworkState = 1 + this.viewSuccessState = 0 + } + + private sizeResult: SizeResult = { width: 0, height: 0 } + + onMeasureSize(selfLayoutInfo: GeometryInfo, children: Measurable[], constraint: ConstraintSizeOptions): SizeResult { + // this.log("======onAppear=1") + const count = children.length + if (typeof (constraint.maxWidth) == "number") { + this.sizeResult.width = constraint.maxWidth as number; + } + if (typeof (constraint.maxHeight) == "number") { + this.sizeResult.height = constraint.maxHeight as number; + } + for (let i = 0; i < count; i++) { + const result = children[i].measure(constraint) + if (i == 0) { + if (this.config.isVertical || this.config.horizontalMode == 1 || this.config.horizontalMode == 2) { + /*保存header高度*/ + this.helper.headerSize = result.height + } else { + /*保存header宽度*/ + this.helper.headerSize = result.width + } + } else if (i == 1 && (this.sizeResult.width <= 0 || this.sizeResult.height <= 0)) { + this.sizeResult.width = result.width; + this.sizeResult.height = result.height; + } else if (i == 2) { + if (this.config.isVertical || this.config.horizontalMode == 1 || this.config.horizontalMode == 2) { + /*保存footer高度*/ + this.helper.footerSize = result.height + } else { + /*保存footer宽度*/ + this.helper.footerSize = result.width + } + } + } + return this.sizeResult + } + + onPlaceChildren(selfLayoutInfo: GeometryInfo, children: Layoutable[], constraint: ConstraintSizeOptions): void { + let count = children.length + if (count <= 0) { + return + } + for (let i = 0; i < count; i++) { + const child = children[i] + if (i == 0) { + /*header*/ + // this.helper.initOffsetY = -child.measureResult.height + if (this.config.isVertical) { + child.layout({ y: -child.measureResult.height }) + } else { + if (this.config.horizontalMode == 1 || this.config.horizontalMode == 2) { + child.layout({ + x: -child.measureResult.width / 2 - child.measureResult.height / 2, + y: this.sizeResult.height / 2 - child.measureResult.height / 2 + }) + } else { + child.layout({ x: -child.measureResult.width }) + } + } + } else if (i == 1) { + /*content*/ + child.layout({}) + } else if (i == 2) { + /*footer*/ + if (this.config.isVertical) { + child.layout({ y: this.sizeResult.height }) + } else { + if (this.config.horizontalMode == 1 || this.config.horizontalMode == 2) { + child.layout({ + x: this.sizeResult.width - child.measureResult.width / 2 + child.measureResult.height / 2, + y: this.sizeResult.height / 2 - child.measureResult.height / 2 + }) + } else { + child.layout({ x: this.sizeResult.width }) + } + } + } + } + } + + build() { + this.headerAndContent() + } + + // endregion + /***********************************************************************************************/ + + + /*****************************************下拉刷新******************************************************/ + /*释放刷新回弹至header动画*/ + private anim: AnimatorResult | undefined = animator.create(this.helper.animOptions); + /*回弹动画暂停*/ + private animPause = false + private setDefStatusId = 0; + /*自动刷新动画*/ + private autoRefreshAnim: AnimatorResult | undefined = undefined + /*记录动画执行进度,在回弹至header高度过程中记录已经执行了多少进度,方便在回弹header高度之前收到刷新或者加载成功的结果后,再执行剩余时间的动画*/ + private animProgress: number = 1 + /******************************************上拉加载*****************************************************/ + /*释放加载回弹至footer动画*/ + private animLoad: AnimatorResult | undefined = undefined + /*回弹动画暂停*/ + private animLoadPause = false + private setDefLoadStatusId = 0; + /*自动刷新动画*/ + private autoLoadAnim: AnimatorResult | undefined = undefined + /*记录动画执行进度,在回弹至header高度过程中记录已经执行了多少进度,方便在回弹header高度之前收到刷新或者加载成功的结果后,再执行剩余时间的动画*/ + private animProgressLoad: number = 1 + + aboutToAppear(): void { + /*刷新成功*/ + this.controller.refreshSuccess = (ignoreViewTips?: boolean) => { + this.refreshSuccess(ignoreViewTips) + } + /*刷新失败*/ + this.controller.refreshError = () => { + this.refreshError() + } + /*通过参数通知刷新结果*/ + this.controller.refreshComplete = (isSuccess: boolean, ignoreViewTips?: boolean) => { + this.refreshComplete(isSuccess, ignoreViewTips) + } + /*刷新取消*/ + this.controller.refreshCancel = () => { + this.refreshCancel() + } + + /*刷新成功*/ + this.controller.loadSuccess = (hasMore?: boolean) => { + this.loadSuccess(hasMore) + } + /*刷新失败*/ + this.controller.loadError = () => { + this.loadError() + } + /*通过参数通知加载结果*/ + this.controller.loadComplete = (isSuccess: boolean, hasMore?: boolean) => { + this.loadComplete(isSuccess, hasMore) + } + /*加载取消*/ + this.controller.loadCancel = () => { + this.loadCancel() + } + /*设置当前是否还有更多*/ + this.controller.hasMore = (hasMore: boolean) => { + this.hasMore = hasMore + } + /*获取当前状态*/ + this.controller.getStatus = () => { + return this.status + } + /*自动刷新*/ + this.controller.refresh = () => { + this.autoRefresh() + } + /*自动加载*/ + this.controller.load = () => { + this.autoLoad() + } + /*是否开启刷新*/ + this.controller.refreshIsEnable = () => { + return this.config.pullRefreshEnable + } + /*是否开启加载*/ + this.controller.loadIsEnable = () => { + return this.config.pullLoadEnable + } + /*设置配置*/ + /*由于v2装饰器@Param限制原因,setConfig方法在v2版本上不提供*/ + /*@Param装饰的变量在子组件中无法进行修改。但当装饰的变量类型为对象时,在子组件中修改对象中属性是允许的。*/ + /* this.controller.setConfig = (config: RefreshLayoutConfig) => { + this.config = config + }*/ + this.controller.getConfig = () => this.config + /*webView专用*/ + this.controller.onWebviewScroll = (xOffset, yOffset) => { + this.helper.webViewXOffset = xOffset; + this.helper.webViewYOffset = yOffset + } + const viewState = this.controller.getViewState() + if (viewState == ViewState.loading) { + this.setViewLoadingState() + } else if (viewState == ViewState.empty) { + this.setViewLoadEmptyState() + } else if (viewState == ViewState.error) { + this.setViewLoadErrorState() + } else if (viewState == ViewState.noNetwork) { + this.setViewLoadNoNetworkState() + } + this.controller.viewLoading = () => { + if (this.viewLoading) { + this.setViewLoadingState() + } + } + + this.controller.viewEmpty = () => { + if (this.viewEmpty) { + this.setViewLoadEmptyState() + } + } + + this.controller.viewError = () => { + if (this.viewError) { + this.setViewLoadErrorState() + } + } + + this.controller.viewNoNetwork = () => { + if (this.viewNoNetwork) { + this.setViewLoadNoNetworkState() + } + } + this.setAnimListener() + } + + aboutToDisappear(): void { + //按照官方示例,置空处理防止内存泄漏 + this.anim = undefined + this.autoRefreshAnim = undefined + + this.animLoad = undefined + } + + private setAnimListener() { + if (!this.anim) { + return + } + this.anim.onFrame = (progress: number) => { + if (this.animPause) { + return + } + this.animProgress = progress + this.currentOffset = this.helper.totalOffset * (1 - progress) + } + this.anim.onFinish = () => { + this.animProgress = 1 + /*如果是回弹至header高度结束,开始判断是否是刷新完成或失败状态,继续回弹动画*/ + if (this.status == PullStatus.RefreshSuccess || this.status == PullStatus.RefreshError) { + if (this.currentOffset > 0) { + //开始隐藏header动画 + this.hiddenHeaderAnim() + } else if (this.currentOffset <= 0) { + /*如果动画结束,需要改变状态*/ + this.setDefStatus() + } + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 0) + return + } + if (this.helper.notReleaseRefresh && this.currentOffset <= 0) { + /*非释放刷新,在刷新完成后,需要改变状态,否则非释放刷新时不松手,刷新完成后再下拉没反应*/ + this.helper.notReleaseRefresh = false + } + this.helper.totalOffset = this.currentOffset; + this.changeStatus(this.currentOffset) + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 1) + } + this.anim.onCancel = () => { + this.helper.totalOffset = this.currentOffset; + } + } + + //用于上拉加载更多 + private initLoadAnim() { + if (this.animLoad) { + return + } + this.animLoad = animator.create(this.helper.animLoadOptions); + this.animLoad.onFrame = (progress: number) => { + if (this.animLoadPause) { + return + } + this.animProgressLoad = progress + this.currentOffsetLoad = this.helper.totalOffsetLoad * (1 - progress) + } + this.animLoad.onFinish = () => { + this.animProgressLoad = 1 + /*如果是回弹至header高度结束,开始判断是否是刷新完成或失败状态,继续回弹动画*/ + if (this.status == PullStatus.LoadSuccess || this.status == PullStatus.LoadError) { + if (this.currentOffsetLoad < 0) { + //开始隐藏footer动画 + this.hiddenFooterAnim() + } else if (this.currentOffsetLoad <= 0) { + /*如果动画结束,需要改变状态*/ + this.setDefStatusLoad() + } + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 2) + return + } + if (this.helper.notReleaseLoad && this.currentOffsetLoad >= 0) { + /*非释放加载,在加载完成后,需要改变状态,否则非释放加载时不松手,加载完成后再上拉没反应*/ + this.helper.notReleaseLoad = false + } + this.helper.totalOffsetLoad = this.currentOffsetLoad; + this.changeStatusLoad(this.currentOffsetLoad) + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 3) + } + this.animLoad.onCancel = () => { + this.helper.totalOffsetLoad = this.currentOffsetLoad; + } + } + + private getHeaderOffsetY() { + return this.currentOffset; + } + + private getFooterOffsetY() { + return this.currentOffsetLoad; + } + + /*下拉高度触发刷新*/ + public getPullRefreshHeaderHeight(): number { + if (this.config.pullHeaderHeightRefresh <= 0) { + this.config.pullHeaderHeightRefresh = this.helper.headerSize * 1.5 + } + return this.config.pullHeaderHeightRefresh + } + + /*下拉高度触发打开页面*/ + public getPullOpenPageHeight(): number { + if (this.config.pullHeaderHeightOpenPage <= this.getPullRefreshHeaderHeight()) { + this.config.pullHeaderHeightOpenPage = this.helper.headerSize * 2.6 + } + return this.config.pullHeaderHeightOpenPage + } + + /*上拉高度触发加载*/ + public getPullLoadFooterHeight(): number { + if (this.config.pullFooterHeightLoad <= 0) { + this.config.pullFooterHeightLoad = this.helper.footerSize * 1.1 + } + return this.config.pullFooterHeightLoad + } + + /*上拉高度触发打开页面*/ + public getPullLoadOpenPageHeight(): number { + if (this.config.pullFooterHeightOpenPage <= this.getPullLoadFooterHeight()) { + this.config.pullFooterHeightOpenPage = this.helper.footerSize * 2.6 + } + return this.config.pullFooterHeightOpenPage + } + + private getAngle() { + if (this.config.isVertical) { + return 0 + } + /*横向模式0:正常横向布局,1:header和footer逆时针旋转90度,2:header和footer顺时针旋转90度*/ + if (this.config.horizontalMode == 1) { + return -90 + } else if (this.config.horizontalMode == 2) { + return 90 + } + return 0 + } + + /*是否是垂直列表或者布局旋转后的横向header或footer*/ + private isVerticalLayout(): boolean { + return (this.config.isVertical || this.config.horizontalMode == 1 || this.config.horizontalMode == 2) + } + + /***********************************************************************************************/ + @Builder + private headerAndContent() { + Stack() { + this.headerView() + } + .onAppear(() => { + // this.totalOffsetY = this.helper.initOffsetY + this.getPullRefreshHeaderHeight() + this.getPullOpenPageHeight() + }) + .offset({ + y: this.config.isVertical ? this.getHeaderOffsetY() + this.getFooterOffsetY() : 0, + x: this.config.isVertical ? 0 : this.getHeaderOffsetY() + this.getFooterOffsetY() + }) + .rotate({ + angle: this.getAngle(), + z: 1, + x: 0, + y: 0 + }) + .width(this.isVerticalLayout() ? "100%" : "auto") + .height(this.isVerticalLayout() ? "auto" : "100%") + + // .borderWidth(2) + // .borderColor(Color.Black) + + Stack() { + /*正在加载中视图*/ + if (this.viewLoading && this.viewLoadingState) { + Stack() { + this.viewLoading() + }.width("100%").height("100%").visibility(this.viewLoadingState == 1 ? Visibility.Visible : Visibility.Hidden) + } + /*空数据视图*/ + if (this.viewEmpty && this.viewEmptyState) { + Stack() { + this.viewEmpty() + }.width("100%").height("100%").visibility(this.viewEmptyState == 1 ? Visibility.Visible : Visibility.Hidden) + } + /*数据加载失败视图*/ + if (this.viewError && this.viewErrorState) { + Stack() { + this.viewError() + }.width("100%").height("100%").visibility(this.viewErrorState == 1 ? Visibility.Visible : Visibility.Hidden) + } + /*无网络视图*/ + if (this.viewNoNetwork && this.viewNoNetworkState) { + Stack() { + this.viewNoNetwork() + }.width("100%").height("100%").visibility(this.viewNoNetworkState == 1 ? Visibility.Visible : Visibility.Hidden) + } + Stack() { + this.contentView() + }.width("100%").height("100%").visibility(this.viewSuccessState == 1 ? Visibility.Visible : Visibility.Hidden) + } + .offset({ + y: this.config.isVertical ? this.getHeaderOffsetY() + this.getFooterOffsetY() : 0, + x: this.config.isVertical ? 0 : this.getHeaderOffsetY() + this.getFooterOffsetY() + }) + // .borderWidth(2) + // .borderColor(Color.Red) + .width("100%") + .height("100%") + .onTouch((event: TouchEvent) => { + if (this.status == PullStatus.OpenPage) { + /*下拉打开其他页面回弹结束前不响应事件*/ + return; + } + if (this.status == PullStatus.LoadOpenPage) { + /*上拉打开其他页面回弹结束前不响应事件*/ + return; + } + if (event.type == TouchType.Down) { + + //todo 临时方案,后续等官方增加事件拦截后再替换 + if (this.currentOffset > 0 || this.currentOffsetLoad < 0) { + //因为通过Scroller.scrollTo不让列表滑动,这里提前记录当前滑动距离,后面判断最大值(回弹期间快速松手,回弹完成之前再按下,需要记录grid列表当前滑动距离,防止上拉再下拉时列表跟随滚动) + if (this.scroller) { + /*其他列表记录当前滑动距离*/ + if (this.config.isVertical) { + /*垂直列表*/ + this.helper.scrollerOffset = this.scroller?.currentOffset()?.yOffset ?? 0 + } else { + /*水平列表*/ + this.helper.scrollerOffset = this.scroller?.currentOffset()?.xOffset ?? 0 + } + } else if (this.webviewController) { + /*webview记录当前滑动距离*/ + this.helper.scrollerOffset = + this.config.isVertical ? this.helper.webViewYOffset : this.helper.webViewXOffset + } + } + + + this.helper.isPressDown = true + /*暂停下拉刷新相关动画*/ + this.pauseAnim() + /*暂停上拉加载相关动画*/ + this.pauseAnimLoad() + if (this.status == PullStatus.RefreshSuccess || this.status == PullStatus.RefreshError) { + /*刷新中,或者刷新完成,此时触摸暂停动画,但是状态还是需要继续改变*/ + this.setDefAfterRefreshEnd(this.config.refreshResultDurationTime) + } else if (this.status == PullStatus.LoadSuccess || this.status == PullStatus.LoadError) { + /*加载中,或者加载完成,此时触摸暂停动画,但是状态还是需要继续改变*/ + this.setDefAfterLoadEnd(this.config.loadResultDurationTime) + } + + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: true, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 4) + } else if (event.type == TouchType.Up || event.type == TouchType.Cancel) { + this.helper.scrollerOffset = 0 + this.helper.notReleaseRefresh = false + this.helper.notReleaseLoad = false + /*刷新完成状态,开始倒计时重置状态时,正好触发down事件,如果在倒计时结束之前this.currentOffsetY>0触发了up或者cancel事件,则需要取消,否则在回弹期间会突然改变状态*/ + /*下拉刷新相关*/ + if (this.setDefStatusId != 0 && this.currentOffsetLoad > 0) { + clearTimeout(this.setDefStatusId) + this.setDefStatusId = 0 + } + /*同上*/ + /*上拉加载相关*/ + if (this.setDefLoadStatusId != 0 && this.currentOffsetLoad < 0) { + clearTimeout(this.setDefLoadStatusId) + this.setDefLoadStatusId = 0 + } + + this.helper.isPressDown = false + this.animLoadPause = false + this.animPause = false + + if (this.currentOffset > 0 || (this.currentOffset == 0 && !this.isLoadStatus())) { + /*是否关闭下拉刷新*/ + if (this.config.pullRefreshEnable) { + /*如果处于加载相关的状态,则不需要执行该方法*/ + this.releaseActionByRefresh() + } + } else if (this.currentOffsetLoad <= 0) { + /*是否关闭上拉加载*/ + if (this.config.pullLoadEnable) { + this.releaseActionByLoad() + } + } + + + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: false, + isTouch: false, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 5) + } + }) + .parallelGesture(PanGesture(new PanGestureOptions(this.config.isVertical ? { + direction: PanDirection.Down | PanDirection.Up + } : { direction: PanDirection.Horizontal })) + .onActionStart((event: GestureEvent) => { + if (this.status == PullStatus.OpenPage) { + /*打开其他页面回弹结束前不响应事件*/ + return; + } + if (this.status == PullStatus.LoadOpenPage) { + /*打开其他页面回弹结束前不响应事件*/ + return; + } + this.helper.preOffset = this.getOffset(event) + }) + .onActionUpdate((event: GestureEvent) => { + if (this.getOffset(event) == this.helper.preOffset) { + /*如果没有偏移量*/ + return + } + const pullDown = this.getOffset(event) > this.helper.preOffset + /*下拉刷新相关动作*/ + //因为下拉之后可以上拉,所以增加this.currentOffsetY>0判断 + if ((pullDown || this.currentOffset > 0) && (this.currentOffsetLoad >= 0)) { //如果加载视图屏幕不可见,才允许下拉 + /*关闭下拉刷新*/ + if (!this.config.pullRefreshEnable) { + this.helper.preOffset = this.getOffset(event) + return + } + /*下拉后可以往上拉,所以需要event.offsetY > this.helper.preOffsetY判断,onCanPullRefresh需要配合下拉动作,这里如果是上拉动作,就不需要判断onCanPullRefresh*/ + if (pullDown && !this.onCanPullRefresh?.() && this.viewSuccessState == 1) { + this.helper.preOffset = this.getOffset(event) + /*不可以下拉*/ + return + } + //todo 临时方案,后续等官方增加事件拦截后再替换 + if (this.currentOffset > 0 && !pullDown) { + /*如果下拉再上拉,不让列表滑动*/ + if (this.scroller) { + /*其他列表*/ + if (this.config.isVertical) { + this.scroller.scrollTo({ yOffset: 0, xOffset: this.scroller.currentOffset()?.xOffset ?? 0 }) + } else { + this.scroller.scrollTo({ yOffset: this.scroller.currentOffset()?.yOffset ?? 0, xOffset: 0 }) + } + } else if (this.webviewController) { + /*webview*/ + if (this.config.isVertical) { + this.webviewController.scrollTo(this.helper.webViewXOffset, 0) + } else { + this.webviewController.scrollTo(0, this.helper.webViewYOffset) + } + } + } + /*如果正在加载中,加载成功或失败,不允许下拉*/ + if (this.status == PullStatus.Load || this.status == PullStatus.LoadSuccess || + this.status == PullStatus.LoadError) { + return + } + this.actionUpdateForRefresh(event) + + return + } + /*上拉加载相关动作*/ + if (!pullDown || this.currentOffsetLoad < 0) { + /*关闭上拉加载*/ + if (!this.config.pullLoadEnable) { + this.helper.preOffset = this.getOffset(event) + return + } + //todo 临时方案,后续等官方增加事件拦截后再替换 + if (this.onCanPullLoad?.()) { + //因为通过Scroller.scrollTo不让列表滑动,这里提前记录当前滑动距离,后面判断最大值 + if (this.scroller) { + /*其他列表*/ + if (this.config.isVertical) { + /*垂直列表*/ + this.helper.scrollerOffset = this.scroller?.currentOffset()?.yOffset ?? 0 + } else { + /*水平列表*/ + this.helper.scrollerOffset = this.scroller?.currentOffset()?.xOffset ?? 0 + } + } else if (this.webviewController) { + /*webview*/ + this.helper.scrollerOffset = + this.config.isVertical ? this.helper.webViewYOffset : this.helper.webViewXOffset + } + } + /*上拉后可以往下拉,所以需要event.offsetY < this.helper.preOffsetYLoad判断,onCanPullLoad需要配合上拉动作,这里如果是下拉动作,就不需要判断onCanPullLoad*/ + if (!pullDown && !this.onCanPullLoad?.()) { + this.helper.preOffset = this.getOffset(event) + /*不可以上拉*/ + return + } + //todo 临时方案,后续等官方增加事件拦截后再替换 + if (this.currentOffsetLoad < 0 && pullDown) { + /*如果下拉再上拉,不让列表滑动*/ + if (this.scroller) { + /*其他列表*/ + let offset = this.scroller.currentOffset() + if (offset) { + /*判断垂直还是水平列表*/ + if (this.config.isVertical) { + this.scroller.scrollTo({ + yOffset: Math.max(this.helper.scrollerOffset, + this.config.isVertical ? offset.yOffset : offset.xOffset), + xOffset: offset.xOffset + }) + } else { + this.scroller.scrollTo({ + yOffset: offset.yOffset, + xOffset: Math.max(this.helper.scrollerOffset, + this.config.isVertical ? offset.yOffset : offset.xOffset) + }) + } + } + } else if (this.webviewController) { + /*webview*/ + if (this.config.isVertical) { + this.webviewController.scrollTo(this.helper.webViewXOffset, this.helper.scrollerOffset) + } else { + this.webviewController.scrollTo(this.helper.scrollerOffset, this.helper.webViewYOffset) + } + } + } + /*如果正在刷新中,加载成功或失败,不允许下拉*/ + if (this.status == PullStatus.Refresh || this.status == PullStatus.RefreshSuccess || + this.status == PullStatus.RefreshError) { + return + } + this.actionUpdateForLoad(event) + return + } + }) + .onActionEnd((event: GestureEvent) => { + // this.releaseAction() + }) + .onActionCancel(() => { + // this.releaseAction() + })) + + if (this.config.pullLoadEnable) { + Stack() { + if (this.hasMore || this.status == PullStatus.PreLoadOpenPage || this.status == PullStatus.LoadOpenPage) { + this.loadView() + } else { + this.noMoreView() + } + } + .onAppear(() => { + this.getPullLoadFooterHeight() + this.getPullLoadOpenPageHeight() + }) + .offset({ + y: this.config.isVertical ? this.getFooterOffsetY() : 0, + x: this.config.isVertical ? 0 : this.getFooterOffsetY() + }) + .rotate({ + angle: this.getAngle(), + z: 1, + x: 0, + y: 0 + }) + .width(this.isVerticalLayout() ? "100%" : "auto") + .height(this.isVerticalLayout() ? "auto" : "100%") + + // .borderWidth(2) + // .borderColor(Color.Black) + } + } + + private getOffset(event: GestureEvent) { + if (this.config.isVertical) { + return event.offsetY + } else { + return event.offsetX + } + } + + /*下拉刷新*/ + private actionUpdateForRefresh(event: GestureEvent) { + /*如果触发了非释放刷新,需要在up事件中改变状态*/ + if (this.helper.notReleaseRefresh) { + /*非释放刷新过程中,继续下拉,需要保存当前偏移量,(否则保存的是刚触发刷新时的偏移量),等刷新完成后,防止出现headerView闪动情况*/ + this.helper.preOffset = this.getOffset(event) + return + } + if (this.status == PullStatus.OpenPage) { + /*打开其他页面回弹结束前不响应事件*/ + return; + } + if (this.config.pullRefreshResistance < 0) { + this.config.pullRefreshResistance = 0.5 + } else if (this.config.pullRefreshResistance > 1) { + this.config.pullRefreshResistance = 1 + } + this.currentOffset = + this.currentOffset + (this.getOffset(event) - this.helper.preOffset) * this.config.pullRefreshResistance + if (this.currentOffset < 0) { + this.currentOffset = 0; + } else if (this.currentOffset > this.config.pullMaxDistance) { + this.currentOffset = this.config.pullMaxDistance + } + this.changeStatus(this.currentOffset) + /*状态监听*/ + this.pullDownListener({ + isPullDown: this.currentOffset > this.helper.totalOffset, + isPullUp: this.currentOffsetLoad < this.helper.totalOffsetLoad, + isTouch: true, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 6) + this.helper.totalOffset = this.currentOffset + this.helper.preOffset = this.getOffset(event) + /*是否是下拉不用up事件触发刷新*/ + if (!this.config.releaseRefresh) { + if (this.currentOffset >= this.getPullRefreshHeaderHeight() && this.status != PullStatus.Refresh && + this.status != PullStatus.RefreshSuccess && this.status != PullStatus.RefreshError) { + this.helper.notReleaseRefresh = true + //自动触发刷新,改变动画暂停状态 + this.animPause = false + this.releaseActionByRefresh() + return + } + } + } + + /*加载更多*/ + private actionUpdateForLoad(event: GestureEvent) { + /*如果触发了非释放加载,需要在up事件中改变状态*/ + if (this.helper.notReleaseLoad) { + /*非释放加载过程中,继续上拉,需要保存当前偏移量,(否则保存的是刚触发加载时的偏移量),等加载完成后,防止出现footerView闪动情况*/ + this.helper.preOffset = this.getOffset(event) + return + } + if (this.status == PullStatus.LoadOpenPage) { + /*打开其他页面回弹结束前不响应事件*/ + return; + } + if (this.config.pullLoadResistance < 0) { + this.config.pullLoadResistance = 0.5 + } else if (this.config.pullLoadResistance > 1) { + this.config.pullLoadResistance = 1 + } + this.currentOffsetLoad = + this.currentOffsetLoad + (this.getOffset(event) - this.helper.preOffset) * this.config.pullLoadResistance + if (this.currentOffsetLoad > 0) { + this.currentOffsetLoad = 0; + } else if (-this.currentOffsetLoad > this.config.pullLoadMaxDistance) { + this.currentOffsetLoad = -this.config.pullLoadMaxDistance + } + this.changeStatusLoad(this.currentOffsetLoad) + /*状态监听*/ + this.pullDownListener({ + isPullDown: this.currentOffset > this.helper.totalOffset, + isPullUp: this.currentOffsetLoad < this.helper.totalOffsetLoad, + isTouch: true, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 7) + this.helper.totalOffsetLoad = this.currentOffsetLoad + this.helper.preOffset = this.getOffset(event) + + if (this.hasMore) { + /*如果有更多数据*/ + /*是否是下拉不用up事件触发刷新*/ + if (!this.config.releaseLoad) { + if (-this.currentOffsetLoad >= this.getPullLoadFooterHeight() && this.status != PullStatus.Load && + this.status != PullStatus.LoadSuccess && this.status != PullStatus.LoadError) { + this.helper.notReleaseLoad = true + //自动触发刷新,改变动画暂停状态 + this.animLoadPause = false + this.releaseActionByLoad() + return + } + } + } + } + + private pullDownListener(pullDown: PullDown, flag: number = 0) { + /*状态监听*/ + this.onPullListener?.(pullDown) + // this.log(flag + "===pullDownListener===" + pullDown.isPullDown) + } + + public autoRefresh() { + if (this.status == PullStatus.Refresh || this.status == PullStatus.RefreshSuccess || + this.status == PullStatus.RefreshError || this.status == PullStatus.OpenPage) { + return + } + if (this.status == PullStatus.Load) { + return + } + if (!this.autoRefreshAnim) { + this.autoRefreshAnim = animator.create({ + duration: 200, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + }) + this.autoRefreshAnim.onFrame = (progress: number) => { + /*如果触摸视图,也暂停处理*/ + if (this.animPause) { + this.autoRefreshAnim?.cancel() + return + } + this.currentOffset = this.helper.headerSize * progress + this.helper.totalOffset = this.currentOffset + } + this.autoRefreshAnim.onFinish = () => { + this.releaseActionByRefresh() + } + } + this.status = PullStatus.Refresh + this.onRefresh?.() + this.autoRefreshAnim.play() + /*状态监听*/ + this.pullDownListener({ + isPullDown: true, + isPullUp: false, + isTouch: this.helper.isPressDown, + distance: this.helper.headerSize, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 8) + } + + public autoLoad() { + if (this.status == PullStatus.Load || this.status == PullStatus.LoadSuccess || + this.status == PullStatus.LoadError || this.status == PullStatus.LoadOpenPage) { + return + } + if (this.status == PullStatus.Refresh) { + return + } + if (!this.autoLoadAnim) { + this.autoLoadAnim = animator.create({ + duration: 200, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + }) + this.autoLoadAnim.onFrame = (progress: number) => { + /*如果触摸视图,也暂停处理*/ + if (this.animLoadPause) { + this.autoLoadAnim?.cancel() + return + } + this.currentOffsetLoad = -this.helper.footerSize * progress + this.helper.totalOffsetLoad = this.currentOffsetLoad + } + this.autoLoadAnim.onFinish = () => { + this.releaseActionByLoad() + } + } + this.status = PullStatus.Load + this.onLoad?.() + this.autoLoadAnim.play() + /*状态监听*/ + this.pullDownListener({ + isPullDown: false, + isPullUp: true, + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: -this.helper.footerSize, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 82) + } + + /*刷新成功*/ + public refreshSuccess(ignoreViewTips?: boolean) { + /*如果上一个状态不是刷新中,直接返回*/ + if (this.status != PullStatus.Refresh && this.viewSuccessState == 1) { + /*如果显示其他异常页面,则刷新成功不需要校验刷新中的状态*/ + return + } + /*刷新成功后,可能存在暂无更多,这里不改成true*/ + // this.hasMore=true + if (!ignoreViewTips) { + this.status = PullStatus.RefreshSuccess + } + this.setViewLoadSuccessState() + this.refreshEnd(); + } + + /*刷新失败*/ + public refreshError() { + /*如果上一个状态不是刷新中,直接返回*/ + if (this.status != PullStatus.Refresh) { + return + } + this.status = PullStatus.RefreshError + this.refreshEnd(); + } + + /*完成刷新*/ + public refreshComplete(refreshSuccess: boolean, ignoreViewTips?: boolean) { + if (refreshSuccess) { + this.refreshSuccess(ignoreViewTips); + } else { + this.refreshError(); + } + } + + /*刷新取消*/ + public refreshCancel() { + /*如果上一个状态不是刷新中,直接返回*/ + if (this.status != PullStatus.Refresh) { + return + } + this.refreshEnd(); + } + + private refreshEnd() { + /*状态监听*/ + this.pullDownListener({ + isPullDown: false || (this.helper.isPressDown && this.helper.preOffset < this.currentOffset), + isPullUp: false || (this.helper.isPressDown && this.helper.preOffset > this.currentOffsetLoad), + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 9) + + /*如果不需要释放刷新情况下,不需要考虑是否是触摸状态*/ + if (!this.helper.notReleaseRefresh) { + /*如果是touch状态,则返回不做回弹操作*/ + if (this.helper.isPressDown || this.currentOffset <= 0) { + //超过显示刷新结果的时间之后改变状态 + this.setDefAfterRefreshEnd(this.config.refreshResultDurationTime) + return + } + } + /*设置刷新结果之后准备回弹*/ + if (this.currentOffset > this.helper.headerSize) { + /*如果回弹至header高度前就刷新成功了,则先回弹至header高度,再回弹至0高度*/ + // 这里会直接回弹至header高度后再回弹至0高度,所以不需要暂停动画再回弹 + this.pauseAnim() + this.releaseActionByRefresh(); + // (动画执行完可能存在误差(精度问题),导致return不会执行hiddenFooterAnim,最终还是不注释) + return + } + /*如果偏移量距离小于等于header高度,直接回弹至0高度*/ + this.hiddenHeaderAnim() + } + + /*加载成功*/ + public loadSuccess(hasMore: boolean = true) { + /*如果上一个状态不是加载中,直接返回*/ + if (this.status != PullStatus.Load) { + return + } + this.hasMore = hasMore + this.status = PullStatus.LoadSuccess + this.loadEnd(); + } + + /*加载失败*/ + public loadError() { + /*如果上一个状态不是加载中,直接返回*/ + if (this.status != PullStatus.Load) { + return + } + this.status = PullStatus.LoadError + this.loadEnd(); + } + + /*加载完成*/ + public loadComplete(loadSuccess: boolean, hasMore: boolean = true) { + if (loadSuccess) { + this.loadSuccess(hasMore); + } else { + this.loadError(); + } + } + + /*加载取消*/ + public loadCancel() { + /*如果上一个状态不是加载中,直接返回*/ + if (this.status != PullStatus.Load) { + return + } + this.loadEnd(); + } + + private loadEnd() { + /*状态监听*/ + this.pullDownListener({ + isPullDown: false || (this.helper.isPressDown && this.helper.preOffset < this.currentOffset), + isPullUp: false || (this.helper.isPressDown && this.helper.preOffset > this.currentOffsetLoad), + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 9) + + /*如果不需要释放加载情况下,不需要考虑是否是触摸状态*/ + if (!this.helper.notReleaseLoad) { + /*如果是touch状态,则返回不做回弹操作*/ + if (this.helper.isPressDown || this.currentOffsetLoad >= 0) { + //超过显示刷新结果的时间之后改变状态 + this.setDefAfterLoadEnd(this.config.loadResultDurationTime) + return + } + } + + /*设置刷新结果之后准备回弹*/ + if (-this.currentOffsetLoad - this.helper.footerSize > 0.5) { + /*如果回弹至header高度前就刷新成功了,则先回弹至footer高度,再回弹至0高度*/ + // 这里会直接回弹至footer高度后再回弹至0高度,所以不需要暂停动画再回弹 + this.pauseAnimLoad() + this.releaseActionByLoad(); + // (动画执行完可能存在误差(精度问题),导致return不会执行hiddenFooterAnim,最终还是不注释) + return + } + /*如果偏移量距离小于等于footer高度,直接回弹至0高度*/ + this.hiddenFooterAnim() + } + + /*设置下拉刷新默认状态*/ + private setDefStatus() { + // this.arrowRotate = 0 + this.status = PullStatus.DEF + this.changeStatus(this.currentOffset) + } + + /*设置上拉加载默认状态*/ + private setDefStatusLoad() { + // this.arrowRotate = 0 + this.status = PullStatus.DEF + this.changeStatusLoad(this.currentOffsetLoad) + } + + /*刷新完成之后改变状态,1:刷新时触摸,2:刷新完成时再触摸*/ + private setDefAfterRefreshEnd(durationTime: number) { + if (durationTime <= 0) { + durationTime = 200 + } + if (this.setDefStatusId != 0) { + // this.log("=====setDefStatusId==防止重复调用===" + this.setDefStatusId) + //防止重复调用 + return + } + this.setDefStatusId = setTimeout(() => { + this.setDefStatusId = 0 + this.setDefStatus() + /*刷新中,手动把header视图上拉至0*/ + this.pullDownListener({ + isPullDown: false || (this.helper.isPressDown && this.helper.preOffset < this.currentOffset), + isPullUp: false || (this.helper.isPressDown && this.helper.preOffset > this.currentOffsetLoad), + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 10) + }, durationTime) + // this.log("=====setDefStatusId=====" + this.setDefStatusId) + } + + /*加载完成之后改变状态,1:加载时触摸,2:加载完成时再触摸*/ + private setDefAfterLoadEnd(durationTime: number) { + if (durationTime <= 0) { + durationTime = 200 + } + if (this.setDefLoadStatusId != 0) { + // this.log("=====setDefLoadStatusId==防止重复调用===" + this.setDefLoadStatusId) + //防止重复调用 + return + } + this.setDefLoadStatusId = setTimeout(() => { + this.setDefLoadStatusId = 0 + this.setDefStatusLoad() + /*加载中,手动把footer视图下拉至0*/ + this.pullDownListener({ + isPullDown: false || (this.helper.isPressDown && this.helper.preOffset < this.currentOffset), + isPullUp: false || (this.helper.isPressDown && this.helper.preOffset > this.currentOffsetLoad), + isTouch: this.helper.isPressDown, + distance: this.currentOffset, + distanceLoad: this.currentOffsetLoad, + headerViewSize: this.helper.headerSize, + footerViewSize: this.helper.footerSize, + status: this.status + }, 10) + }, durationTime) + // this.log("=====setDefLoadStatusId=====" + this.setDefLoadStatusId) + } + + /*隐藏header回弹动画*/ + private hiddenHeaderAnim() { + /*如果不需要显示刷新成功或者失败视图*/ + let timeDuration = 0 + if ((this.config.refreshShowSuccess && this.status == PullStatus.RefreshSuccess) || + (this.config.refreshShowError && this.status == PullStatus.RefreshError)) { + timeDuration = this.config.refreshResultDurationTime + } + setTimeout(() => { + /*如果不需要释放刷新情况下,不需要考虑是否是触摸状态*/ + if (!this.helper.notReleaseRefresh) { + //显示刷新完成的结果之后,准备回弹至0时,正好触摸 + if (this.helper.isPressDown || this.currentOffset <= 0) { + this.setDefStatus() + return + } + } + + //不管动画执行完毕还是动画被触摸时暂停,都需要重置状态(隐藏header之后) + this.setDefAfterRefreshEnd(this.config.durationCloseHeader) + + + this.pauseAnim() + this.anim?.reset({ + duration: this.config.durationCloseHeader * (this.currentOffset / this.helper.headerSize), + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + }) + this.animPause = false + this.anim?.play() + }, timeDuration) + } + + /*隐藏footer回弹动画*/ + private hiddenFooterAnim() { + this.initLoadAnim() + /*如果不需要显示加载成功或者失败视图*/ + let timeDuration = 0 + if ((this.config.loadShowSuccess && this.status == PullStatus.LoadSuccess) || + (this.config.loadShowError && this.status == PullStatus.LoadError)) { + timeDuration = this.config.loadResultDurationTime + } + setTimeout(() => { + /*如果不需要释放加载情况下,不需要考虑是否是触摸状态*/ + if (!this.helper.notReleaseLoad) { + //显示刷新完成的结果之后,准备回弹至0时,正好触摸 + if (this.helper.isPressDown || this.currentOffsetLoad >= 0) { + this.setDefStatusLoad() + return + } + } + + //不管动画执行完毕还是动画被触摸时暂停,都需要重置状态(隐藏header之后) + this.setDefAfterLoadEnd(this.config.durationCloseFooter) + + + this.pauseAnimLoad() + this.animLoad?.reset({ + duration: this.config.durationCloseFooter * (-this.currentOffsetLoad / this.helper.footerSize), + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + }) + this.animLoadPause = false + this.animLoad?.play() + }, timeDuration) + } + + private changeStatus(currentOffsetY: number) { + /*如果是刷新中,刷新完成,刷新失败,下拉不改变状态*/ + if (this.status == PullStatus.Refresh || this.status == PullStatus.RefreshSuccess || + this.status == PullStatus.RefreshError) { + return + } + /*根据下拉距离改变状态*/ + if (this.config.pullRefreshOpenPageEnable && currentOffsetY >= this.getPullOpenPageHeight()) { + /*满足打开其他页面条件*/ + this.status = PullStatus.PreOpenPage + } else if (currentOffsetY >= this.getPullRefreshHeaderHeight()) { + if (this.status == PullStatus.PullDown) { + /*进入释放刷新状态*/ + /*animateTo({ duration: 150, curve: Curve.Linear }, () => { + this.arrowRotate = 180 + })*/ + } + /*满足释放刷新条件*/ + this.status = PullStatus.PreRefresh + } else if (currentOffsetY > 0) { + if (this.status == PullStatus.PreRefresh) { + /*进入下拉状态*/ + /*animateTo({ duration: 10, curve: Curve.Linear }, () => { + this.arrowRotate = 0 + })*/ + } + /*满足下拉条件*/ + this.status = PullStatus.PullDown + } else if (currentOffsetY <= 0) { + /*默认状态*/ + this.status = PullStatus.DEF + } + } + + private changeStatusLoad(currentOffsetYLoad: number) { + /*如果是加载中,加载完成,加载失败,上拉不改变状态*/ + if (this.status == PullStatus.Load || this.status == PullStatus.LoadSuccess || + this.status == PullStatus.LoadError) { + return + } + /*根据下拉距离改变状态*/ + if (this.config.pullLoadOpenPageEnable && -currentOffsetYLoad >= this.getPullLoadOpenPageHeight()) { + /*满足打开其他页面条件*/ + this.status = PullStatus.PreLoadOpenPage + } else if (-currentOffsetYLoad >= this.getPullLoadFooterHeight()) { + /*如果没有更多数据*/ + if (!this.hasMore) { + this.status = PullStatus.PullUp + return + } + if (this.status == PullStatus.PullUp) { + /*进入释放加载状态*/ + /*animateTo({ duration: 150, curve: Curve.Linear }, () => { + this.arrowRotate = 180 + })*/ + } + /*满足释放加载条件*/ + this.status = PullStatus.PreLoad + } else if (-currentOffsetYLoad > 0) { + if (this.status == PullStatus.PreLoad) { + /*进入上拉状态*/ + /*animateTo({ duration: 10, curve: Curve.Linear }, () => { + this.arrowRotate = 0 + })*/ + } + /*满足上拉条件*/ + this.status = PullStatus.PullUp + } else if (currentOffsetYLoad <= 0) { + /*默认状态*/ + this.status = PullStatus.DEF + } + } + + /*下拉刷新*/ + private pauseAnim() { + //回弹过程中暂停动画 + this.animPause = true + this.anim?.cancel() + } + + /*上拉加载*/ + private pauseAnimLoad() { + //回弹过程中暂停动画 + this.animLoadPause = true + this.initLoadAnim() + this.animLoad?.cancel() + } + + private releaseActionByRefresh() { + this.helper.totalOffset = this.currentOffset + if (this.currentOffset <= 0) { + // this.arrowRotate = 0 + this.changeStatus(this.currentOffset) + return + } + + if (this.status == PullStatus.PreRefresh) { + /*释放刷新*/ + this.status = PullStatus.Refresh + // this.startRefresh() + /*触发刷新操作*/ + this.onRefresh?.() + } else if (this.status == PullStatus.PreOpenPage) { + /*打开其他页面*/ + this.status = PullStatus.OpenPage + this.onOpenPage?.() + } + + let endFraction: number = 1; + /*如果刷新需要保持header视图*/ + if (this.config.refreshKeepHeader) { + if (this.status == PullStatus.Refresh || this.status == PullStatus.RefreshSuccess || + this.status == PullStatus.RefreshError) { + /*如果此时下拉距离大于header高度,则回弹至header高度,否则回弹到0*/ + if (this.currentOffset >= this.helper.headerSize) { + endFraction = (this.currentOffset - this.helper.headerSize) / this.currentOffset + } + } + } + + let durationTime = this.config.durationToHeader + /*如果回弹至header高度过程中刷新完成了,再次回弹时不用执行相同时间*/ + if (this.animProgress != 1) { + durationTime = durationTime * (1 - this.animProgress) + } + if (this.status == PullStatus.OpenPage) { + durationTime = this.config.durationCloseForOpenPage + } + + this.animPause = false + this.anim?.reset({ + duration: durationTime, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: endFraction + }) + this.anim?.play() + } + + /*是否处于加载相关的状态*/ + private isLoadStatus(): boolean { + if (this.status == PullStatus.PullUp || this.status == PullStatus.PreLoad || this.status == PullStatus.Load + || this.status == PullStatus.PreLoadOpenPage || this.status == PullStatus.LoadOpenPage || + this.status == PullStatus.LoadSuccess || this.status == PullStatus.LoadError) { + return true + } + return false + } + + private releaseActionByLoad() { + this.helper.totalOffsetLoad = this.currentOffsetLoad + if (this.currentOffsetLoad >= 0) { + // this.arrowRotate = 0 + this.changeStatusLoad(this.currentOffsetLoad) + return + } + + if (this.status == PullStatus.PreLoad) { + /*释放加载*/ + this.status = PullStatus.Load + /*触发加载操作*/ + this.onLoad?.() + } else if (this.status == PullStatus.PreLoadOpenPage) { + /*打开其他页面*/ + this.status = PullStatus.LoadOpenPage + this.onLoadOpenPage?.() + } + + let endFraction: number = 1; + /*如果刷新需要保持header视图*/ + if (this.config.loadKeepFooter) { + if (this.status == PullStatus.Load || this.status == PullStatus.LoadSuccess || + this.status == PullStatus.LoadError) { + /*如果此时下拉距离大于header高度,则回弹至header高度,否则回弹到0*/ + if (-this.currentOffsetLoad >= this.helper.footerSize) { + endFraction = (-this.currentOffsetLoad - this.helper.footerSize) / -this.currentOffsetLoad + } + } + } + + let durationTime = this.config.durationToFooter + /*如果回弹至footer高度过程中刷新完成了,再次回弹时不用执行相同时间*/ + if (this.animProgressLoad != 1) { + durationTime = durationTime * (1 - this.animProgressLoad) + } + if (this.status == PullStatus.LoadOpenPage) { + durationTime = this.config.durationCloseLoadForOpenPage + } + this.animLoadPause = false + this.initLoadAnim() + this.animLoad?.reset({ + duration: durationTime, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: endFraction + }) + this.animLoad?.play() + } +} + diff --git a/RefreshLib/src/main/ets/RefreshLayoutConfig.ets b/RefreshLib/src/main/ets/RefreshLayoutConfig.ets new file mode 100644 index 0000000..b5201f5 --- /dev/null +++ b/RefreshLib/src/main/ets/RefreshLayoutConfig.ets @@ -0,0 +1,68 @@ +export class RefreshLayoutConfig { + /*是否是垂直列表*/ + public isVertical: boolean = true + /*横向模式0:正常横向布局,1:header和footer逆时针旋转90度,2:header和footer顺时针旋转90度*/ + public horizontalMode: number = 0 + /*是否打开刷新*/ + public pullRefreshEnable: boolean = true + /*是否打开加载更多*/ + public pullLoadEnable: boolean = true + /*******************************刷新配置*******************************************/ + /*true:释放刷新,false下拉到一定距离刷新*/ + public releaseRefresh: boolean = true + /*下拉最大距离*/ + public pullMaxDistance: number = 500 + /*下拉阻力*/ + public pullRefreshResistance: number = 0.5 + /*下拉距离超过多少时达到刷新条件*/ + public pullHeaderHeightRefresh: number = 0 + /*下拉打开其他页面开关*/ + public pullRefreshOpenPageEnable: boolean = false + /*下拉距离超过多少时达到打开其他页面条件*/ + public pullHeaderHeightOpenPage: number = 0 + /*释放刷新时,回弹至headerView高度的时间*/ + public durationToHeader: number = 250 + /*头布局刷新结束时回弹的时间*/ + public durationCloseHeader: number = 200 + /*需要打开其他页面时,头布局的回弹时间*/ + public durationCloseForOpenPage: number = 180 + /*刷新时是否显示头view*/ + public refreshKeepHeader: boolean = true + /*是否显示刷新成功状态的view*/ + public refreshShowSuccess: boolean = true; + /*是否显示刷新失败状态的view*/ + public refreshShowError: boolean = true; + /*刷新结果view显示持续时间*/ + public refreshResultDurationTime: number = 600 + + + + /*******************************加载更多配置*******************************************/ + + /*true:释放加载,false下拉到一定距离加载*/ + public releaseLoad: boolean = true + /*上拉最大距离*/ + public pullLoadMaxDistance: number = 500 + /*上拉阻力*/ + public pullLoadResistance: number = 0.5 + /*上拉距离超过多少时达到刷新条件*/ + public pullFooterHeightLoad: number = 0 + /*上拉打开其他页面开关*/ + public pullLoadOpenPageEnable: boolean = false + /*上拉距离超过多少时达到打开其他页面条件*/ + public pullFooterHeightOpenPage: number = 0 + /*释放刷新时,回弹至footerView高度的时间*/ + public durationToFooter: number = 250 + /*footer布局刷新结束时回弹的时间*/ + public durationCloseFooter: number = 200 + /*需要打开其他页面时,头布局的回弹时间*/ + public durationCloseLoadForOpenPage: number = 180 + /*刷新时是否显示footerView*/ + public loadKeepFooter: boolean = true + /*是否显示刷新成功状态的view*/ + public loadShowSuccess: boolean = true; + /*是否显示刷新失败状态的view*/ + public loadShowError: boolean = true; + /*刷新结果view显示持续时间*/ + public loadResultDurationTime: number = 600 +} \ No newline at end of file diff --git a/RefreshLib/src/main/ets/RefreshLayoutHelper.ets b/RefreshLib/src/main/ets/RefreshLayoutHelper.ets new file mode 100644 index 0000000..a98d5e3 --- /dev/null +++ b/RefreshLib/src/main/ets/RefreshLayoutHelper.ets @@ -0,0 +1,67 @@ +import { AnimatorOptions } from '@kit.ArkUI'; + +@Builder +export function _headerView() { + Text("headerView:()=>{your @Builder View}") +} + +@Builder +export function _ContentView() { + Text("contentView:()=>{your @Builder View}") +} + +@Builder +export function _loadMoreView() { + Text("loadView:()=>{your @Builder View}") +} + +@Builder +export function _noMoreView() { + Text("noMoreView:()=>{your @Builder View}") +} + +export class RefreshLayoutHelper { + private log(str: string, tag: string = "RefreshLayout") { + console.debug(tag + "==" + str) + } + + /*记录列表滑动到底部的偏移量*/ + public scrollerOffset: number = 0; + /*下拉刷新*/ + public preOffset: number = 0; + public totalOffset: number = 0; + /*上拉加载*/ + // public preOffsetLoad: number = 0; + public totalOffsetLoad: number = 0; + public headerSize: number = 0; + public footerSize: number = 0; + public isPressDown: boolean = false + public options: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Down | PanDirection.Up }) + /*不是释放刷新*/ + public notReleaseRefresh: boolean = false + /*不是释放加载*/ + public notReleaseLoad: boolean = false + public animOptions: AnimatorOptions = { + duration: 250, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + } + public animLoadOptions: AnimatorOptions = { + duration: 250, + easing: "fast-out-linear-in", + delay: 0, + fill: "forwards", + direction: "normal", + iterations: 1, + begin: 0, + end: 1 + } + /*webview滑动偏移量*/ + public webViewXOffset: number = 0 + public webViewYOffset: number = 0 +} \ No newline at end of file diff --git a/RefreshLib/src/main/module.json5 b/RefreshLib/src/main/module.json5 new file mode 100644 index 0000000..17b498c --- /dev/null +++ b/RefreshLib/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "refreshlib", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] + } +} diff --git a/RefreshLib/src/main/resources/base/element/string.json b/RefreshLib/src/main/resources/base/element/string.json new file mode 100644 index 0000000..eb9ae82 --- /dev/null +++ b/RefreshLib/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "zr_pull_down_to_refresh", + "value": "下拉刷新" + }, + { + "name": "zr_release_to_refresh", + "value": "释放立即刷新" + }, + { + "name": "zr_release_to_open_age", + "value": "释放打开其他页面" + }, + { + "name": "zr_refreshing", + "value": "正在刷新..." + }, + { + "name": "zr_refresh_success", + "value": "刷新完成" + }, + { + "name": "zr_refresh_error", + "value": "刷新失败" + }, + { + "name": "zr_last_update", + "value": "上次更新:" + }, + { + "name": "zr_seconds_ago", + "value": " 秒之前" + }, + { + "name": "zr_minutes_ago", + "value": " 分钟之前" + }, + { + "name": "zr_hours_ago", + "value": " 小时之前" + }, + { + "name": "zr_days_ago", + "value": " 天之前" + }, + { + "name": "zr_pull_up_to_load", + "value": "上拉加载" + }, + { + "name": "zr_release_to_load", + "value": "释放立即加载" + }, + { + "name": "zr_loading", + "value": "正在加载..." + }, + { + "name": "zr_load_success", + "value": "加载完成" + }, + { + "name": "zr_load_error", + "value": "加载失败" + }, + { + "name": "zr_load_no_more", + "value": "暂无更多" + } + ] +} diff --git a/RefreshLib/src/main/resources/base/element/string_h.json b/RefreshLib/src/main/resources/base/element/string_h.json new file mode 100644 index 0000000..58f1dd5 --- /dev/null +++ b/RefreshLib/src/main/resources/base/element/string_h.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "h_zr_pull_down_to_refresh", + "value": "右\n拉\n刷\n新" + }, + { + "name": "h_zr_release_to_refresh", + "value": "释\n放\n立\n即\n刷\n新" + }, + { + "name": "h_zr_release_to_open_age", + "value": "释\n放\n打\n开\n其\n他\n页\n面" + }, + { + "name": "h_zr_refreshing", + "value": "正\n在\n刷\n新\n..." + }, + { + "name": "h_zr_refresh_success", + "value": "刷\n新\n完\n成" + }, + { + "name": "h_zr_refresh_error", + "value": "刷\n新\n失\n败" + }, + { + "name": "h_zr_last_update", + "value": "上\n次\n更\n新" + }, + { + "name": "h_zr_seconds_ago", + "value": "秒\n之\n前" + }, + { + "name": "h_zr_minutes_ago", + "value": "分\n钟\n之\n前" + }, + { + "name": "h_zr_hours_ago", + "value": "小\n时\n之\n前" + }, + { + "name": "h_zr_days_ago", + "value": "天\n之\n前" + }, + { + "name": "h_zr_pull_up_to_load", + "value": "左\n拉\n加\n载" + }, + { + "name": "h_zr_release_to_load", + "value": "释\n放\n立\n即\n加\n载" + }, + { + "name": "h_zr_loading", + "value": "正\n在\n加\n载\n..." + }, + { + "name": "h_zr_load_success", + "value": "加\n载\n完\n成" + }, + { + "name": "h_zr_load_error", + "value": "加\n载\n失\n败" + }, + { + "name": "h_zr_load_no_more", + "value": "暂\n无\n更\n多" + } + ] +} diff --git a/RefreshLib/src/main/resources/base/media/icon_refresh_arrow.svg b/RefreshLib/src/main/resources/base/media/icon_refresh_arrow.svg new file mode 100644 index 0000000..372b591 --- /dev/null +++ b/RefreshLib/src/main/resources/base/media/icon_refresh_arrow.svg @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/RefreshLib/src/main/resources/en_US/element/string.json b/RefreshLib/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000..f51a9c8 --- /dev/null +++ b/RefreshLib/src/main/resources/en_US/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/RefreshLib/src/main/resources/zh_CN/element/string.json b/RefreshLib/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000..f51a9c8 --- /dev/null +++ b/RefreshLib/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/RefreshLib/src/ohosTest/ets/test/Ability.test.ets b/RefreshLib/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000..85c78f6 --- /dev/null +++ b/RefreshLib/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,35 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/RefreshLib/src/ohosTest/ets/test/List.test.ets b/RefreshLib/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..794c7dc --- /dev/null +++ b/RefreshLib/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/RefreshLib/src/ohosTest/module.json5 b/RefreshLib/src/ohosTest/module.json5 new file mode 100644 index 0000000..ed5081e --- /dev/null +++ b/RefreshLib/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "refreshlib", + "type": "feature", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/RefreshLib/src/test/List.test.ets b/RefreshLib/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/RefreshLib/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/RefreshLib/src/test/LocalUnit.test.ets b/RefreshLib/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..165fc16 --- /dev/null +++ b/RefreshLib/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 index 6287d99..48391dc 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -77,6 +77,14 @@ "name": "scene_single_video", "srcPath": "./scene_single_video" }, + { + "name": "patient", + "srcPath": "./features/patient", + }, + { + "name": "refreshlib", + "srcPath": "./RefreshLib" + }, { "name": "corekit", "srcPath": "./corekit" diff --git a/commons/basic/Index.ets b/commons/basic/Index.ets index bb41880..59bdaab 100644 --- a/commons/basic/Index.ets +++ b/commons/basic/Index.ets @@ -26,7 +26,7 @@ export { DataWebModel,DataWebModels } from './src/main/ets/models/DataWebModel' export { PromptActionClass } from './src/main/ets/components/PromptActionClass' -export { RequestDefaultModel,ExpertData } from './src/main/ets/models/RequestDefaultModel' +export { RequestDefaultModel,ExpertData} from './src/main/ets/models/RequestDefaultModel' export { HdList, HdListController } from './src/main/ets/components/HdList' @@ -56,8 +56,23 @@ export { huanzheDb } from './src/main/ets/utils/HuanzhelasDbHelper' export { HdSearchNav } from './src/main/ets/components/HdSearchNav' +export { HdHomeNav } from './src/main/ets/components/HdHomeNav' + export { DefaultHintProWindows } from './src/main/ets/Views/DefaultHintProWindows' +export { TimestampUtil } from './src/main/ets/utils/TimestampUtil' + +export { SignPopWindow } from './src/main/ets/Views/SignPopWindow' + +// 患者数据库管理相关导出 +export { + patientDbManager, + PatientDatabaseManager, + PatientEntity, + PatientDao, + PatientData +} from './src/main/ets/utils/PatientsEntity' + export { PermissionsUtils } from './src/main/ets/utils/PermissionsUtils' export { calculateExactAge } from './src/main/ets/utils/DateUtils' diff --git a/commons/basic/src/main/ets/Views/SignPopWindow.ets b/commons/basic/src/main/ets/Views/SignPopWindow.ets new file mode 100644 index 0000000..b88518d --- /dev/null +++ b/commons/basic/src/main/ets/Views/SignPopWindow.ets @@ -0,0 +1,75 @@ +import { router } from '@kit.ArkUI' +import { BasicConstant } from '../../../../Index'; + +@CustomDialog +export struct SignPopWindow { + controller: CustomDialogController; + + @Prop signDay:string = '今天是我们相识的第1天'; + @Prop signWeek:string = '1'; + @Prop signMouth:string = '1'; + + @Prop signNews:string = '卫健委:鼓励医务人员做兼职!可以做哪些兼职,有哪些法律分险?'; + @Prop signHtml:string = ''; + + build() { + Column(){ + Image($r('app.media.sign_popwindow_close')) + .margin({right:10}).width(20).height(70) + .onClick(()=>{ + this.controller.close() + }) + Stack(){ + Column(){ + Text('签到成功') + .textAlign(TextAlign.Center) + .fontSize(21).fontColor(Color.White).backgroundColor('rgb(255, 210,3)').height(50).width('100%') + .borderRadius({topLeft:8,topRight:8}) + Text(this.signDay) + .textAlign(TextAlign.Center) + .fontSize(16).fontColor(Color.Black).height(18).width('100%').margin({top:15}) + Row(){ + Text('本周共签到') + .fontSize(14).fontColor('#333333') + Text(this.signWeek) + .fontSize(20).fontColor(Color.Red) + Text('次') + .fontSize(14).fontColor('#333333') + }.width('100%').justifyContent(FlexAlign.Center).height(20).margin({top:15,bottom:3}).alignItems(VerticalAlign.Bottom) + Row(){ + Text('已连续签到') + .fontSize(14).fontColor('#333333') + Text(this.signMouth) + .fontSize(20).fontColor(Color.Red) + Text('天') + .fontSize(14).fontColor('#333333') + }.width('100%').justifyContent(FlexAlign.Center).height(20).alignItems(VerticalAlign.Bottom) + Text('连续签到获得更多积分') + .textAlign(TextAlign.Center) + .fontSize(14).fontColor(Color.Red).margin({top:15,bottom:10}) + }.width('90%').backgroundColor(Color.White).borderRadius(8).margin({top:30}) + Row(){ + Image($r('app.media.sign_back_leftright_icon')).width(18).height(18) + Blank() + .width('50%').height(18) + Image($r('app.media.sign_back_leftright_icon')).width(18).height(18) + }.margin({top:20}) + Column(){ + Image($r('app.media.sign_back_news_icon')) + .width(36).height(36) + Text(this.signNews) + .width('100%') + .fontSize(16).fontColor(Color.Black) + .backgroundColor(Color.White).borderRadius(8).padding({left:10,top:10,right:10,bottom:10}).margin({top:5}) + }.width('90%').margin({top:215,bottom:15}) + .onClick(()=>{ + this.controller.close(); + router.pushUrl({ + url: 'pages/WebView/WebPage', + params: {'title':this.signNews,'url':BasicConstant.urlHtml+this.signHtml} + }) + }) + }.width('100%').backgroundColor('rgb(255, 161, 24)').borderRadius(8).alignContent(Alignment.Top) + }.width('100%').alignItems(HorizontalAlign.End) + } +} diff --git a/commons/basic/src/main/ets/components/HdHomeNav.ets b/commons/basic/src/main/ets/components/HdHomeNav.ets new file mode 100644 index 0000000..c9021be --- /dev/null +++ b/commons/basic/src/main/ets/components/HdHomeNav.ets @@ -0,0 +1,59 @@ +import { ChangeUtil } from "../../../../Index"; + +@Component +export struct HdHomeNav { + @StorageProp('topHeight') + topHeight: number = 0 + @Prop + bgColor: ResourceStr = $r('app.color.top_bg') + @Prop + hasBorder: boolean = false + @Prop + leftIcon: ResourceStr = $r('app.media.home_no_qiandao_icon') + @Prop + isFocus:boolean = false; + @Prop + placeholder:string = '' + + @State textInputContent:string = '' + @Prop alpha: number = 0; // 接收透明度值 + @Prop backColor: string = '' + private leftItemAction: () => void = () => {}; + // 添加左侧点击函数 + private searchItemAction:()=> void = () => {}; + + build() { + Row() { + Row() { + Image(this.leftIcon) + .size({ width: 24, height: 24 }) + .fillColor($r('app.color.black')) + }.size({ width: 40, height: 50 }) + .onClick(()=>this.leftItemAction()) + + Row(){ + Image($r('app.media.selected_hospital_ws')) + .width(20).height(20) + .margin({left:10}) + Text(this.placeholder) + .defaultFocus(this.isFocus) + .fontSize(15) + .fontColor(Color.Gray) + .backgroundColor(Color.White) + .height('100%').width('calc(100% - 40vp)') + .padding({ left: 0 }) + .margin({left:5}) + } + .backgroundColor(Color.White) + .borderWidth(0.5).borderRadius(5).borderColor($r('app.color.common_main_color')).justifyContent(FlexAlign.Start) + .width('calc(100% - 40vp)').height(40) + .onClick(()=>{ + this.searchItemAction(); + }) + } + .padding({ left: 16, right: 16, top: this.topHeight }) + .height(56 + this.topHeight) + .width('100%') + .backgroundColor(ChangeUtil.hexToRgba(this.backColor,this.alpha)) + } +} \ No newline at end of file diff --git a/commons/basic/src/main/ets/components/HdNav.ets b/commons/basic/src/main/ets/components/HdNav.ets index fc13b31..0823ec1 100644 --- a/commons/basic/src/main/ets/components/HdNav.ets +++ b/commons/basic/src/main/ets/components/HdNav.ets @@ -29,13 +29,20 @@ export struct HdNav { showRightText: boolean = false @Prop rightText: string = '' + @Prop + rightBackColor:ResourceColor = this.bgColor + @Prop + rightTextColor:ResourceColor = this.textColor @BuilderParam titleBuilder: () => void = defaultBuilder @BuilderParam menuBuilder: () => void = defaultBuilder - + @Prop + isLeftAction: boolean = false // 添加右侧点击函数 private rightItemAction:()=> void = () => {}; + // 添加左侧点击函数 + private leftItemAction:()=> void = () => {}; build() { Row() { @@ -44,7 +51,7 @@ export struct HdNav { { Image(this.leftIcon) .size({ width: 24, height: 24 }) - .onClick(() => router.back()) + .onClick(() => this.isLeftAction? this.leftItemAction():router.back()) .fillColor($r('app.color.black')) } .size({ width: 50, height: 50 }) @@ -84,11 +91,16 @@ export struct HdNav { } else if (this.showRightText) { Text(this.rightText) - .fontSize(16) - .fontColor(this.textColor) + .maxFontSize(16) + .minFontSize(6) + .maxLines(1) + .fontColor(this.rightTextColor) .onClick(()=>this.rightItemAction()) .width(50) .textAlign(TextAlign.Center) + .borderRadius(5) + .backgroundColor(this.rightBackColor) + .height(35) // .margin({right:10}) } else { Blank() diff --git a/commons/basic/src/main/ets/components/HdSearchNav.ets b/commons/basic/src/main/ets/components/HdSearchNav.ets index 5197ff0..f2b3808 100644 --- a/commons/basic/src/main/ets/components/HdSearchNav.ets +++ b/commons/basic/src/main/ets/components/HdSearchNav.ets @@ -50,7 +50,6 @@ export struct HdSearchNav { .fontSize(15) .backgroundColor(this.bgColor) .height('100%').width('calc(100% - 40vp)') - .padding({ left: 0 }) .margin({left:5}) .onChange((value:string)=>{ this.textInputContent = value; diff --git a/commons/basic/src/main/ets/constants/BasicConstant.ets b/commons/basic/src/main/ets/constants/BasicConstant.ets index becfc16..4a221f9 100644 --- a/commons/basic/src/main/ets/constants/BasicConstant.ets +++ b/commons/basic/src/main/ets/constants/BasicConstant.ets @@ -24,6 +24,22 @@ export class BasicConstant { // static readonly polvId = "21";//保利威视学员id + static readonly addBonusPoints = BasicConstant.urlExpertApp+'addBonusPoints' + static readonly indexV2 = BasicConstant.urlExpertAPI+'indexV2';//首页轮播 + static readonly applyList = BasicConstant.urlExpert+'applyList' + static readonly groupList = BasicConstant.urlExpertApp+'groupList' + static readonly isMaiLanExpert = BasicConstant.urlExpertAPI+'isMaiLanExpert' + static readonly patientListByGroup = BasicConstant.urlExpertApp+'patientListByGroup' + static readonly deleteGroup = BasicConstant.urlExpertApp+'deleteGroup' + static readonly addGroup = BasicConstant.urlExpertApp+'addGroup' + static readonly updateGroup = BasicConstant.urlExpertApp+'updateGroup' + static readonly relationRecordLately = BasicConstant.urlExpertAPI+'relationRecordLately' + static readonly updateNicknameNote = BasicConstant.urlExpertAPI+'updateNicknameNote' + static readonly applyListOperate = BasicConstant.urlExpert+'applyListOperate' + static readonly patientListNoInThisGroup = BasicConstant.urlExpertAPI+'patientListNoInThisGroup' + static readonly patientCard = BasicConstant.urlExpertAPI+'patientCard' + static readonly toAddNickname = BasicConstant.urlExpert+'toAddNickname' + static readonly patientDetail = BasicConstant.urlExpert+'patientDetail' static readonly getStartpage=BasicConstant.urlExpertApp + "startpage"; static readonly meetingListV2=BasicConstant.urlExpertAPI + "meetingListV2"; static readonly meetingV2Video=BasicConstant.urlExpertAPI + "meetingV2Video"; @@ -53,7 +69,6 @@ export class BasicConstant { static readonly listOutPatient = BasicConstant.urlExpertApp+'listOutPatient'//出诊 static readonly urlOutpatientnew = BasicConstant.wxUrl+"wxPatient/index.htm#/outPatient?link=share&expertUuid=";//出诊 static readonly getNewWa="https://wx.igandan.com/shop_notify/";//肝胆商城妞娃购买链接 - static readonly patientDetail= BasicConstant.urlExpert + "patientDetail";// 患者详情 static readonly newConsultList = BasicConstant.urlExpertAPI + "newConsultList";// 新的公益咨询列表 static readonly consultListHis = BasicConstant.urlExpertAPI +"consultListHis";// 公益咨询历史列表 static readonly consultDetail = BasicConstant.urlExpertAPI + "consultDetail";// 公益咨询详情 diff --git a/commons/basic/src/main/ets/models/RequestDefaultModel.ets b/commons/basic/src/main/ets/models/RequestDefaultModel.ets index 55be9bb..32ebc35 100644 --- a/commons/basic/src/main/ets/models/RequestDefaultModel.ets +++ b/commons/basic/src/main/ets/models/RequestDefaultModel.ets @@ -1,4 +1,3 @@ - export interface RequestDefaultModel{ code:string; data:DefaulyData[]; diff --git a/commons/basic/src/main/ets/utils/PatientsEntity.ets b/commons/basic/src/main/ets/utils/PatientsEntity.ets new file mode 100644 index 0000000..655516d --- /dev/null +++ b/commons/basic/src/main/ets/utils/PatientsEntity.ets @@ -0,0 +1,862 @@ +import { Entity, Id, Columns, ColumnType } from '@ohos/dataorm'; +import { authStore } from './auth'; +import { hdHttp, HdResponse } from '../utils/request'; +import { BasicConstant } from '../constants/BasicConstant'; +import { BusinessError } from '@kit.BasicServicesKit'; +import relationalStore from '@ohos.data.relationalStore'; +import common from '@ohos.app.ability.common'; + +// 患者数据接口 +export interface PatientData { + uuid: string; + nickname: string; + mobile: string; + realName: string; + nation: string | null; + sex: number; + type: number; + photo: string; + expertUuid: string; // 关联的专家UUID +} + +// 服务器响应接口 +interface updateExtraData { + expertUuid: string +} + +// 患者实体类 +@Entity('patients') +export class PatientEntity { + @Id() + @Columns({ columnName: 'id', types: ColumnType.num }) + id: number = 0; + + @Columns({ columnName: 'uuid', types: ColumnType.str }) + uuid: string = ''; + + @Columns({ columnName: 'nickname', types: ColumnType.str }) + nickname: string = ''; + + @Columns({ columnName: 'mobile', types: ColumnType.str }) + mobile: string = ''; + + @Columns({ columnName: 'realName', types: ColumnType.str }) + realName: string = ''; + + @Columns({ columnName: 'nation', types: ColumnType.str }) + nation: string = ''; + + @Columns({ columnName: 'sex', types: ColumnType.num }) + sex: number = 0; + + @Columns({ columnName: 'type', types: ColumnType.num }) + type: number = 0; + + @Columns({ columnName: 'photo', types: ColumnType.str }) + photo: string = ''; + + @Columns({ columnName: 'expertUuid', types: ColumnType.str }) + expertUuid: string = ''; + + @Columns({ columnName: 'createTime', types: ColumnType.str }) + createTime: string = ''; + + @Columns({ columnName: 'updateTime', types: ColumnType.str }) + updateTime: string = ''; + + constructor( + uuid: string = '', + nickname: string = '', + mobile: string = '', + realName: string = '', + nation: string = '', + sex: number = 0, + type: number = 0, + photo: string = '', + expertUuid: string = '' + ) { + this.uuid = uuid; + this.nickname = nickname; + this.mobile = mobile; + this.realName = realName; + this.nation = nation; + this.sex = sex; + this.type = type; + this.photo = photo; + this.expertUuid = expertUuid; + this.createTime = new Date().toISOString(); + this.updateTime = new Date().toISOString(); + } +} + +// 患者DAO类 - 使用原生 relationalStore +export class PatientDao { + private rdbStore: relationalStore.RdbStore | null = null; + private context: common.Context; + private isInitialized: boolean = false; + + constructor(context: common.Context) { + this.context = context; + } + + // 初始化数据库 + async initDatabase(): Promise { + try { + console.info('开始初始化患者数据库...'); + + const storeConfig: relationalStore.StoreConfig = { + name: 'patient_database', + securityLevel: relationalStore.SecurityLevel.S1 + }; + + this.rdbStore = await relationalStore.getRdbStore(this.context, storeConfig); + console.info('数据库连接创建成功'); + + // 创建表 + await this.createTable(); + this.isInitialized = true; + console.info('患者数据库初始化完成'); + } catch (error) { + console.error('初始化患者数据库失败:', error); + this.isInitialized = false; + if (error instanceof Error) { + throw error; + } else { + throw new Error('初始化患者数据库失败'); + } + } + } + + // 创建患者表 + private async createTable(): Promise { + if (!this.rdbStore) { + throw new Error('数据库未初始化'); + } + + try { + const createTableSql = ` + CREATE TABLE IF NOT EXISTS patients ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT NOT NULL, + nickname TEXT, + mobile TEXT, + realName TEXT, + nation TEXT, + sex INTEGER DEFAULT 0, + type INTEGER DEFAULT 0, + photo TEXT, + expertUuid TEXT NOT NULL, + createTime TEXT, + updateTime TEXT + ) + `; + + await this.rdbStore.executeSql(createTableSql); + console.info('患者表创建成功'); + + // 验证表是否存在 + await this.verifyTableExists(); + } catch (error) { + console.error('创建患者表失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('创建患者表失败'); + } + } + } + + // 验证表是否存在 + private async verifyTableExists(): Promise { + if (!this.rdbStore) { + throw new Error('数据库未初始化'); + } + + try { + const checkTableSql = "SELECT name FROM sqlite_master WHERE type='table' AND name='patients'"; + const resultSet = await this.rdbStore.querySql(checkTableSql, []); + + if (resultSet.rowCount === 0) { + throw new Error('患者表创建失败'); + } + + resultSet.close(); + console.info('患者表验证成功'); + } catch (error) { + console.error('验证患者表失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('验证患者表失败'); + } + } + } + + // 检查数据库状态 + private checkDatabaseState(): void { + if (!this.rdbStore) { + throw new Error('数据库未初始化'); + } + if (!this.isInitialized) { + throw new Error('数据库初始化未完成'); + } + } + + // 根据专家UUID查询所有患者 + async getPatientsByExpertUuid(expertUuid: string): Promise { + try { + this.checkDatabaseState(); + console.info(`开始查询专家 ${expertUuid} 的患者数据`); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = 'SELECT * FROM patients WHERE expertUuid = ? ORDER BY createTime DESC'; + const resultSet = await this.rdbStore.querySql(sql, [expertUuid]); + + const patients: PatientEntity[] = []; + if (resultSet.rowCount > 0) { + resultSet.goToFirstRow(); + do { + try { + const patient = this.parseResultSet(resultSet); + patients.push(patient); + } catch (parseError) { + console.error('解析患者数据失败:', parseError); + } + } while (resultSet.goToNextRow()); + } + resultSet.close(); + + console.info(`成功查询到 ${patients.length} 个患者`); + return patients; + } catch (error) { + console.error('查询患者数据失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('查询患者数据失败'); + } + } + } + + // 根据患者UUID查询单个患者 + async getPatientByUuid(uuid: string): Promise { + try { + this.checkDatabaseState(); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = 'SELECT * FROM patients WHERE uuid = ?'; + const resultSet = await this.rdbStore.querySql(sql, [uuid]); + + if (resultSet.rowCount > 0) { + resultSet.goToFirstRow(); + const patient = this.parseResultSet(resultSet); + resultSet.close(); + return patient; + } + resultSet.close(); + return null; + } catch (error) { + console.error('根据UUID查询患者失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('根据UUID查询患者失败'); + } + } + } + + // 根据手机号查询患者 + async getPatientByMobile(mobile: string, expertUuid: string): Promise { + try { + this.checkDatabaseState(); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = 'SELECT * FROM patients WHERE mobile = ? AND expertUuid = ?'; + const resultSet = await this.rdbStore.querySql(sql, [mobile, expertUuid]); + + if (resultSet.rowCount > 0) { + resultSet.goToFirstRow(); + const patient = this.parseResultSet(resultSet); + resultSet.close(); + return patient; + } + resultSet.close(); + return null; + } catch (error) { + console.error('根据手机号查询患者失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('根据手机号查询患者失败'); + } + } + } + + // 删除指定专家的所有患者数据 + async deletePatientsByExpertUuid(expertUuid: string): Promise { + try { + this.checkDatabaseState(); + console.info(`开始删除专家 ${expertUuid} 的所有患者数据`); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = 'DELETE FROM patients WHERE expertUuid = ?'; + await this.rdbStore.executeSql(sql, [expertUuid]); + console.info('删除专家患者数据成功'); + } catch (error) { + console.error('删除专家患者数据失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('删除专家患者数据失败'); + } + } + } + + // 删除单个患者 + async deletePatientByUuid(uuid: string): Promise { + try { + this.checkDatabaseState(); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = 'DELETE FROM patients WHERE uuid = ?'; + await this.rdbStore.executeSql(sql, [uuid]); + } catch (error) { + console.error('删除患者失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('删除患者失败'); + } + } + } + + // 更新患者信息 + async updatePatient(patient: PatientEntity): Promise { + try { + this.checkDatabaseState(); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + patient.updateTime = new Date().toISOString(); + const sql = ` + UPDATE patients + SET nickname = ?, mobile = ?, realName = ?, nation = ?, + sex = ?, type = ?, photo = ?, updateTime = ? + WHERE uuid = ? + `; + + await this.rdbStore.executeSql(sql, [ + patient.nickname, + patient.mobile, + patient.realName, + patient.nation, + patient.sex, + patient.type, + patient.photo, + patient.updateTime, + patient.uuid + ]); + } catch (error) { + console.error('更新患者信息失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('更新患者信息失败'); + } + } + } + + // 插入单个患者 + async insertPatient(patient: PatientEntity): Promise { + try { + this.checkDatabaseState(); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + const sql = ` + INSERT INTO patients (uuid, nickname, mobile, realName, nation, sex, type, photo, expertUuid, createTime, updateTime) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; + + await this.rdbStore.executeSql(sql, [ + patient.uuid, + patient.nickname, + patient.mobile, + patient.realName, + patient.nation, + patient.sex, + patient.type, + patient.photo, + patient.expertUuid, + patient.createTime, + patient.updateTime + ]); + } catch (error) { + console.error('插入患者数据失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('插入患者数据失败'); + } + } + } + + // 批量插入患者数据 + async insertPatients(patients: PatientEntity[]): Promise { + try { + this.checkDatabaseState(); + console.info(`开始批量插入 ${patients.length} 个患者数据`); + + if (!this.rdbStore) { + throw new Error('数据库连接为空'); + } + + await this.rdbStore.beginTransaction(); + try { + for (const patient of patients) { + await this.insertPatient(patient); + } + await this.rdbStore.commit(); + console.info('批量插入患者数据成功'); + } catch (error) { + await this.rdbStore.rollBack(); + console.error('批量插入患者数据失败,已回滚:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('批量插入患者数据失败'); + } + } + } catch (error) { + console.error('批量插入患者数据失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('批量插入患者数据失败'); + } + } + } + + // 解析查询结果 + private parseResultSet(resultSet: relationalStore.ResultSet): PatientEntity { + try { + const uuidIndex = resultSet.getColumnIndex('uuid'); + const nicknameIndex = resultSet.getColumnIndex('nickname'); + const mobileIndex = resultSet.getColumnIndex('mobile'); + const realNameIndex = resultSet.getColumnIndex('realName'); + const nationIndex = resultSet.getColumnIndex('nation'); + const sexIndex = resultSet.getColumnIndex('sex'); + const typeIndex = resultSet.getColumnIndex('type'); + const photoIndex = resultSet.getColumnIndex('photo'); + const expertUuidIndex = resultSet.getColumnIndex('expertUuid'); + + return new PatientEntity( + uuidIndex >= 0 ? (resultSet.getString(uuidIndex) ?? '') : '', + nicknameIndex >= 0 ? (resultSet.getString(nicknameIndex) ?? '') : '', + mobileIndex >= 0 ? (resultSet.getString(mobileIndex) ?? '') : '', + realNameIndex >= 0 ? (resultSet.getString(realNameIndex) ?? '') : '', + nationIndex >= 0 ? (resultSet.getString(nationIndex) ?? '') : '', + sexIndex >= 0 ? (resultSet.getDouble(sexIndex) ?? 0) : 0, + typeIndex >= 0 ? (resultSet.getDouble(typeIndex) ?? 0) : 0, + photoIndex >= 0 ? (resultSet.getString(photoIndex) ?? '') : '', + expertUuidIndex >= 0 ? (resultSet.getString(expertUuidIndex) ?? '') : '' + ); + } catch (error) { + console.error('解析查询结果失败:', error); + if (error instanceof Error) { + throw error; + } else { + throw new Error('解析查询结果失败'); + } + } + } + + // 关闭数据库连接 + async closeDatabase(): Promise { + try { + if (this.rdbStore) { + await this.rdbStore.close(); + this.rdbStore = null; + this.isInitialized = false; + console.info('数据库连接已关闭'); + } + } catch (error) { + console.error('关闭数据库连接失败:', error); + } + } + + // 检查数据库是否已初始化 + isDatabaseInitialized(): boolean { + return this.isInitialized && this.rdbStore !== null; + } + + // 开始事务 + async beginTransaction(): Promise { + this.checkDatabaseState(); + if (this.rdbStore) { + await this.rdbStore.beginTransaction(); + } + } + + // 提交事务 + async commitTransaction(): Promise { + this.checkDatabaseState(); + if (this.rdbStore) { + await this.rdbStore.commit(); + } + } + + // 回滚事务 + async rollbackTransaction(): Promise { + this.checkDatabaseState(); + if (this.rdbStore) { + await this.rdbStore.rollBack(); + } + } +} + +// 数据库管理类 +export class PatientDatabaseManager { + private static instance: PatientDatabaseManager; + private patientDao: PatientDao | null = null; + private context: common.Context | null = null; + private isInitialized: boolean = false; + + private constructor() {} + + public static getInstance(): PatientDatabaseManager { + if (!PatientDatabaseManager.instance) { + PatientDatabaseManager.instance = new PatientDatabaseManager(); + } + return PatientDatabaseManager.instance; + } + + // 初始化数据库 + async initDatabase(context: common.Context): Promise { + try { + console.info('开始初始化患者数据库管理器...'); + this.context = context; + this.patientDao = new PatientDao(context); + await this.patientDao.initDatabase(); + this.isInitialized = true; + console.info('患者数据库管理器初始化成功'); + } catch (error) { + console.error('初始化患者数据库管理器失败:', error); + this.isInitialized = false; + if (error instanceof Error) { + throw error; + } else { + throw new Error('初始化患者数据库管理器失败'); + } + } + } + + // 获取PatientDao实例 + getPatientDao(): PatientDao { + if (!this.patientDao || !this.isInitialized) { + throw new Error('数据库未初始化。请先调用 initDatabase() 方法。'); + } + return this.patientDao; + } + + // 检查数据库状态 + isDatabaseReady(): boolean { + return this.isInitialized && this.patientDao !== null && this.patientDao.isDatabaseInitialized(); + } + + // 从服务器获取患者列表并存储到数据库 + async patientsToFMDB(): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法获取患者数据'); + return; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return; + } + + console.info('开始从服务器获取患者数据...'); + + hdHttp.post(BasicConstant.urlExpert + 'patientList', { + expertUuid: authStore.getUser().uuid + } as updateExtraData).then(async (res: HdResponse) => { + try { + let json: Record = JSON.parse(res + '') as Record; + console.log('服务器返回的患者数据:', json); + + if (json.code == '1' && json.data && Array.isArray(json.data)) { + console.info(`服务器返回 ${json.data.length} 个患者数据`); + + for (const item of json.data as PatientData[]) { + const patientEntity = new PatientEntity( + item.uuid || '', + item.nickname || '', + item.mobile || '', + item.realName || '', + item.nation || '', + item.sex || 0, + item.type || 0, + item.photo || '', + expertUuid + ); + // 检查本地是否已存在 + const localPatient = await this.getPatientDao().getPatientByUuid(item.uuid || ''); + if (localPatient) { + // 已存在,更新 + await this.getPatientDao().updatePatient(patientEntity); + } else { + // 不存在,插入 + await this.getPatientDao().insertPatient(patientEntity); + } + } + } else { + console.error('服务器返回数据格式错误:', json); + } + } catch (parseError) { + console.error('解析服务器响应失败:', parseError); + } + }).catch((err: BusinessError) => { + console.error('请求患者数据失败:', err); + }); + } catch (error) { + console.error('获取患者数据过程中发生错误:', error); + } + } + + // 获取所有患者数据 + async getAllPatients(): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法获取患者数据'); + return []; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return []; + } + + console.info('开始获取所有患者数据...'); + const patients = await this.getPatientDao().getPatientsByExpertUuid(expertUuid); + console.info(`成功获取 ${patients.length} 个患者数据`); + return patients; + } catch (error) { + console.error('获取所有患者数据失败:', error); + return []; + } + } + + // 根据UUID获取单个患者 + async getPatientByUuid(uuid: string): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法获取患者数据'); + return null; + } + + return await this.getPatientDao().getPatientByUuid(uuid); + } catch (error) { + console.error('根据UUID获取患者失败:', error); + return null; + } + } + + // 根据手机号获取患者 + async getPatientByMobile(mobile: string): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法获取患者数据'); + return null; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return null; + } + return await this.getPatientDao().getPatientByMobile(mobile, expertUuid); + } catch (error) { + console.error('根据手机号获取患者失败:', error); + return null; + } + } + + // 更新患者信息 + async updatePatient(patient: PatientEntity): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法更新患者数据'); + return false; + } + + await this.getPatientDao().updatePatient(patient); + return true; + } catch (error) { + console.error('更新患者信息失败:', error); + return false; + } + } + + // 删除患者 + async deletePatient(uuid: string): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法删除患者数据'); + return false; + } + + await this.getPatientDao().deletePatientByUuid(uuid); + return true; + } catch (error) { + console.error('删除患者失败:', error); + return false; + } + } + + // 删除当前专家的所有患者数据 + async deleteAllPatients(): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法删除患者数据'); + return false; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return false; + } + await this.getPatientDao().deletePatientsByExpertUuid(expertUuid); + return true; + } catch (error) { + console.error('删除所有患者失败:', error); + return false; + } + } + + // 搜索患者(根据姓名或昵称) + async searchPatients(keyword: string): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法搜索患者数据'); + return []; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return []; + } + + const allPatients = await this.getPatientDao().getPatientsByExpertUuid(expertUuid); + return allPatients.filter(patient => + patient.realName.toLowerCase().includes(keyword.toLowerCase()) || + patient.nickname.toLowerCase().includes(keyword.toLowerCase()) + ); + } catch (error) { + console.error('搜索患者失败:', error); + return []; + } + } + + // 添加患者信息(支持单个或多个) + async addPatients(patients: PatientData | PatientData[]): Promise { + try { + if (!this.isDatabaseReady()) { + console.error('数据库未准备好,无法添加患者数据'); + return false; + } + + const expertUuid = authStore.getUser().uuid; + if (!expertUuid) { + console.error('专家UUID未找到'); + return false; + } + + // 将单个患者转换为数组 + const patientsArray = Array.isArray(patients) ? patients : [patients]; + + if (patientsArray.length === 0) { + console.warn('没有患者数据需要添加'); + return true; + } + + console.info(`开始处理 ${patientsArray.length} 个患者数据...`); + + // 批量处理患者数据 + const patientDao = this.getPatientDao(); + await patientDao.beginTransaction(); + try { + for (const patientData of patientsArray) { + // 转换为PatientEntity对象 + const patientEntity = new PatientEntity( + patientData.uuid || '', + patientData.nickname || '', + patientData.mobile || '', + patientData.realName || '', + patientData.nation || '', + patientData.sex || 0, + patientData.type || 0, + patientData.photo || '', + expertUuid + ); + + // 检查患者是否已存在(根据UUID) + const existingPatient = await patientDao.getPatientByUuid(patientData.uuid || ''); + + if (existingPatient) { + // 患者已存在,更新信息 + console.info(`患者 ${patientData.uuid} 已存在,执行更新操作`); + await patientDao.updatePatient(patientEntity); + } else { + // 患者不存在,插入新记录 + console.info(`患者 ${patientData.uuid} 不存在,执行插入操作`); + await patientDao.insertPatient(patientEntity); + } + } + + await patientDao.commitTransaction(); + console.info(`成功处理 ${patientsArray.length} 个患者数据`); + return true; + } catch (error) { + await patientDao.rollbackTransaction(); + console.error('处理患者数据失败,已回滚:', error); + return false; + } + } catch (error) { + console.error('添加患者数据失败:', error); + return false; + } + } + + // 添加单个患者信息(便捷方法) + async addPatient(patient: PatientData): Promise { + return await this.addPatients(patient); + } +} + +// 导出单例实例 +export const patientDbManager = PatientDatabaseManager.getInstance(); diff --git a/commons/basic/src/main/ets/utils/TimestampUtil.ets b/commons/basic/src/main/ets/utils/TimestampUtil.ets new file mode 100644 index 0000000..124e5b7 --- /dev/null +++ b/commons/basic/src/main/ets/utils/TimestampUtil.ets @@ -0,0 +1,50 @@ +import { systemDateTime } from '@kit.BasicServicesKit'; + +export class TimestampUtil { + + static format(timestamp: number | string, formatStr: string): string { + const date = new Date(timestamp); + const padZero = (num: number, len: number = 2) => num.toString().padStart(len, '0'); + const map: Record = { + 'YYYY': date.getFullYear().toString(), + 'MM': padZero(date.getMonth() + 1), + 'DD': padZero(date.getDate()), + 'HH': padZero(date.getHours()), + 'mm': padZero(date.getMinutes()), + 'ss': padZero(date.getSeconds()), + }; + let result = formatStr; + Object.keys(map).forEach((key) => { + result = result.replace(new RegExp(key, 'g'), map[key]); + }); + return result; + } + + static isToday(dateStr: string): boolean { + try { + // 1. 解析目标日期字符串 → Date对象 + const targetDate = new Date(dateStr); + + // 2. 获取当前系统日期(年、月、日) + const now = new Date(systemDateTime.getTime()); + const currentYear = now.getFullYear(); + const currentMonth = now.getMonth(); + const currentDay = now.getDate(); + + // 3. 提取目标日期的年、月、日 + const targetYear = targetDate.getFullYear(); + const targetMonth = targetDate.getMonth(); + const targetDay = targetDate.getDate(); + + // 4. 比较日期是否相同 + return ( + targetYear === currentYear && + targetMonth === currentMonth && + targetDay === currentDay + ); + } catch (e) { + console.error("日期解析失败", e); + return false; + } + } +} \ No newline at end of file diff --git a/commons/basic/src/main/resources/base/media/home_no_qiandao_icon.png b/commons/basic/src/main/resources/base/media/home_no_qiandao_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8388c762cf408fd26d0962c358fa543520f1951b GIT binary patch literal 1987 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 UJxQ`=!FBwoB3 z@5YN~qX!Qr#)Ahno;+y0?*0U$@h9k%1(t(2NjvjS-sgG0o_SoW-dbPVT4NYyy;9cd zbWcRz$~pRf`}xH~x^0qTi!^YD3=JPKc?-7@s5nL!)sbP19(+MrhB?1uH(R8o-;qu1 za7N_Ag^owj43o`;o?-S80d3T^U4{Mq{sRkaOJVm#o!31T_3ZN4M~!i{X^#7*WU;wD zkPT%@;2>gv&^dGiIaJsQuT0O;HDtlWh4dBnG^v(e11k0r5V=&$6cQPbmN-F_GV$at zi1R`U@)8u1F2_aL5f49QM0VkR%E6aTt%s zs7EXqxx@%#Zm>OL&`@CdwnuF2f{4**;{j1vn(1i?jyI=ugK3*+!(eE5P~iBeq?x0x z|KHVd=H3CRqqBVfNgOms9)fig;DK+_!F9HyP@b&%$ROBnVthF3Vy%Y>4tm%Fsv31I z4X*Cnu7!uewFySo<%$~+!!=PwQ&`Hv*|sI83P~}orBWF&pA>{*rcja+)KoPsQxdg; zI>Tz%95~1&Gpu!nO)tepa&SBvSwp^kgshT}9WW_dwwLFUT*^1aTFY}0mtrAJ21dhO z820RnDkwV6rAv*u_>oH$?^9_fS2teKXHl+b`DS?X^ZVUZ23)@Qsj+MPIk>skSh@6V xXXEI~%coy|Y;7*C<0to?|607*JkGtj@sW9Ua^ZUB_Q|{GtyPLu?RDY)qrWk+ECm1n literal 0 HcmV?d00001 diff --git a/commons/basic/src/main/resources/base/media/sign_back_news_icon.png b/commons/basic/src/main/resources/base/media/sign_back_news_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8b94e65036b980adb2e09e04f60fac538a968312 GIT binary patch literal 4896 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 Mbd0qtwVba03k{m6yZ`_I literal 0 HcmV?d00001 diff --git a/commons/basic/src/main/resources/base/media/sign_popwindow_close.png b/commons/basic/src/main/resources/base/media/sign_popwindow_close.png new file mode 100644 index 0000000000000000000000000000000000000000..ffa02b9b90e1310cb3c3fa5e990ffff3377c300e GIT binary patch literal 3621 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 E0+*&k!~g&Q literal 0 HcmV?d00001 diff --git a/features/Home/src/main/ets/components/FlipperComp.ets b/features/Home/src/main/ets/components/FlipperComp.ets new file mode 100644 index 0000000..8f6154c --- /dev/null +++ b/features/Home/src/main/ets/components/FlipperComp.ets @@ -0,0 +1,108 @@ +import { FlipperOptions,FlipperView,FlipperOptionsBuilder } from '@hshare/hshare-flipper' +import { promptAction } from '@kit.ArkUI'; +import { router } from '@kit.ArkUI' +import { meetingModel } from '../model/HomeModel' +import { TimestampUtil } from '@itcast/basic' + +@Component +export struct FlipperComp { + @Prop bankAdBeans: meetingModel[]; + @State processedData: meetingModel[] = []; + + options: FlipperOptions = FlipperOptionsBuilder.getInstance() + .setHeight(56)//View高度 + .setInterval(3000)//上下滚动间隔,单位为毫秒 + .setAnimateParam(500)//动画持续时间,单位为毫秒 + .setOnItemClicked((item: meetingModel, index: number) => { + router.pushUrl({ + url: 'pages/WebView/WebPage', // 目标url + params: {url:item.liveurl,title:item.title} + }) + }) + .setOnItemScrolled((item: meetingModel, index: number) => { + //滚动事件 + }) + .build() + + aboutToAppear() { + if (this.bankAdBeans.length === 1) { + this.processedData = [...this.bankAdBeans, ...this.bankAdBeans, ...this.bankAdBeans]; + } else { + this.processedData = this.bankAdBeans; + } + } + + build() { + Column() { + Row() { + FlipperView({ + options: this.options, + sourceBeans: this.processedData, + itemContentView: (data: meetingModel, index: number) => { + this.itemContentView(data, index) + } + }) + } + } + .width('100%').height('100%').backgroundColor('#F4F4F4') + } + + @Builder + itemContentView(item: meetingModel, index: number) { + Stack({alignContent:Alignment.Center}){ + Image($r('app.media.meeting_back_icon')) + .width('100%').height(46).margin({top:10}) + Row(){ + Column() { + Row({space:2}){ + Image($r('app.media.meeting_live_icon')).width(30).height(13) + if (item.state === '1') { + if (TimestampUtil.isToday(item.begin_date_timestamp)) { + Image($r('app.media.meeting_begin_play_icon')).width(16).height(16) + } else { + Image($r('app.media.meeting_noPlay_icon')).width(16).height(16) + } + } else if (item.state === '0' || item.state === '3') { + if (TimestampUtil.isToday(item.begin_date_timestamp)) { + Image($r('app.media.meeting_begin_play_icon')).width(16).height(16) + } else { + Image($r('app.media.meeting_noPlay_icon')).width(16).height(16) + } + } else { + Image($r('app.media.meeting_begin_play_icon')).width(16).height(16) + } + } + Stack({alignContent:Alignment.Center}){ + Image($r('app.media.meeting_timeBack_icon')).width(74).height(15).margin({top:-3}) + if (item.state === '1') { + if (TimestampUtil.isToday(item.begin_date_timestamp)) { + Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm')) + .fontSize(12).fontColor(Color.White) + } else { + Text(TimestampUtil.format(item.begin_date_timestamp,'MM月dd日')) + .fontSize(12).fontColor(Color.White) + } + } else if (item.state === '0' || item.state === '3') { + if (TimestampUtil.isToday(item.begin_date_timestamp)) { + Text('开播'+TimestampUtil.format(item.begin_date_timestamp,'HH:mm')) + .fontSize(12).fontColor(Color.White) + } else { + Text(TimestampUtil.format(item.begin_date_timestamp,'MM月dd日')) + .fontSize(12).fontColor(Color.White) + } + } else { + Text('正在直播') + .fontSize(12).fontColor(Color.White) + } + } + }.margin({left:10}).alignItems(HorizontalAlign.Start) + Blank() + .width(1).height(28).margin({left:9}).backgroundColor('#C5C5C5') + Text(item.title) + .fontSize(14) + .fontColor('#333333') + .margin({left:9}) + }.margin({top:10}).width('100%').height(46) + } + } +} diff --git a/features/Home/src/main/ets/components/HomeIconComp.ets b/features/Home/src/main/ets/components/HomeIconComp.ets new file mode 100644 index 0000000..b66b616 --- /dev/null +++ b/features/Home/src/main/ets/components/HomeIconComp.ets @@ -0,0 +1,65 @@ +import { iconsModel } from '../model/HomeModel' +import { patientDbManager, PatientEntity } from '@itcast/basic'; +import { promptAction } from '@kit.ArkUI'; + +// interface iconsModel { +// img:string; +// name:string; +// isRed:boolean; +// } + +@Component +export struct HomeIconComp { + @Prop iconList: iconsModel[]; + @State patientIcon: string = ''; + @State patientName: string = '我的患者'; + @State videoIcon: string = ''; + @State videoName: string = '肝胆视频'; + + aboutToAppear(): void { + for (const icons of this.iconList) { + if (icons.name === '我的患者') { + this.patientIcon = icons.img; + } else if (icons.name === '肝胆视频') { + this.videoIcon = icons.img; + } + } + + for (let index = 0; index < this.iconList!.length; index++) { + const iconModel = this.iconList![index] as iconsModel ; + if (index == 0) { + iconModel.isRed = true; + } + } + } + + build() { + Row() { + Grid() { + ForEach(this.iconList, (item: iconsModel) => {//[{ 'img': this.patientIcon, 'name': this.patientName },{ 'img': this.videoIcon, 'name': this.videoName}] + GridItem(){ + Stack() { + Column() { + Image(item.img) + .width(24).height(24) + .objectFit(ImageFit.Auto) + Text(item.name) + .fontSize(14) + .fontColor('#333333') + .margin({ top: 10 }) + }.width('100%') + if (item.isRed) { + Text().backgroundColor(Color.Red).width(10).height(10).borderRadius(5) + } + }.width('25%').alignContent(Alignment.TopEnd) + }.margin({top:20,bottom:20}) + .onClick(async ()=>{ + const patients = await patientDbManager.getAllPatients(); + console.info(`添加了 ${patients.length} 个患者`); + promptAction.showToast({message:`添加了 ${patients.length} 个患者`}) + }) + }) + }.width('100%').backgroundColor(Color.White) + } + } +} diff --git a/features/Home/src/main/ets/components/HomeReplayVideoComp.ets b/features/Home/src/main/ets/components/HomeReplayVideoComp.ets new file mode 100644 index 0000000..8c0e108 --- /dev/null +++ b/features/Home/src/main/ets/components/HomeReplayVideoComp.ets @@ -0,0 +1,56 @@ +import { videoModel } from '../model/HomeModel' +import { router } from '@kit.ArkUI'; +import { BasicConstant } from '@itcast/basic' +import { videoTools } from '../polyv/VideoUtil' +import { getDisplayWindowWidth } from 'media-player-common' + +@Component +export struct HomeReplayVideoComp { + @Prop videoList: videoModel[]; + @State newVideosList: videoModel[] = this.videoList.slice(0, 4); + + build() { + Column() { + Row(){ + Text('精彩回放') + .fontSize(17) + .fontWeight(FontWeight.Bold) + .margin({left:15}) + Blank() + .layoutWeight(1) + Row(){ + Text('更多 ') + .fontSize(15) + .fontColor('#999999') + Image($r('app.media.course_invoice_to_details')) + .width(15).height(15) + }.margin({right:15}) + .onClick(()=>{ + router.pushUrl({ + url:'pages/VideoPage/VideoGandanPage', + }) + }) + }.height(50) + Grid(){ + ForEach(this.newVideosList,(item:videoModel,index:number)=>{ + GridItem(){ + Column() { + Image(item.imgpath).alt($r('app.media.default_video')).width('100%').height(102) + .objectFit(ImageFit.Fill) + Text(item.name).maxLines(2).fontSize(15).fontColor('app.color.666666').textAlign(TextAlign.Start) + .textOverflow({ overflow: TextOverflow.Ellipsis }).width('100%').height(56).padding({left:10,top:10,right:10,bottom:10}) + }.backgroundColor(Color.White) + .borderRadius(5) + .height('auto') + .clip(true) + .width('calc((100% - 45vp)/2)') + .margin({left:15,bottom:15}) + .onClick(()=>{ + videoTools.getVideoDetail(item.uuid) + }) + } + }) + }.width('100%') + }.width('100%') + } +} diff --git a/features/Home/src/main/ets/components/HomeSwiperComp.ets b/features/Home/src/main/ets/components/HomeSwiperComp.ets new file mode 100644 index 0000000..5cc692b --- /dev/null +++ b/features/Home/src/main/ets/components/HomeSwiperComp.ets @@ -0,0 +1,57 @@ +import { newsModel,expertDetailModel } from '../model/HomeModel' +import { router } from '@kit.ArkUI' + +@Preview +@Component +export struct HomeSwiperComp { + @Prop newslist: newsModel[]; + @Prop expertData:expertDetailModel; + + build() { + Column() { + Swiper() { + ForEach(this.newslist, (item: newsModel,index:number) => { + Stack({alignContent:Alignment.Center}) { + Image(item.headImg) + .objectFit(ImageFit.Fill)// 图片填充模式 + .width('100%').height('100%') + if (index == 0) { + Column({space:5}){ + Text(this.expertData.realName+'专家工作室') + .fontSize(19) + .fontColor(Color.White) + .margin({left:20,top:60}) + Text(this.expertData.hospitalName) + .fontSize(16) + .fontColor(Color.White) + .margin({left:20}) + }.width('100%').alignItems(HorizontalAlign.Start) + } + }.onClick(()=>{ + if (index == 0) { + router.pushUrl({url:'pages/MinePage/EditUserDataPage'}) + } else { + router.pushUrl({ + url: 'pages/WebView/WebPage', // 目标url + params: {url:item.path,title:item.title} + }) + } + }) + }, (item: newsModel) => JSON.stringify(item)) + } + .indicator( + Indicator.dot() + .itemWidth(8) + .itemHeight(8) + .selectedItemWidth(8) + .selectedItemHeight(8) + .color(Color.Gray) + .selectedColor($r('app.color.main_color')) + ) + .loop(true) + .autoPlay(true) + .interval(5000) + } + .width('100%') + } +} \ No newline at end of file diff --git a/features/Home/src/main/ets/components/SpeciallyEStandingComp.ets b/features/Home/src/main/ets/components/SpeciallyEStandingComp.ets new file mode 100644 index 0000000..15831a3 --- /dev/null +++ b/features/Home/src/main/ets/components/SpeciallyEStandingComp.ets @@ -0,0 +1,92 @@ +import { esiteModel } from '../model/HomeModel' +import { router } from '@kit.ArkUI' + +@Component +export struct SpeciallyEStandingComp { + @Prop esiteArray: esiteModel[]; + @State newEsiteArr: esiteModel[][] = []; + @State selectedIndex:number = 0; + + aboutToAppear(): void { + this.newEsiteArr = convertTo2DArray(this.esiteArray) + } + + changeGroup() { + animateTo({ + duration: 500, + curve: Curve.EaseIn, + }, () => { + if (this.selectedIndex === this.newEsiteArr.length - 1) { + this.selectedIndex = 0; + } else { + this.selectedIndex++; + } + }) + } + + build() { + Column() { + Row(){ + Text('专题E站') + .fontSize(17) + .fontWeight(FontWeight.Bold) + .margin({left:15}) + Blank() + .layoutWeight(1) + if (this.newEsiteArr.length>=2) { + Row(){ + Image($r('app.media.new_home_choose_icon')) + .width(15).height(15) + Text(' 换一换') + .fontSize(15) + .fontColor('#999999') + }.margin({right:15}) + .onClick(()=>{ + this.changeGroup(); + }) + } + }.height(50) + Column(){ + ForEach(getSubArray(this.newEsiteArr,this.selectedIndex),(item:esiteModel,index:number)=>{ + Image(item.img_path) + .objectFit(ImageFit.Cover) + .margin({left:10,top:10,right:10}) + .height(45).width('95%') + .onClick(()=>{ + router.pushUrl({ + url: 'pages/WebView/WebPage', // 目标url + params: {url:item.url,title:item.name} + }) + }) + }) + }.backgroundColor(Color.White).borderRadius(4).height(175).width('93%') + .margin({left:15,right:15}) + }.width('100%').alignItems(HorizontalAlign.Start) + } +} + +function convertTo2DArray(sourceArray: T[], groupSize: number = 3): T[][] { + const resultArray: T[][] = []; + + for (let i = 0; i < sourceArray.length; i += groupSize) { + const group = sourceArray.slice(i, i + groupSize); + + if (group.length < groupSize) { + const emptyObj: esiteModel = {} as esiteModel; + const emptyItems = Array(groupSize - group.length).fill(emptyObj) as T[]; + resultArray.push([...group, ...emptyItems]); + } else { + resultArray.push(group); + } + } + return resultArray; +} + +// 获取指定下标的子数组(带边界检查) +function getSubArray(data: T[][], index: number): T[] { + if (index < 0 || index >= data.length) { + // 越界返回空数组(或抛异常) + return []; + } + return data[index]; +} \ No newline at end of file diff --git a/features/Home/src/main/ets/model/HomeModel.ets b/features/Home/src/main/ets/model/HomeModel.ets new file mode 100644 index 0000000..437cbfb --- /dev/null +++ b/features/Home/src/main/ets/model/HomeModel.ets @@ -0,0 +1,126 @@ +export interface HomeModel { + code:string; + data:dataModel; + message:string; +} + +export interface dataModel{ + consult_list?:consultModel; + news_list?:newsModel[]; + gandanfile_list?:gandanfileModel[]; + isOnlineToday?:string; + has_unread?:string; + guide_ist?:guideModel[]; + expertDetail?:expertDetailModel; + excellencourse_list?:excellencourseModel[]; + video_list?:videoModel[]; + meeting_list?:meetingModel[]; + icons_list?:iconsModel[]; + welfare_notice?:welfareModel; + esite_list?:esiteModel[]; + sign_in?:string +} + +export interface consultModel { + yetDayTotalNum:string; + count:string; + list:[]; + yetDayTotalnumEPNum:string +} + +export interface newsModel { + uuid:string; + title:string; + headImg:string; + color:string; + type:string; + path:string; +} + +export interface gandanfileModel { + type:string; + article_uuid:string; + title:string; + tags:string; + path:string +} + +export interface guideModel { + create_date:string; + guide_type_uuid:string; + guide_type:string; + guide_uuid:string; + article_uuid:string; + title:string; + tags:string; + path:string +} + +export interface expertDetailModel { + photo:string; + officeName:string; + positionName:string; + hospitalName:string; + qrcode:string; + realName:string; +} + +export interface excellencourseModel { + video_num:string; + discount_type:string; + account:string; + discount_price:string; + title:string; + search_second_list:string; + study_num:string; + sroll_img:string; + index_img:string; + upload_num:string; + back_bon:string; + fuli_bon:string; + special_type_name:string; + tags:string; + id:string; +} + +export interface videoModel { + readnum:string; + uuid:string; + imgpath:string; + polyv_uuid:string; + public_name:string; + imgUrl:string; + note:string; + name:string; + path:string; + content:string; +} + +export interface meetingModel { + title:string; + begin_date_timestamp:string; + end_date_timestamp:string; + liveurl:string; + begin_date:string; + end_date:string; + state:string; + path:string; +} + +export interface iconsModel { + fixed:string; + img:string; + name:string; + isRed:boolean; +} + +export interface welfareModel { + one_last_notice:boolean; + receive_notice:boolean; +} + +export interface esiteModel { + url:string; + img_path:string; + name:string; +} \ No newline at end of file diff --git a/features/Home/src/main/ets/pages/HomePage.ets b/features/Home/src/main/ets/pages/HomePage.ets index 2ca217e..1c34d49 100644 --- a/features/Home/src/main/ets/pages/HomePage.ets +++ b/features/Home/src/main/ets/pages/HomePage.ets @@ -1,19 +1,185 @@ +import { FlipperComp } from '../components/FlipperComp' +import { HomeIconComp } from '../components/HomeIconComp' +import { HomeSwiperComp } from '../components/HomeSwiperComp' +import { SpeciallyEStandingComp } from '../components/SpeciallyEStandingComp' +import { HomeReplayVideoComp } from '../components/HomeReplayVideoComp' +import { getDisplayWindowWidth } from 'media-player-common' +import { BusinessError } from '@kit.BasicServicesKit'; +import HashMap from '@ohos.util.HashMap'; +import { BasicConstant,hdHttp, HdResponse ,logger,HdHomeNav} from '@itcast/basic/Index' +import { HomeModel,dataModel, newsModel,iconsModel } from '../model/HomeModel'; +import { DefaultHintProWindows,SignPopWindow,HdLoadingDialog } from '@itcast/basic' +import { promptAction, router } from '@kit.ArkUI'; + +@Entry @Component export struct HomePage { - @State message: string = 'Hello World'; + @State homeData:dataModel = {} as dataModel; + @State navAlpha: number = 0; + @State navBackColor: string = 'FFFFFF' + @Consume@Watch('gotoTop') + toTop:boolean; + @State hintMessage:string = ''; + @State signData:Record = {}; + + scroller:Scroller = new Scroller() + private hintWindowDialog!: CustomDialogController; + private signWindowDialog!:CustomDialogController; + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + private hintPopWindowDialog() { + this.hintWindowDialog = new CustomDialogController({ + builder:DefaultHintProWindows({ + controller:this.hintWindowDialog, + message:this.hintMessage, + cancleTitle:'', + confirmTitle:'关闭', + confirmTitleColor: '#000000', + selectedButton: (index:number)=>{ + this.hintWindowDialog.close(); + } + }), + alignment: DialogAlignment.Center, + cornerRadius:24, + autoCancel:false, + backgroundColor: ('rgba(0,0,0,0.5)'), + }) + } + + private signPopWindowDialog(){ + this.signWindowDialog = new CustomDialogController({ + builder:SignPopWindow({ + controller:this.signWindowDialog, + signDay:'今天是我们相识的第'+this.signData.gdxzday+'天', + signWeek:this.signData.totalDay, + signMouth:this.signData.continuous_day, + signNews:this.signData.news['title'], + signHtml:this.signData.news['path'], + }), + alignment: DialogAlignment.Center, + cornerRadius:8, + autoCancel:false, + backgroundColor: Color.Transparent, + backgroundBlurStyle: BlurStyle.NONE, + }) + } + + gotoTop() { + this.scroller.scrollToIndex(0); + this.initData() + } + + aboutToAppear(): void { + this.initData() + this.hintPopWindowDialog(); + this.signPopWindowDialog(); + } + + initData() { + const hashMap: HashMap = new HashMap(); + this.dialog.open() + hashMap.clear(); + hdHttp.httpReq(BasicConstant.indexV2,hashMap).then(async (res: HdResponse) => { + logger.info('Response indexV2'+res); + let json:HomeModel = JSON.parse(res+'') as HomeModel; + this.dialog.close(); + this.homeData = json.data; + for (const item of this.homeData.news_list as newsModel[]) { + if (item.type == '1') { + this.navBackColor = item.color; + } + } + }).catch((err: BusinessError) => { + this.dialog.close(); + }) + } + + getSignData() { + const hashMap: HashMap = new HashMap(); + this.dialog.open() + hashMap.clear(); + hashMap.set('score_type','1'); + hdHttp.httpReq(BasicConstant.addBonusPoints,hashMap).then(async (res: HdResponse) => { + logger.info('Response addBonusPoints'+res); + this.dialog.close(); + let json:Record = JSON.parse(res+'') as Record; + if (json.code == '1') { + this.homeData.sign_in = '1'; + this.signData = json; + this.signWindowDialog.open(); + } else if (json.code == '201') { + this.homeData.sign_in = '1'; + this.hintMessage = '今日已签到,每日只能签到一次。\n请明日继续哦~'; + this.hintWindowDialog.open(); + } + }).catch((err: BusinessError) => { + this.dialog.close(); + }) + } build() { - Row() { - Column() { - Text(this.message) - .fontSize($r('app.float.page_text_font_size')) - .fontWeight(FontWeight.Bold) - .onClick(() => { - this.message = 'Welcome'; - }) + Stack(){ + Scroll(this.scroller) { + Column() { + if (this.homeData.news_list && this.homeData.news_list.length > 0) { + HomeSwiperComp({ newslist: this.homeData.news_list, expertData: this.homeData.expertDetail }) + .height(getDisplayWindowWidth().vp / 16 * 9) + } + if (this.homeData.icons_list && this.homeData.icons_list.length > 0) { + HomeIconComp({iconList:this.homeData.icons_list}) + } + if (this.homeData.meeting_list && this.homeData.meeting_list.length > 0) { + FlipperComp({ bankAdBeans: this.homeData.meeting_list }) + .height(56) + .backgroundColor(Color.Yellow) + } + if (this.homeData.esite_list && this.homeData.esite_list.length > 0) { + SpeciallyEStandingComp({ esiteArray: this.homeData.esite_list }) + } + if (this.homeData.video_list && this.homeData.video_list.length > 0) { + HomeReplayVideoComp({ videoList: this.homeData.video_list }) + } + } } - .width('100%') + .edgeEffect(EdgeEffect.Spring) + .scrollBar(BarState.Off) + .onWillScroll(() => { + const yOffset = this.scroller.currentOffset().yOffset; + const threshold = 56; + if (yOffset <= 0) { + this.navAlpha = 0; + } else if (yOffset >= threshold) { + this.navAlpha = 1; + } else { + this.navAlpha = yOffset / threshold; + } + }) + HdHomeNav({ + leftIcon:this.homeData.sign_in == '0'?$r('app.media.home_no_qiandao_icon'):$r('app.media.home_qiandao_icon'), + placeholder:'搜索视频、会议', + alpha:this.navAlpha, + backColor:this.navBackColor, + leftItemAction:()=>{ + this.getSignData(); + }, + searchItemAction:()=>{ + router.pushUrl({ + url:'pages/SearchPage/VideoSearchPage', + params: { + params:{'pageName':'视频'} + } + }) + } + }) } + .alignContent(Alignment.Top) + .backgroundColor('#f4f4f4') + .width('100%') .height('100%') } } diff --git a/features/Home/src/main/resources/base/element/float.json b/features/Home/src/main/resources/base/element/float.json index 33ea223..a0a93dd 100644 --- a/features/Home/src/main/resources/base/element/float.json +++ b/features/Home/src/main/resources/base/element/float.json @@ -5,4 +5,4 @@ "value": "50fp" } ] -} +} \ No newline at end of file diff --git a/features/Home/src/main/resources/base/media/course_invoice_to_details.png b/features/Home/src/main/resources/base/media/course_invoice_to_details.png new file mode 100644 index 0000000000000000000000000000000000000000..1f64e9115323e73cc97c4da2ae08be8914d39169 GIT binary patch literal 968 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 TVU7mJK>1D@ni~`zKO6dg=r>R| literal 0 HcmV?d00001 diff --git a/features/Home/src/main/resources/base/media/meeting_begin_play_icon.gif b/features/Home/src/main/resources/base/media/meeting_begin_play_icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..27ee658882bcf6bab29ef70ff07000aed7d25262 GIT binary patch literal 1304 zcmZ?wbhEHblwpuzXkcV`#wp0az@Ye_+s`#5*x50_)kx2PnGqrs;Txdfl30=mq;2dg z3KEmEQ%e+*Qqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)Wn zQ(;w+TacStlBiITo0C^;Rbi`?n3A8AY6WD2g!Ppaz)DK8ZIv8B5(*$Yo%4%Q6-@Qa zbdwEDEELQw^$g8S42;cm6pRcEE%gm7^bL)54GgUe%&bh!6`()~Xj@TAnpKdC8`Lf! z&sHg;q@=(~U%$M(T(8_%FTW^V-_X($Xoiu#k&!M?g>G?WUP)qwZeFo6%mkOz;^d;t zf|AVqJOz-6iAnjTCALaHmqNUdTL3pUuUHT49lhlIT>Xl~0)0b01CW*(J3ovn(~m zttdZN0qkq5Ox$iU#AzN>ZwhX=7~#~b4|I$^C~lGB8YToxKp-YOOz=g( zl+F16-`_vKfBpRN{oB_spFe&4@c!M~H?Lp4eDVC*(TITuzuazHLF*xT(NxF z(j|)*EnF~v-rPB}XU&{3ecIG1lP67_(BIeF)7{nC(caeD(%jV8P+wPDQ(aYAQC?PB zQe0G6ke`>Elbw~Bk)D>ClAM&75FZyC6CD*95gryA5*!p5;P2<_Vr*n+ps%N^qphW>p{}N?qO7E-ATK8?BP}H--h6M*Z~nGN z9@Cz6E_>A_f>6%HAjrT4gbqxCEwU?TAAk6u)Bjv#{<7}!03<`1890IJ1c9oQc;r@| zUfVvWhd1cuf{kL2-x)^zC{V~`N~vsp{A-+RnxVD7AVs~x?(*Ls%YeNdN!< literal 0 HcmV?d00001 diff --git a/features/Home/src/main/resources/base/media/meeting_live_icon.png b/features/Home/src/main/resources/base/media/meeting_live_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bae6d98a9b965cb7dd1e4825c672450e6dc0358e GIT binary patch literal 3599 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 VF@r%Pl7avL002ovPDHLkV1llm(_H`n literal 0 HcmV?d00001 diff --git a/features/Home/src/main/resources/base/media/meeting_noPlay_icon.png b/features/Home/src/main/resources/base/media/meeting_noPlay_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..af73810a75a91d4b3d384de3d2b8b7fcd37f42bc GIT binary patch literal 2184 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 d`*SRU)88=4Q;kXp = []; + @State expertData:object | string = new Object; scroller = new Scroller() + aboutToAppear(): void { + this.uploadBackImgAction(); + emitter.on({ eventId: BasicConstant.notification_home_tab_change }, (eventData: emitter.EventData) => { + if (eventData.data?.changeIndex === 2) { + this.uploadBackImgAction(); + } + }); + } + + uploadBackImgAction() { + const hashMap: HashMap = new HashMap(); + hdHttp.httpReq(BasicConstant.myData,hashMap).then(async (res: HdResponse) => { + console.info(`我的背景图: ${res}`); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '200') { + this.heroArray = json.data["honor_list"]; + this.myInfoBackGround = json.data['myInfoBackGround']; + this.expertData = json.data; + // 获取ranking值(带安全类型转换) + const ranking: string = json.data["ranking"]?.toString() ?? ""; + if (Number(ranking) > 0) { + // 创建排名对象 + const rankDic: heroFirst = { + "id": "ranking", + "nick_name": `随访达人 I 排名${ranking}` + }; + // 插入数组首位 + this.heroArray.unshift(rankDic); + } + } else { + console.error('我的背景图:'+json.message) + } + }).catch((err: BusinessError) => { + console.info(`Response login fail: ${err}`); + }) + } + build() { Column() { HdNav({ title: '我的', showLeftIcon:false , showRightIcon: false, hasBorder: true }) Scroll(this.scroller) { - Column() { - HeaderView() - // OneSection() - // TwoSection() - // ThreeSection() - FourSection() - OtherList() - } + Stack() { + Image(this.myInfoBackGround) + .backgroundImageSize(ImageSize.Cover) + .width('100%') + Column() { + HeaderView({heroArray:this.heroArray,expertData:this.expertData}) + OneSection() + FourSection() + OtherList() + } + }.alignContent(Alignment.Top) } .width('100%') .height('100%') diff --git a/features/mypage/src/main/ets/view/FourSection.ets b/features/mypage/src/main/ets/view/FourSection.ets index 3490fa9..c6ca118 100644 --- a/features/mypage/src/main/ets/view/FourSection.ets +++ b/features/mypage/src/main/ets/view/FourSection.ets @@ -21,10 +21,10 @@ export struct FourSection { // new MyPageSectionClass('threeItem',this.pushIconPath,this.pushStatus,''), // new MyPageSectionClass('fourItem',$r('app.media.my_page_version'),'发现新版本','') - new MyPageSectionClass('oneItem',$r('app.media.my_page_choosePhone'),'更换手机号','pages/MinePage/ChangePhonePage'), - new MyPageSectionClass('twoItem',this.pushIconPath,this.pushStatus,''), - new MyPageSectionClass('threeItem',$r('app.media.my_page_guanyu_icon'),'关于肝胆相照','pages/WebView/WebPage'), - new MyPageSectionClass('fourItem',$r('app.media.my_page_zhibo_icon'),'肝胆相照直播群','pages/WebView/WebPage') + new MyPageSectionClass('oneItem',$r('app.media.my_page_choosePhone'),'更换手机号','pages/MinePage/ChangePhonePage',false), + new MyPageSectionClass('twoItem',this.pushIconPath,this.pushStatus,'',false), + new MyPageSectionClass('threeItem',$r('app.media.my_page_guanyu_icon'),'关于肝胆相照','pages/WebView/WebPage',false), + new MyPageSectionClass('fourItem',$r('app.media.my_page_zhibo_icon'),'肝胆相照直播群','pages/WebView/WebPage',false) ]; aboutToAppear() { @@ -60,7 +60,7 @@ export struct FourSection { // 更新数组中的标题 this.fourSectionList = this.fourSectionList.map((item, index) => { if (index === 1) { - return new MyPageSectionClass(item.id, this.pushIconPath, this.pushStatus, item.path); + return new MyPageSectionClass(item.id, this.pushIconPath, this.pushStatus, item.path,false); } return item; }); diff --git a/features/mypage/src/main/ets/view/HeaderView.ets b/features/mypage/src/main/ets/view/HeaderView.ets index 39a3a99..212b55d 100644 --- a/features/mypage/src/main/ets/view/HeaderView.ets +++ b/features/mypage/src/main/ets/view/HeaderView.ets @@ -33,8 +33,8 @@ export struct HeaderView { @State heroIndex: number = 0 // 当前页索引 @State photoPath:string = BasicConstant.urlImage+authStore.getUser().photo; @State name:string = authStore.getUser().realName; - @State myPageData:object = new Object; - @State heroArray:Array = []; + @Prop heroArray:Array = []; + @Prop expertData:object | string= new Object; @State clickHeroId:string = ''; @Consume@Watch('gotoTop') toTop:boolean; @@ -48,11 +48,9 @@ export struct HeaderView { aboutToAppear(): void { this.uploadUserDataAction(); - this.uploadBackImgAction(); emitter.on({ eventId: BasicConstant.notification_home_tab_change }, (eventData: emitter.EventData) => { if (eventData.data?.changeIndex === 2) { this.uploadUserDataAction(); - this.uploadBackImgAction(); } }); } @@ -81,103 +79,82 @@ export struct HeaderView { }) } - uploadBackImgAction() { - const hashMap: HashMap = new HashMap(); - hdHttp.httpReq(BasicConstant.myData,hashMap).then(async (res: HdResponse) => { - console.info(`我的背景图: ${res}`); - let json:RequestDefaultModel = JSON.parse(res+'') as RequestDefaultModel; - if(json.code == '200') { - this.heroArray = json.data["honor_list"]; - this.myPageData = json.data; - // 获取ranking值(带安全类型转换) - const ranking: string = this.myPageData["ranking"]?.toString() ?? ""; - if (Number(ranking) > 0) { - // 创建排名对象 - const rankDic: heroFirst = { - "id": "ranking", - "nick_name": `随访达人 I 排名${ranking}` - }; - // 插入数组首位 - this.heroArray.unshift(rankDic); - } - } else { - console.error('我的背景图:'+json.message) - } - }).catch((err: BusinessError) => { - console.info(`Response login fail: ${err}`); - }) - } - handleAvatarClick() { router.pushUrl({url:'pages/MinePage/EditUserDataPage'}) } build() { - // Row() { - Column({space:5}) { - Row({space:10}) { - Image(this.photoPath) - .alt($r('app.media.userPhoto_default')) - .margin({left:15}) - .width(60) - .height(60) - .borderRadius(5) - .objectFit(ImageFit.Cover) - .onClick(()=>this.handleAvatarClick()) - Column({space:5}) { - Text(this.name) - .fontSize(18) - .fontColor('#FFFFFF') - .onClick(()=>this.handleAvatarClick()) - List({space:5,initialIndex:this.heroIndex,scroller:this.scrollerForList}) { - ForEach(this.heroArray, (item: heroFirst) => { - ListItem() { - Row() { - Image(item.id === 'ranking'?$r('app.media.my_home_hero_ranking'):$r('app.media.my_page_header_hertIcon')) - .width(13) - .height(13) - .margin({left:7}) - Text(item.nick_name) - .fontColor(Color.White) - .fontSize(11) - .margin({left:4,right:10}) - } - .height(18) - .margin({right:5}) - .borderRadius(9) - .borderWidth(1) - .borderColor(Color.White) - .onClick(()=>{ - if (item.id !== 'ranking') { - this.clickHeroId = item.id; - this.dialogController.open(); - } - }) - } - }) - } - .listDirection(Axis.Horizontal) - .scrollBar(BarState.Off) - .width('100%') - .height(18) - } - .alignItems(HorizontalAlign.Start) + Column({space:15}) { + Row({space:10}) { + Image(this.photoPath) + .alt($r('app.media.userPhoto_default')) + .margin({left:15}) + .width(60) .height(60) - .width('70%') - .margin({top:22}) + .borderRadius(5) + .objectFit(ImageFit.Cover) + .onClick(()=>this.handleAvatarClick()) + Column({space:5}) { + Text(this.name) + .fontSize(18) + .fontColor('#FFFFFF') + .onClick(()=>this.handleAvatarClick()) + List({space:5,initialIndex:this.heroIndex,scroller:this.scrollerForList}) { + ForEach(this.heroArray, (item: heroFirst) => { + ListItem() { + Row() { + Image(item.id === 'ranking'?$r('app.media.my_home_hero_ranking'):$r('app.media.my_page_header_hertIcon')) + .width(13) + .height(13) + .margin({left:7}) + Text(item.nick_name) + .fontColor(Color.White) + .fontSize(11) + .margin({left:4,right:10}) + } + .height(18) + .margin({right:5}) + .borderRadius(9) + .borderWidth(1) + .borderColor(Color.White) + .onClick(()=>{ + if (item.id !== 'ranking') { + this.clickHeroId = item.id; + this.dialogController.open(); + } + }) + } + }) + } + .listDirection(Axis.Horizontal) + .scrollBar(BarState.Off) + .width('100%') + .height(18) + }.alignItems(HorizontalAlign.Start).width('calc(100% - 100vp)') + }.width('100%').margin({top:20}) + Column(){ + Row({space:70}){ + ForEach([{'title':'随访患者数','content':this.expertData['expert_apply_num'] || '0'}, + {'title':'公益咨询数','content':this.expertData['consult_total_num'] || '0'}], + // {'title':'患者送花数','content':this.expertData['ping_flowewr_num'] || '0'}], + (item:object)=>{ + Column(){ + Text(item['content']?.toString() || '0') + .fontSize(20) + .fontColor('#000000') + .height(28) + Text(item['title']) + .fontSize(12) + .fontColor('#333333') + .height(17) + }.height('100%').justifyContent(FlexAlign.Center) + }) } - .width('100%') - .height(97) - } - .width('100%') - .height(97) - .backgroundImage(this.myPageData["myInfoBackGround"]).backgroundImageSize(ImageSize.Cover) + }.width('95%').height(65).borderRadius(5).backgroundColor(Color.White) } - // } - gotoTop() - { + .width('100%') + } + gotoTop() { this.photoPath = BasicConstant.urlImage+authStore.getUser().photo; - - } } diff --git a/features/mypage/src/main/ets/view/MyPageSectionItem.ets b/features/mypage/src/main/ets/view/MyPageSectionItem.ets index e6f623c..be76a24 100644 --- a/features/mypage/src/main/ets/view/MyPageSectionItem.ets +++ b/features/mypage/src/main/ets/view/MyPageSectionItem.ets @@ -5,15 +5,19 @@ export struct MyPageSectionItem { @Prop sectionItem: MyPageSectionClass; build() { - Column(){ - Image(this.sectionItem.imageSrc) - .width(35).height(35) - .borderRadius(17.5) - .objectFit(ImageFit.Auto) - Text(this.sectionItem.title) - .fontSize(12) - .fontColor(Color.Black) - .margin({top:6}) - } + Stack() { + Column() { + Image(this.sectionItem.imageSrc) + .width(24).height(24) + .objectFit(ImageFit.Auto) + Text(this.sectionItem.title) + .fontSize(14) + .fontColor('#333333') + .margin({ top: 10 }) + }.width('100%') + if (this.sectionItem.status) { + Text().backgroundColor(Color.Red).width(10).height(10).borderRadius(5) + } + }.width('100%').height('100%').alignContent(Alignment.TopEnd) } } diff --git a/features/mypage/src/main/ets/view/OneSection.ets b/features/mypage/src/main/ets/view/OneSection.ets index 25766f0..c390511 100644 --- a/features/mypage/src/main/ets/view/OneSection.ets +++ b/features/mypage/src/main/ets/view/OneSection.ets @@ -1,19 +1,34 @@ -import { it } from "@ohos/hypium"; import { MyPageSectionClass } from "../model/MyPageSectionClass"; import { MyPageSectionItem } from '../view/MyPageSectionItem' +import { router } from "@kit.ArkUI"; +import { BasicConstant,hdHttp, HdResponse ,logger,authStore} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import { it } from "@ohos/hypium"; + +interface extraData { + expertUuid: string +} + +interface requestCallBack { + code:string; + msg:string; + message:string; + data:Array +} -@Preview @Component export struct OneSection { @State sectionTitle: string = "随访服务"; @State currentIndex: number = 0; + @Consume@Watch('gotoTop') + toTop:boolean; @State oneSectionList: Array = [ - new MyPageSectionClass('oneItem', $r('app.media.app_icon'), '患者审核', '/pages/MyHomePage'), - new MyPageSectionClass('twoItem', $r('app.media.app_icon'), '患者分组', '/pages/MyHomePage'), - new MyPageSectionClass('threeItem', $r('app.media.app_icon'), '群发消息', '/pages/MyHomePage'), - new MyPageSectionClass('fourItem', $r('app.media.app_icon'), '随访二维码', '/pages/MyHomePage'), - new MyPageSectionClass('fiveItem', $r('app.media.app_icon'), '出诊计划', '/pages/MyHomePage') + new MyPageSectionClass('oneItem', $r('app.media.my_page_patientAudit'), '患者审核', '/pages/MyHomePage',false), + new MyPageSectionClass('twoItem', $r('app.media.my_page_patientList'), '患者分组', '/pages/MyHomePage',false), + new MyPageSectionClass('threeItem', $r('app.media.my_page_message'), '群发消息', '/pages/MyHomePage',false), + new MyPageSectionClass('fourItem', $r('app.media.my_page_QrCode'), '随访二维码', '/pages/MyHomePage',false), + new MyPageSectionClass('fiveItem', $r('app.media.my_page_visitPlan'), '出诊计划', '/pages/MyHomePage',false) ]; private getPagedItems(): Array> { @@ -25,6 +40,35 @@ export struct OneSection { return pages; } + aboutToAppear(): void { + this.getApplyListData(); + } + + gotoTop() { + this.getApplyListData(); + } + + getApplyListData(){ + hdHttp.post(BasicConstant.applyList, { + expertUuid: authStore.getUser().uuid, + } as extraData).then(async (res: HdResponse) => { + logger.info('Response applyList'+res); + let json:requestCallBack = JSON.parse(res+'') as requestCallBack; + if(json.code == '1') { + if (json.data.length > 0) { + this.oneSectionList = this.oneSectionList.map((item, index) => { + if (index === 0) { + return new MyPageSectionClass(item.id,item.imageSrc,item.title,item.path,true); + } + return item; + }); + } + } + }).catch((err: BusinessError) => { + console.info(`Response fails: ${err}`); + }) + } + build() { Column() { // 标题 @@ -42,6 +86,17 @@ export struct OneSection { ForEach(pageItems, (item: MyPageSectionClass) => { GridItem() { MyPageSectionItem({ sectionItem: item }) + .onClick(()=>{ + if (item.title === '患者审核') { + router.pushUrl({ + url:'pages/PatientsPage/PatientPages' + }) + } else if (item.title === '患者分组') { + router.pushUrl({ + url:'pages/PatientsPage/PatientsGroupPage' + }) + } + }) } }, (item: MyPageSectionClass) => item.id) } @@ -51,13 +106,13 @@ export struct OneSection { .height('100%') .width('100%') }) - } + }.width('100%') .index(this.currentIndex) .onChange((index: number) => { this.currentIndex = index; }) .height(78) - .indicator(false) + .indicator(false).loop(false) Row() { ForEach(new Array(Math.ceil(this.oneSectionList.length / 4)).fill(0), (item: number, idx: number) => { diff --git a/features/mypage/src/main/ets/view/ThreeSection.ets b/features/mypage/src/main/ets/view/ThreeSection.ets index db0d862..bd46de9 100644 --- a/features/mypage/src/main/ets/view/ThreeSection.ets +++ b/features/mypage/src/main/ets/view/ThreeSection.ets @@ -9,12 +9,12 @@ export struct ThreeSection { @State currentIndex: number = 0; @State threeSectionList:Array = [ - new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的账户','/pages/MyHomePage'), - new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的积分','/pages/MyHomePage'), - new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的福利','/pages/MyHomePage'), - new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的鲜花','/pages/MyHomePage'), - new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课件明细','/pages/MyHomePage'), - new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课程明细','/pages/MyHomePage') + new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的账户','/pages/MyHomePage',false), + new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的积分','/pages/MyHomePage',false), + new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的福利','/pages/MyHomePage',false), + new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的鲜花','/pages/MyHomePage',false), + new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课件明细','/pages/MyHomePage',false), + new MyPageSectionClass('fiveItem',$r('app.media.app_icon'),'课程明细','/pages/MyHomePage',false) ]; private getPagedItems(): Array> { diff --git a/features/mypage/src/main/ets/view/TwoSection.ets b/features/mypage/src/main/ets/view/TwoSection.ets index dd301a0..f6930bb 100644 --- a/features/mypage/src/main/ets/view/TwoSection.ets +++ b/features/mypage/src/main/ets/view/TwoSection.ets @@ -9,10 +9,10 @@ export struct TwoSection { @State currentIndex: number = 0; @State twoSectionList:Array = [ - new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的视频','/pages/MyHomePage'), - new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的课程','/pages/MyHomePage'), - new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的下载','/pages/MyHomePage'), - new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的收藏','/pages/MyHomePage') + new MyPageSectionClass('oneItem',$r('app.media.app_icon'),'我的视频','/pages/MyHomePage',false), + new MyPageSectionClass('twoItem',$r('app.media.app_icon'),'我的课程','/pages/MyHomePage',false), + new MyPageSectionClass('threeItem',$r('app.media.app_icon'),'我的下载','/pages/MyHomePage',false), + new MyPageSectionClass('fourItem',$r('app.media.app_icon'),'我的收藏','/pages/MyHomePage',false) ]; private getPagedItems(): Array> { diff --git a/features/mypage/src/main/resources/base/media/my_page_QrCode.png b/features/mypage/src/main/resources/base/media/my_page_QrCode.png new file mode 100644 index 0000000000000000000000000000000000000000..6bd355daefa3fef4d39988ffc7114901dddff9e6 GIT binary patch literal 1927 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 literal 0 HcmV?d00001 diff --git a/features/mypage/src/main/resources/base/media/my_page_patientAudit.png b/features/mypage/src/main/resources/base/media/my_page_patientAudit.png new file mode 100644 index 0000000000000000000000000000000000000000..5dafee4382a9eb5d063d25fbd1b666c3b673595c GIT binary patch literal 2981 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 Z{|7M1A;BQv{I382002ovPDHLkV1heDF?Ij| literal 0 HcmV?d00001 diff --git a/features/mypage/src/main/resources/base/media/my_page_visitPlan.png b/features/mypage/src/main/resources/base/media/my_page_visitPlan.png new file mode 100644 index 0000000000000000000000000000000000000000..4eade1410a264d879bc650d3bc822ac739ec13d5 GIT binary patch literal 2371 zcmV-J3B2}+P)Px-{YgYYRCr$PTWf3_R~7!woz1#ViQPEFk3djq3Nmd0RVbBsyFxSt(eNlqC`e$( z_Ut-}fuxS|qfuR>QmNQ!32y9}S+{mUC~d8l1|&+ukET_j5)Yx!k|NR2h(vK>YUfqg z_1ZfJ9cwmPFZ-C;nT^-c-hZPx_dLEi_uO;OJwqsuoCr`EpWFzP0m|bcegUG^*4CBb zaCkK{zdbQAae8!gG?7DQvFQU}5WWCvZEda5G_4=NR{7g0QZJ<_)>~0g z(VI=tV$;ve%y3mz)pTE9-#Y*}2*9#=5b3%u%jRnU-cLkYCSlBcHGuUg?~W7E@ob7+ z26~BqCjmTbS=PZIK*|V^uIu*zxEsLAAilyP@Hh*g&$6rs{L216fZE&JKc$phd%?(Wdo*jNvM-2h}+$vQqlMCX>s61xV#$DPebmfM33 z4GrBrJw0=Ug0ujj&d$!Ncsy<~^A?BUG26DcR9060CzF6;2i*ere?TeqB!G3IFA+T% z4u|#r{{9Pr0MydbvU=IFWg_5KhvAD#sm+FAjAs(i-rjyA5xti3uE(;h`?4uhZ2D+4 zT5H?(Qvg2i=c>_^#@DK@=V{oR0;zQjBM)YjJaK~2+MWahO1-XWszo2L0_Ho?WFFFAmm zpuU-iq5xjjH0^l(RUKS(fTGdp&CGn5nXB@BYa*Hk&}y3Ifh4W2>jLUe07&MYu}Yc7_edxdx?^Bq z;6y%o@_x|)(sg|k068tY9SctP9UUDj6N$v*0Pb+f;FN9K8;6F5{_2Ll6aZRIM85;@ zA!g3y0kR~C-LDEG6Cdf?FNiYG@bz}RM6#`w`tQR zt+uvSO0O6nA3vAprZ=xzR~$fjE%r#TyAYHMq&Bcex$ND|6S>4fwE z9AxI#lu{>@Qj@lA*VwkbmWXa*X6Y-CWtcu@nE5x%ynSeB=!_eEyaMDaQFNn_bMrCC z%(tbr=e$Kkzqf7s5zDe($#p(sW@*0or1UE>^B2={E+(QwiA17fXlUrod^&joNSOIe z6muygo}+2nC89p1)I)=VgVM24(2z5rZQDDUxhn-Ex#)bl zy1KeKS4=7ifMmC-sj1n)%=f3n9PyNU8yXt+`Z+2j5tSWyY-~(gdgSIe=}9w0v}ba1 z^5JZ|TS)*EjYhAvZTr^%KI#}W?>Y7L_0pM<^BlGJ`ZYH*5={i{@ZE2ml6JtX!#S(< zqj6_k_^&J2nO4(fw%*@Peb-P;?0+8%(6%`eaG4ow1A7Q6u zSr4aw6i)yJohUlZ3@-rqOj0ps{M{hV~tI*J}QP4hapMfyUQn9P%8G7^dWJ0;^jsg(MXVHl|mVGA3{-PCl0OcbNh z=+|xA9tKeDD7`Nhi~Z0|>`BrT>OnF;5{djYS$za>UMbaL7{;$s<$LlVFN(XNjYJ~b zm|5aa^1RhAKzqAm#{@oM@+MnzF-P8jB6kZf5>*@(M^Pb-Q60&$~uv zeiy5o0XUpz&fgV7Xf(RPwr#nDkdg6Mip2(K4>L<mNNXF=Do@n`hrjMl8@-Rt^<&K!SfxI2W5{|RaM=REOq*+2YHY= zPwJ~4Bn+f*u9T8m$rro|_d-uZ>rK;?B@90mMK3aaR}_=Nwr}6QW_EUVA2WZ`ivk{K z7kJQ;sY$0A=e2^rB6m) z@8%PU#H6Na#lM^PX;+}rOR~EoPzwWKR&xD80oYVD7A4VPRy6AZWW1<7iUHbj#jtX9 pReQx)TYw>DfbxJ=1}G0e{{!oY6v3O^)C>Rs002ovPDHLkV1nvSc*y_& literal 0 HcmV?d00001 diff --git a/features/patient/.gitignore b/features/patient/.gitignore new file mode 100644 index 0000000..e2713a2 --- /dev/null +++ b/features/patient/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/features/patient/BuildProfile.ets b/features/patient/BuildProfile.ets new file mode 100644 index 0000000..3a501e5 --- /dev/null +++ b/features/patient/BuildProfile.ets @@ -0,0 +1,17 @@ +/** + * Use these variables when you tailor your ArkTS code. They must be of the const type. + */ +export const HAR_VERSION = '1.0.0'; +export const BUILD_MODE_NAME = 'debug'; +export const DEBUG = true; +export const TARGET_NAME = 'default'; + +/** + * BuildProfile Class is used only for compatibility purposes. + */ +export default class BuildProfile { + static readonly HAR_VERSION = HAR_VERSION; + static readonly BUILD_MODE_NAME = BUILD_MODE_NAME; + static readonly DEBUG = DEBUG; + static readonly TARGET_NAME = TARGET_NAME; +} \ No newline at end of file diff --git a/features/patient/Index.ets b/features/patient/Index.ets new file mode 100644 index 0000000..8751295 --- /dev/null +++ b/features/patient/Index.ets @@ -0,0 +1,17 @@ +export { MainPage } from './src/main/ets/components/MainPage'; + +export { PatientApplyPage } from './src/main/ets/components/PatientApplyPage' + +export { PatientSetMsgPage } from './src/main/ets/components/PatientSetMsgPage' + +export { applyListCallBacl, applyListModel, applyHistoryCallBacl , historyObjectModel, historyModel } from './src/main/ets/models/ApplyModel' + +export { PatientsGroup } from './src/main/ets/components/PatientsGroup' + +export { groupRequest,groupRequestCall,groupModel,patientListModel } from './src/main/ets/models/PatientsGroupModel' + +export { BuildOrEditGroupPage } from './src/main/ets/components/BuildOrEditGroupPage' + +export { PatientsListComp } from './src/main/ets/components/PatientsListComp' + +export { PatientDetailsComp } from './src/main/ets/components/PatientDetailsComp' \ No newline at end of file diff --git a/features/patient/build-profile.json5 b/features/patient/build-profile.json5 new file mode 100644 index 0000000..e6773f9 --- /dev/null +++ b/features/patient/build-profile.json5 @@ -0,0 +1,31 @@ +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + }, + "consumerFiles": [ + "./consumer-rules.txt" + ] + } + }, + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest" + } + ] +} diff --git a/features/patient/consumer-rules.txt b/features/patient/consumer-rules.txt new file mode 100644 index 0000000..e69de29 diff --git a/features/patient/hvigorfile.ts b/features/patient/hvigorfile.ts new file mode 100644 index 0000000..4218707 --- /dev/null +++ b/features/patient/hvigorfile.ts @@ -0,0 +1,6 @@ +import { harTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/features/patient/obfuscation-rules.txt b/features/patient/obfuscation-rules.txt new file mode 100644 index 0000000..272efb6 --- /dev/null +++ b/features/patient/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/features/patient/oh-package-lock.json5 b/features/patient/oh-package-lock.json5 new file mode 100644 index 0000000..d504604 --- /dev/null +++ b/features/patient/oh-package-lock.json5 @@ -0,0 +1,25 @@ +{ + "meta": { + "stableOrder": true + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@itcast/basic@../../commons/basic": "@itcast/basic@../../commons/basic", + "refreshlib@../../RefreshLib": "refreshlib@../../RefreshLib" + }, + "packages": { + "@itcast/basic@../../commons/basic": { + "name": "@itcast/basic", + "version": "1.0.0", + "resolved": "../../commons/basic", + "registryType": "local" + }, + "refreshlib@../../RefreshLib": { + "name": "refreshlib", + "version": "1.0.0", + "resolved": "../../RefreshLib", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/features/patient/oh-package.json5 b/features/patient/oh-package.json5 new file mode 100644 index 0000000..81ee9ae --- /dev/null +++ b/features/patient/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "name": "patient", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "Index.ets", + "author": "", + "license": "Apache-2.0", + "dependencies": { + "@itcast/basic": "file:../../commons/basic", + "refreshlib": "file:../../RefreshLib" + } +} diff --git a/features/patient/src/main/ets/components/BuildOrEditGroupPage.ets b/features/patient/src/main/ets/components/BuildOrEditGroupPage.ets new file mode 100644 index 0000000..a328f45 --- /dev/null +++ b/features/patient/src/main/ets/components/BuildOrEditGroupPage.ets @@ -0,0 +1,304 @@ +import { authStore, HdNav, PositionSelectedSheet } from '@itcast/basic'; +import { promptAction, router } from '@kit.ArkUI' +import { HdLoadingDialog,DefaultHintProWindows } from '@itcast/basic' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import { patientListModel } from '../models/PatientsGroupModel' + +@Component +export struct BuildOrEditGroupPage { + @State params:Record = router.getParams() as Record + scrollerCon:Scroller = new Scroller() + @State groupPatientList:patientListModel[] = [] + @State groupName:string = '' + private hintWindowDialog!: CustomDialogController + @Consume@Watch('onRefreshAction') refreshFlag: boolean; + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + private hintPopWindowDialog() { + this.hintWindowDialog = new CustomDialogController({ + builder:DefaultHintProWindows({ + controller:this.hintWindowDialog, + message:'确定删除该分组', + cancleTitleColor: '#333333', + confirmTitleColor: '#333333', + selectedButton: (index:number)=>{ + if (index === 1) { + this.deleGroupAction() + } + this.hintWindowDialog.close(); + } + }), + alignment: DialogAlignment.Center, + cornerRadius:24, + backgroundColor: ('rgba(0,0,0,0.5)'), + }) + } + + onRefreshAction(flag: boolean) { + const returnParams = this.getUIContext().getRouter().getParams() as Record; + const patients = returnParams?.selectedPatients as patientListModel[] | undefined; + if (patients?.length) { + for (const model of returnParams.selectedPatients as patientListModel[]) { + if (model.isSelected) { + this.groupPatientList.push(model) + } + } + } + } + + aboutToAppear(): void { + if (this.params.title != '新建分组') { + this.getGroupPatientsData() + } + this.hintPopWindowDialog() + } + + getGroupPatientsData() { + this.dialog.open() + hdHttp.post(BasicConstant.patientListByGroup, { + "expert_uuid": authStore.getUser().uuid, + "group_uuid":this.params.group_uuid + } as Record).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientListByGroup'+res); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '1') { + this.groupPatientList = json.data as patientListModel[]; + } else { + console.error('获取患者分组列表失败:'+json.message) + promptAction.showToast({ message: String(json.message), duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + } + + deleGroupAction() { + this.dialog.open() + hdHttp.post(BasicConstant.deleteGroup, { + "expert_uuid": authStore.getUser().uuid, + "group_uuid":this.params.group_uuid + } as Record).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientListByGroup'+res); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '1') { + promptAction.showToast({ message: '删除分组成功', duration: 1000 }) + router.back(); + } else { + console.error('删除患者分组列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + } + + setCreatOrEditGroup(index:number) { + if (this.groupName.length <= 0) { + promptAction.showToast({ message: '请输入分组名称', duration: 1000 }) + return + } + const uuidString:string = this.groupPatientList.map(item => item.uuid).join(","); + this.dialog.open() + hdHttp.post(index == 0 ? BasicConstant.addGroup:BasicConstant.updateGroup, index == 0 ? { + "expert_uuid": authStore.getUser().uuid, + "name":this.groupName, + "patient_uuid":uuidString + } as Record : { + "uuid": this.params.group_uuid, + "name":this.groupName, + "patient_uuid":uuidString + } as Record).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientListByGroup'+res); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '1') { + promptAction.showToast({ message:'分组成功', duration: 1000 }) + router.back(); + } else if (json.code == '2') { + promptAction.showToast({ message:'该分组已存在', duration: 1000 }) + } else { + console.error('删除患者分组列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + + + // const postContent = new rcp.MultipartForm({ + // "uuid": this.params.group_uuid, + // "name":this.groupName, + // "patient_uuid":uuidString + // }) + // const session = rcp.createSession(); + // session.post(BasicConstant.updateGroup, postContent) + // .then((response) => { + // this.dialog.close(); + // logger.info('Response patientListByGroup'+response); + // let json:Record = JSON.parse(response+'') as Record; + // if(json.code == '1') { + // promptAction.showToast({ message:'分组成功', duration: 1000 }) + // router.back(); + // } else if (json.code == '2') { + // promptAction.showToast({ message:'该分组已存在', duration: 1000 }) + // } else { + // console.error('删除患者分组列表失败:'+json.message) + // promptAction.showToast({ message: json.message, duration: 1000 }) + // } + // }) + // .catch((err: BusinessError) => { + // this.dialog.close(); + // console.error(`Response err: Code is ${JSON.stringify(err.code)}, message is ${JSON.stringify(err)}`); + // }) + } + + build() { + Row() { + Column() { + HdNav({ + title: this.params.title, + showRightIcon: false, + hasBorder: true, + rightText: '保存', + showRightText: true, + rightItemAction: () => { + if (this.params.title == '新建分组') { + this.setCreatOrEditGroup(0); + } else { + this.setCreatOrEditGroup(1); + } + } + }) + Scroll(this.scrollerCon){ + Column() { + Text('分组名称') + .fontSize(15) + .fontColor('#333333') + .margin({ left: 15 }) + .height(50) + .textAlign(TextAlign.Start) + + TextInput({placeholder:'设置分组名称',text:this.params.group_name}) + .padding({left:15}) + .width('100%') + .height(50) + .backgroundColor(Color.White) + .onChange((input:string)=>{ + this.groupName = input; + }) + + Text('分组成员') + .fontSize(15) + .fontColor('#333333') + .margin({ left: 15 }) + .height(50) + .textAlign(TextAlign.Start) + + Row(){ + Image($r('app.media.add_patients_to_roup')) + .width(50).height(50) + .margin({left:15}) + + Text('添加组患者') + .fontSize(16) + .fontColor('#333333') + .margin({left:15}) + } + .width('100%') + .height(80) + .backgroundColor(Color.White) + .onClick(()=>{ + router.pushUrl({ + url:'pages/PatientsPage/PatientsListPage', + params:{group_uuid:this.params.group_uuid,selectedPatients:this.groupPatientList} + }) + }) + + List(){ + ListItemGroup({footer:this.footerView()}) { + ForEach(this.groupPatientList,(item:patientListModel,index:number)=>{ + ListItem(){ + this.patientsListItem(item,index) + } + }) + } + } + + }.width('100%').alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.Start) + } + .width('100%').height('calc(100% - 56vp - 55vp)') + .scrollBar(BarState.Off) + .backgroundColor('#f4f4f4') + .align(Alignment.TopStart) + } + .width('100%').height('100%') + } + .height('100%') + } + + @Builder + footerView (){ + Column() { + Text('删除分组') + .fontSize(16) + .fontColor(Color.White) + .backgroundColor($r('app.color.main_color')) + .borderRadius(5) + .height(50) + .textAlign(TextAlign.Center) + .width('90%') + .onClick(()=>{ + this.hintWindowDialog.open(); + }) + .visibility(this.params.title == '新建分组'?Visibility.Hidden:Visibility.Visible) + }.width('100%') + .height(120) + .justifyContent(FlexAlign.End) + } + + @Builder + patientsListItem(item:patientListModel,index:number) { + Column() { + Row() { + Image(BasicConstant.urlImage + item.photo) + .alt($r('app.media.userPhoto_default')) + .borderRadius(6) + .width(50) + .height(50) + .margin({ left: 15 }) + Text(item.nickname ? item.nickname : item.realname) + .fontSize(16) + .fontColor('#333333') + .margin({ left: 15 }) + Blank() + Image($r('app.media.dele_patient_inThe_group')) + .width(22).height(22) + .objectFit(ImageFit.Fill) + .margin({ right: 15 }) + .onClick(()=>{ + this.groupPatientList.splice(index,1); + this.groupPatientList = [...this.groupPatientList]; + }) + } + .width('100%') + .height(80) + .backgroundColor(Color.White) + Blank() + .width('80%') + .height(1) + .backgroundColor(Color.Gray) + .margin({left:60}) + } + } +} diff --git a/features/patient/src/main/ets/components/MainPage.ets b/features/patient/src/main/ets/components/MainPage.ets new file mode 100644 index 0000000..9de5eb3 --- /dev/null +++ b/features/patient/src/main/ets/components/MainPage.ets @@ -0,0 +1,19 @@ +@Component +export struct MainPage { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize($r('app.float.page_text_font_size')) + .fontWeight(FontWeight.Bold) + .onClick(() => { + this.message = 'Welcome'; + }) + } + .width('100%') + } + .height('100%') + } +} diff --git a/features/patient/src/main/ets/components/PatientApplyPage.ets b/features/patient/src/main/ets/components/PatientApplyPage.ets new file mode 100644 index 0000000..84c8b66 --- /dev/null +++ b/features/patient/src/main/ets/components/PatientApplyPage.ets @@ -0,0 +1,222 @@ +import { ApplyViews } from '../views/ApplyViews' +import { authStore, HdNav } from '@itcast/basic'; +import { applyListCallBacl,applyListModel,applyHistoryCallBacl,historyModel } from '../models/ApplyModel' +import HashMap from '@ohos.util.HashMap'; +import { HdLoadingDialog } from '@itcast/basic' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import { promptAction, router } from '@kit.ArkUI' +import { patientDbManager, PatientData } from '@itcast/basic'; +import { PullToRefreshLayout, RefreshController } from 'refreshlib' + +interface extraData { + expertUuid: string, + page: number, + uuid: string, + status: string +} + +@Component +export struct PatientApplyPage { + public controller:RefreshController = new RefreshController(); + scroller = new Scroller(); + @State applyArray:applyListModel[] = []; + @State historyArray:historyModel[] = []; + @State pageNumber:number = 1; + @State totalPageNumer:number = 1; + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + aboutToAppear(): void { + this.getApplyList(); + this.getHistoryApplyList(); + } + + getHistoryApplyList() { + const hashMap: HashMap = new HashMap(); + hashMap.set('page',this.pageNumber.toString()); + this.dialog.open() + hdHttp.httpReq(BasicConstant.relationRecordLately,hashMap).then(async (res: HdResponse) => { + this.dialog.close(); + this.controller.refreshSuccess(); + this.controller.loadSuccess(); + logger.info('Response relationRecordLately'+res); + let json:applyHistoryCallBacl = JSON.parse(res+'') as applyHistoryCallBacl; + if(json.code == 200) { + this.historyArray = json.data.list; + this.totalPageNumer = Number(json.data.total); + } else { + console.error('患者申请记录列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + this.controller.refreshError(); + console.info(`Response fails: ${err}`); + }) + } + + getApplyList() { + this.dialog.open() + hdHttp.post(BasicConstant.applyList, { + expertUuid: authStore.getUser().uuid, + } as extraData).then(async (res: HdResponse) => { + this.dialog.close(); + this.controller.refreshSuccess(); + this.controller.loadSuccess(); + logger.info('Response applyList'+res); + let json:applyListCallBacl = JSON.parse(res+'') as applyListCallBacl; + if(json.code == 1) { + this.applyArray = json.data; + } else { + console.error('新的患者列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + this.controller.refreshError(); + console.info(`Response fails: ${err}`); + }) + } + + getApplyListOperate(status: string,model:applyListModel) { + this.dialog.open() + hdHttp.post(BasicConstant.applyListOperate, { + uuid: model.uuid, + status: status + } as extraData).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response applyListOperate'+res); + let json:applyListCallBacl = JSON.parse(res+'') as applyListCallBacl; + if(json.code == 1) { + if (status == '2') { + this.getApplyList(); + // 添加单个患者 + const singlePatient: PatientData = { + uuid:model.patientUuid as string, + nickname: '', + mobile: model.mobile as string, + realName: model.realName as string, + nation: '', + sex: model.sex as number, + type: 1, + photo: model.photo as string, + expertUuid: authStore.getUser().uuid + }; + const success1 = await patientDbManager.addPatient(singlePatient); + if (success1) { + console.info('添加成功'); + const patients = await patientDbManager.getAllPatients(); + promptAction.showToast({message:`现在一共是 ${patients.length} 个患者`}) + } else { + console.info('添加失败'); + } + promptAction.showToast({ message: '消息已处理', duration: 1000 }) + router.pushUrl({ + url:'pages/PatientsPage/PatientMsgSetPage', + params:{ 'model':model } + }) + } else if (status == '3') { + this.pageNumber = 1; + this.getApplyList(); + this.getHistoryApplyList(); + } + } else { + console.error('患者列表申请处理失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.info(`Response fails: ${err}`); + }) + } + + build() { + Column(){ + HdNav({ title: '新的患者', showRightIcon: false, hasBorder: true }) + PullToRefreshLayout({ + scroller:this.scroller, + viewKey:"ListPage", + controller:this.controller, + contentView:()=>{ + this.contentView() + }, + onRefresh:()=>{ + this.pageNumber = 1; + this.getApplyList(); + this.getHistoryApplyList(); + }, + onCanPullRefresh:()=>{ + if (!this.scroller.currentOffset()) { + /*处理无数据,为空的情况*/ + return true + } + //如果列表到顶,返回true,表示可以下拉,返回false,表示无法下拉 + return this.scroller.currentOffset().yOffset <= 0 + }, + onLoad:()=>{ + this.pageNumber++; + this.getApplyList(); + this.getHistoryApplyList(); + }, + onCanPullLoad: () => { + if (this.pageNumber >= this.totalPageNumer) { + return false; + } else { + return true; + } + } + }).width('100%').height('calc(100% - 156vp)').clip(true) + + Row(){ + Text('加患者') + .fontSize(16) + .fontColor(Color.White) + .backgroundColor('rgb(63,199,193)') + .width('100%').height(50).textAlign(TextAlign.Center) + .onClick(()=>{ + router.pushUrl({ + url: 'pages/WebView/WebPage', // 目标url + params: {url:BasicConstant.wxUrl+'expert/expertcodeimg?expert_uuid='+authStore.getUser().uuid,title:'我的二维码'} + }) + }) + }.width('100%').height(56).backgroundColor(Color.White).alignItems(VerticalAlign.Top) + }.width('100%').height('100%') + } + + @Builder + contentView(){ + Column(){ + Row({space:5}){ + Image($r('app.media.addPatientApply_reminder_icon')) + .width(18).height(21) + Text('提醒: 为了避免不必要的纠纷,请您务必选择线下就诊过的患者') + .fontSize(16).fontColor('#666666') + }.width('100%').padding({left:10,top:10,right:20,bottom:10}).backgroundColor(Color.White) + if (this.applyArray.length > 0) { + Column(){ + Text('随访申请') + .fontSize(15).fontColor('#333333').margin({left:10}).height(42) + ForEach(this.applyArray,(item:applyListModel)=>{ + ApplyViews({applyItme:item,isApply:true,applyItemAction:((status: string,model:applyListModel)=>{ + this.getApplyListOperate(status,model) + })}) + }) + }.width('100%').alignItems(HorizontalAlign.Start) + } + if (this.historyArray.length > 0) { + Column(){ + Text('申请记录(近一月)') + .fontSize(15).fontColor('#333333').margin({left:10}).height(42) + ForEach(this.historyArray,(item:historyModel)=>{ + ApplyViews({historyItem:item,isApply:false}) + }) + }.width('100%').alignItems(HorizontalAlign.Start) + } + }.width('100%').height('100%').backgroundColor('#f4f4f4') + } +} diff --git a/features/patient/src/main/ets/components/PatientDetailsComp.ets b/features/patient/src/main/ets/components/PatientDetailsComp.ets new file mode 100644 index 0000000..e6d037f --- /dev/null +++ b/features/patient/src/main/ets/components/PatientDetailsComp.ets @@ -0,0 +1,198 @@ +import { authStore, HdNav, PositionSelectedSheet } from '@itcast/basic'; +import { promptAction, router } from '@kit.ArkUI' +import { HdLoadingDialog,DefaultHintProWindows } from '@itcast/basic' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import HashMap from '@ohos.util.HashMap'; +import { patientListModel } from '../models/PatientsGroupModel' +import measure from '@ohos.measure'; + +@Component +export struct PatientDetailsComp { + scroller:Scroller = new Scroller() + @Consume@Watch('onRefreshAction') refreshFlag: boolean + @State params:Record = router.getParams() as Record + @State groupArray:Array> = [] + @State footerArray:Array> = [] + @State patientCase:Array> = [] + @State patientData:Record = {} + @State patientData2:Record = {} + @State medicalHistoryContent:string = '' + @State isExpanded: boolean = false; // 展开状态 + @State showExpandBtn: boolean = false; // 是否显示操作按钮 + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + onRefreshAction() { + this.getPatientCardData() + } + + aboutToAppear(): void { + this.getPatientCardData() + this.footerArray = [{"img":$r('app.media.sendMessage_blackBtn'),"title":"发消息"},{"img":$r('app.media.fuifangPlan_blackBtn'),"title":"制定随访计划"},{"img":$r('app.media.listBing_blackBtn'),"title":"记录病情"}] + } + + getPatientCardData(){ + const hashMap: HashMap = new HashMap(); + hashMap.set('patient_uuid',String(this.params.patient_uuid)); + this.dialog.open() + hdHttp.httpReq(BasicConstant.patientCard,hashMap).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientCard'+res); + let json:Record = JSON.parse(res+'') as Record + const isFriend = String(json.isFriend) + if (isFriend == '0') { + promptAction.showToast({ message: '随访关系已解除', duration: 1000 }) + router.back() + } else { + if(json.code == '200') { + this.getPatientDetailsData(String(json.group["name"])) + } else { + console.error('患者详情请求失败:'+json.message) + } + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.info(`Response fails: ${err}`); + }) + } + + getPatientDetailsData(groupType:string){ + this.dialog.open() + hdHttp.post(BasicConstant.toAddNickname, { + "expertUuid": authStore.getUser().uuid, + "patientUuid":String(this.params.patient_uuid) + } as Record).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response toAddNickname'+res); + let json:Record> = JSON.parse(res+'') as Record>; + if(json.code == '1') { + this.getPatientData() + this.patientData = json.patientEx as Record + let nickname = this.patientData.nickname + let note = this.patientData.note + let mobile = this.patientData.mobile + if (nickname.length>0) { + this.groupArray = [{"title":"备注","content":String(nickname),"prompt":"给患者添加备注名"},{"title":"分组","content":String(groupType),"prompt":"通过分组给患者分类"},{"title":"描述","content":String(note),"prompt":"补充患者关键信息,方便随访患者"},{"title":"电话号码","content":String(mobile),"prompt":""}] + } else { + this.groupArray = [{"title":"分组","content":String(groupType),"prompt":"通过分组给患者分类"},{"title":"描述","content":String(note),"prompt":"补充患者关键信息,方便随访患者"},{"title":"电话号码","content":String(mobile),"prompt":""}] + } + } else { + console.error('获取患者信息失败:'+json.message) + promptAction.showToast({ message: String(json.message), duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + } + + getPatientData() { + this.dialog.open() + hdHttp.post(BasicConstant.patientDetail, { + "patientUuid":String(this.params.patient_uuid) + } as Record).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientDetail'+res); + let json:Record | Array>> = JSON.parse(res+'') as Record | Array>>; + if(json.code == '1') { + this.patientData2 = json.data as Record + this.medicalHistoryContent = String(json.medicalHistoryContent) + this.patientCase = json.patientCase as Array> + } else { + console.error('获取患者信息失败:'+json.message) + promptAction.showToast({ message: String(json.message), duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + } + + build() { + Row() { + Column() { + HdNav({ + title: '患者详情', + showRightIcon: true, + hasBorder: true, + rightIcon:$r("app.media.patient_details_navigation_right"), + showRightText: false, + rightItemAction: () => { + router.pushUrl({ + url: 'pages/PatientsPage/BuildOrEditGroupPage', + params:{"title":"新建分组"} + }) + } + }) + Scroll(this.scroller){ + this.historyView() + this.footerView() + }.width('100%').height('calc(100% - 56vp)').backgroundColor('#f4f4f4') + .scrollBar(BarState.Off) + } + .width('100%').height('100%') + } + .height('100%') + } + + @Builder + historyView(){ + Column({space:10}){ + Text('患者病史') + .fontSize(15) + .fontColor('#333333') + + Text(this.medicalHistoryContent) + .fontSize(16) + .lineHeight(22) + .maxLines(this.isExpanded ? 0 : 2) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + .onAreaChange((_, area) => { + let fullHeight = measure.measureTextSize({ + textContent: this.medicalHistoryContent, + fontSize: 15, + maxLines:2 + }).height; + this.showExpandBtn = Number(area.height) < Number(fullHeight); + }) + // 操作按钮(独立可点击区域) + if (this.showExpandBtn) { + Text(this.isExpanded ? "...收起" : "..展开全部") + .fontSize(15) + .fontColor($r('app.color.main_color')) // 红色标识可点击 + .onClick(() => { + this.isExpanded = !this.isExpanded; // 切换状态 + }) + } + } + .alignItems(HorizontalAlign.Start) + .backgroundColor(Color.White) + .width('100%') + .padding(15) + } + + @Builder + footerView(){ + List(){ + ForEach(this.footerArray,(item:Record,index:number)=>{ + ListItem(){ + Row(){ + Image(item.img) + .width(20).height(20) + Text(item.title) + .fontSize(15) + } + .height(49) + .width('100%') + .justifyContent(FlexAlign.Center) + .backgroundColor(Color.White) + }.width('100%').height(50) + }) + } + } +} diff --git a/features/patient/src/main/ets/components/PatientSetMsgPage.ets b/features/patient/src/main/ets/components/PatientSetMsgPage.ets new file mode 100644 index 0000000..09c046e --- /dev/null +++ b/features/patient/src/main/ets/components/PatientSetMsgPage.ets @@ -0,0 +1,197 @@ +import { authStore, HdNav } from '@itcast/basic'; +import { applyListModel } from '../models/ApplyModel' +import HashMap from '@ohos.util.HashMap'; +import { HdLoadingDialog } from '@itcast/basic' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import { Font, promptAction, router } from '@kit.ArkUI' +import { patientDbManager, PatientData } from '@itcast/basic'; + +interface callBackData { + code: string, + data: object, + msg: string, + message: string +} + +interface paramsCallData { + model:applyListModel; +} + +@Component +export struct PatientSetMsgPage { + @State params:paramsCallData = router.getParams() as paramsCallData; + @State noteName: string | undefined = ''; + @State contentFrist:string | undefined = ''; + @State groupName: string = '通过分组给患者分类'; + @State maxDescribe:string = '0'; + @State descibe:string = ''; + @State isNote:boolean = false; + @State isDescibe:boolean = false; + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + aboutToAppear(): void { + this.contentFrist = getFirstSegment(this.params.model.content) + } + + patientMsgSubmit(){ + const hashMap: HashMap = new HashMap(); + hashMap.set('patient_uuid',this.params.model.patientUuid); + hashMap.set('nickname',this.noteName); + hashMap.set('note',this.descibe); + this.dialog.open() + hdHttp.httpReq(BasicConstant.updateNicknameNote,hashMap).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response relationRecordLately'+res); + let json:callBackData = JSON.parse(res+'') as callBackData; + if(json.code == '200') { + // 添加单个患者 + const singlePatient: PatientData = { + uuid:this.params.model.patientUuid as string, + nickname: this.noteName as string, + mobile: this.params.model.mobile as string, + realName: this.params.model.realName as string, + nation: '', + sex: this.params.model.sex as number, + type: 1, + photo: this.params.model.photo as string, + expertUuid: authStore.getUser().uuid + }; + const success1 = await patientDbManager.addPatient(singlePatient); + if (success1) { + console.info('添加成功'); + const patients = await patientDbManager.getAllPatients(); + promptAction.showToast({message:`现在一共是 ${patients.length} 个患者`}) + } else { + console.info('添加失败'); + } + promptAction.showToast({ message: '设置成功', duration: 1000 }) + router.back() + } else { + console.error('患者申请记录列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.info(`Response fails: ${err}`); + }) + } + + build() { + Column() { + HdNav({ title: '设置备注和分组', showRightIcon: false, hasBorder: true }) + + Text('备注') + .margin({left:15,top:15}) + .fontSize(15).fontColor('#333333') + + TextInput({placeholder:'给患者添加备注名',text:this.noteName}) + .fontSize(15).fontColor('#333333').placeholderColor('#999999') + .padding({left:10,right:10}) + .width('calc(100% - 30vp)') + .height(50) + .backgroundColor('#f4f4f4') + .borderRadius(4) + .maxLength(20) + .margin({left:15,top:10}) + .onChange((value:string)=>{ + this.noteName = value; + if (value.length > 0) { + this.isNote = true; + }else { + this.isNote = false; + } + }) + + Row() { + Text('申请消息为:'+this.contentFrist) + .fontSize(15).fontColor('#666666') + Text('填入') + .fontSize(15).fontColor('#3CC7C0') + }.justifyContent(FlexAlign.Start).margin({left:15,top:5}) + // .visibility(this.contentFrist?Visibility.Visible:Visibility.Hidden) + .onClick(()=>{ + this.noteName = this.contentFrist?.substring(2); + }) + + Text('分组') + .margin({left:15,top:15}) + .fontSize(15).fontColor('#333333') + + Row(){ + Text(this.groupName) + .fontSize(15).fontColor(this.groupName.includes('通过分组给患者分类')?'#999999':'#333333') + .margin({left:10}) + .layoutWeight(1) + Image($r('sys.media.ohos_ic_public_arrow_right')) + .width(15).height(15).margin({right:10}) + } + .height(50) + .width('calc(100% - 30vp)') + .backgroundColor('#f4f4f4') + .borderRadius(4) + .margin({left:15,top:10}) + .onClick(()=>{ + + }) + + Text('描述') + .margin({left:15,top:15}) + .fontSize(15).fontColor('#333333') + + Column() { + TextArea({ placeholder: '补充患者关键信息,方便随访患者' }) + .fontSize(15).placeholderColor('#999999') + .fontColor('#333333') + .backgroundColor('#f4f4f4') + .borderRadius(4) + .maxLength(100) + .padding({ + left: 10, + top: 10, + right: 10, + bottom: 10 + }) + .width('100%').height(100) + .onChange((value: string) => { + this.maxDescribe = value.length.toString(); + this.descibe = value; + if (value.length > 0) { + this.isDescibe = true; + } else { + this.isDescibe = false; + } + }) + Text('已输入'+this.maxDescribe+'/100') + .fontSize(13).fontColor('#cccccc').textAlign(TextAlign.End).width('90%') + .margin({top:-20}) + } + .margin({ left: 15,top: 10,}).width('calc(100% - 30vp)').height(120).borderRadius(4) + .layoutWeight(1) + + Text('完成') + .width('100%').height(45) + .fontSize(18).fontColor(Color.White).textAlign(TextAlign.Center) + .backgroundColor(this.isNote||this.isDescibe?'#3CC7C0':'#cccccc') + .margin({bottom:10}) + .onClick(()=>{ + if (this.isNote || this.isDescibe) { + this.patientMsgSubmit(); + } + }) + + }.justifyContent(FlexAlign.Start).alignItems(HorizontalAlign.Start) + .height('100%') + } +} + +function getFirstSegment(content?: string): string | undefined { + if (!content) return undefined; + const segments: string[] = content.split(/[\uFF0C,]/); + return segments.find(seg => seg.trim() !== ''); +} \ No newline at end of file diff --git a/features/patient/src/main/ets/components/PatientsGroup.ets b/features/patient/src/main/ets/components/PatientsGroup.ets new file mode 100644 index 0000000..c6f1a9f --- /dev/null +++ b/features/patient/src/main/ets/components/PatientsGroup.ets @@ -0,0 +1,315 @@ +import { authStore, HdNav, PositionSelectedSheet } from '@itcast/basic'; +import { promptAction, router } from '@kit.ArkUI' +import { HdLoadingDialog } from '@itcast/basic' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import HashMap from '@ohos.util.HashMap'; +import { groupRequest,groupRequestCall,groupModel,patientListModel } from '../models/PatientsGroupModel' + +@Component +export struct PatientsGroup { + @State groupSort: string = '分组排序' + @State innerSort: string = '组内排序' + @State groupSortList:sortModel[] = []; + @State innerSortList:sortModel[] = []; + @State groupSortSelected: boolean = false//是否展开 + @State innerSortSelected: boolean = false//是否展开 + @State isGroupSelected: boolean = false//是否选中 + @State IsInnerSelected: boolean = false//是否选中 + @State isMaiLanHidden:boolean = false;//麦兰项目是否显示 + @State group_sort:string = '0' + @State list_sort:string = '0' + @State groupListArray: groupModel[] = []; + @Consume@Watch('onRefreshAction') refreshFlag: boolean; + + onRefreshAction(flag: boolean) { + this.getGroupData(); + } + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + aboutToAppear(): void { + this.getIsMaiLanData(); + this.groupSortList = [{"name":"按首字母","isSeleted":false} as sortModel,{"name":"分组人数","isSeleted":false} as sortModel] + this.innerSortList = [{"name":"按首字母","isSeleted":false} as sortModel,{"name":"随访时间","isSeleted":false} as sortModel] + } + + getGroupData(){ + this.dialog.open() + hdHttp.post(BasicConstant.groupList, { + expert_uuid: authStore.getUser().uuid, + group_sort:this.group_sort, + list_sort:this.list_sort + } as groupRequest).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response groupList'+res); + let json:groupRequestCall = JSON.parse(res+'') as groupRequestCall; + if(json.code == 1) { + this.groupListArray = [] + this.groupListArray = json.data + } else { + console.error('患者分组列表失败:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.error(`Response fails: ${err}`); + }) + } + + getIsMaiLanData() { + const hashMap: HashMap = new HashMap(); + this.dialog.open() + hdHttp.httpReq(BasicConstant.isMaiLanExpert,hashMap).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response isMaiLanExpert'+res); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '200') { + let isMaiLanExpert:string = json.isMaiLanExpert; + if (isMaiLanExpert == '1') { + this.isMaiLanHidden = true; + } + } else { + console.error('麦兰:'+json.message) + promptAction.showToast({ message: json.message, duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.info(`Response fails: ${err}`); + }) + } + + build() { + Column() { + HdNav({ + title: '患者分组', + showRightIcon: false, + hasBorder: true, + rightText: '新建', + showRightText: true, + rightItemAction: () => { + router.pushUrl({ + url: 'pages/PatientsPage/BuildOrEditGroupPage', + params:{"title":"新建分组"} + }) + } + }) + Stack() { + Row() { + Row() { + Text(this.groupSort) + .fontSize(16).fontColor(this.isGroupSelected ? $r('app.color.main_color') : '#333333') + Image(this.isGroupSelected ?$r('app.media.triangle_green_theme'):$r('app.media.triangle_normal')).width(10).height(10) + } + .width('50%') + .height(48) + .backgroundColor(Color.White) + .justifyContent(FlexAlign.Center) + .onClick(() => { + this.groupSortSelected = !this.groupSortSelected + this.innerSortSelected = false + }) + + Blank() + .width(1).height(20).margin({ top: 15 }) + Row() { + Text(this.innerSort) + .fontSize(16).fontColor(this.IsInnerSelected ? $r('app.color.main_color') : '#333333') + Image(this.IsInnerSelected ?$r('app.media.triangle_green_theme'):$r('app.media.triangle_normal')).width(10).height(10) + } + .width('50%') + .height(48) + .backgroundColor(Color.White) + .justifyContent(FlexAlign.Center) + .onClick(() => { + this.innerSortSelected = !this.innerSortSelected + this.groupSortSelected = false + }) + }.width('100%').height(55).backgroundColor('#f4f4f4') + + List() { + ForEach(this.groupListArray, (sectionModel: groupModel,index:number) => { + ListItemGroup({ header: this.itemHeaderView(sectionModel,index) }) { + ForEach(sectionModel.isShow ? sectionModel.patientList : [], (rowModel: patientListModel) => { + ListItem() { + Stack() { + Row({ space: 15 }) { + Image(BasicConstant.urlImage + rowModel.photo) + .alt($r('app.media.userPhoto_default')) + .width(54) + .height(54) + .borderRadius(6) + .margin({ left: 15 }) + Text(rowModel.nickname ? rowModel.nickname : rowModel.realName) + .fontSize(16).fontColor('#666666') + if (Number(rowModel.type) === 0) { + Image($r('app.media.group_vip')) + .objectFit(ImageFit.Cover) + .width(10).height(10) + } + }.width('100%').height(80) + + Text('随访于' + rowModel.join_date?.substring(0, 10)) + .fontSize(14) + .fontColor('#999999') + .textAlign(TextAlign.End) + .margin({ right: 15 }) + .height(30) + Row() + .width('95%').height(0.5) + .backgroundColor('#999999') + }.width('100%').height(80).alignContent(Alignment.BottomEnd) + .onClick(()=>{ + router.pushUrl({ + url:'pages/PatientsPage/PatientDetailsPage', + params:{"patient_uuid":rowModel.uuid} + }) + }) + }.width('100%') + }) + } + }) + } + .width('100%') + .height('calc(100% - 55vp - 56vp)') + .backgroundColor('#f4f4f4') + .scrollBar(BarState.Off) + .sticky(StickyStyle.Header) + .margin({top:55}) + + List() { + ForEach(this.groupSortList, (item: sortModel) => { + ListItem() { + Column() { + Row() { + Text(item.name) + .fontSize(16) + .fontColor(item.isSeleted ? $r('app.color.main_color') : 'rgba(144,144,144)') + .margin({ left: 20 }) + Blank() + if (item.isSeleted) { + Image($r('app.media.chose_card')) + .width(20).height(20).margin({ right: 25 }) + } + }.width('100%').height(50).backgroundColor(Color.White) + + Blank() + .width('100%').height(1).backgroundColor($r('app.color.main_color')).margin({left:20}) + }.onClick(()=>{ + this.isGroupSelected = true; + this.innerSortSelected = false + this.groupSortSelected = false + this.groupSort = String(item.name); + this.group_sort = item.name == '按首字母' ? '0' : '1' + this.groupSortList.forEach((element: sortModel) => { + element.isSeleted = false + }) + const indexof = this.groupSortList.indexOf(item) + if (indexof !== -1) { + this.groupSortList[indexof].isSeleted = true + } + this.groupSortList = [...this.groupSortList] + this.getGroupData() + }) + } + }) + }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({top:55}) + .visibility(this.groupSortSelected?Visibility.Visible:Visibility.Hidden) + + List() { + ForEach(this.innerSortList, (item: sortModel) => { + ListItem() { + Column() { + Row() { + Text(item.name) + .fontSize(16) + .fontColor(item.isSeleted ? $r('app.color.main_color') : 'rgba(144,144,144)') + .margin({ left: 20 }) + Blank() + if (item.isSeleted) { + Image($r('app.media.chose_card')) + .width(20).height(20).margin({ right: 25 }) + } + }.width('100%').height(50).backgroundColor(Color.White) + + Blank() + .width('95%').height(1).backgroundColor($r('app.color.main_color')) + } + .onClick(()=>{ + this.IsInnerSelected = true + this.innerSortSelected = false + this.groupSortSelected = false + this.innerSort = String(item.name); + this.list_sort = item.name == '按首字母' ? '0' : '1' + this.innerSortList.forEach((element: sortModel) => { + element.isSeleted = false + }); + const indexof = this.innerSortList.indexOf(item) + if (indexof !== -1) { + this.innerSortList[indexof].isSeleted = true + } + this.innerSortList = [...this.innerSortList] + this.getGroupData() + }) + } + }) + }.width('100%').height('calc(100% - 55vp)').backgroundColor('rgba(0,0,0,0.5)').margin({top:55}) + .visibility(this.innerSortSelected?Visibility.Visible:Visibility.Hidden) + + Image($r('app.media.lifetime_right_icon')) + .width(76).height(40) + .position({ x: '80%', y: '80%' }) // 定位到右下角 + .visibility(this.isMaiLanHidden?Visibility.Visible:Visibility.Hidden) + + }.width('100%').height('calc(100% - 56vp)').backgroundColor('#f4f4f4').alignContent(Alignment.TopStart) + } + } + + @Builder + itemHeaderView(model:groupModel,index:number) { + Column() { + Row() { + Image(model.isShow ? $r('app.media.group_turnDown') : $r('app.media.group_turnRight')) + .width(model.isShow ? 10 : 5).height(model.isShow ? 5 : 10).margin({ left: 15 }) + Text(model.name + ' | ' + model.patientNum) + .fontSize(16) + .fontColor('#333333') + .margin({ left: 15 }) + .layoutWeight(1) + Text('编辑') + .width(60) + .height(60) + .fontSize(15) + .fontColor('#981308') + .margin({ right: 15 }) + .textAlign(TextAlign.End) + .visibility(model.name != '待分组患者'?Visibility.Visible:Visibility.Hidden) + .onClick(()=>{ + router.pushUrl({ + url: 'pages/PatientsPage/BuildOrEditGroupPage', + params:{"title":"编辑分组","group_uuid":model.uuid,"group_name":model.name} + }) + }) + } + .width('100%') + .height(60) + .onClick(() => { + let newModel = new groupModel(model); + newModel.isShow = !model.isShow; + this.groupListArray[index] = newModel; + this.groupListArray = [...this.groupListArray]; + }) + Blank() + .width('100%').height(1).backgroundColor('#666666') + }.width('100%').height(61).backgroundColor(Color.White) + } +} + +export class sortModel { + name?:string; + isSeleted:boolean = false; +} \ No newline at end of file diff --git a/features/patient/src/main/ets/components/PatientsListComp.ets b/features/patient/src/main/ets/components/PatientsListComp.ets new file mode 100644 index 0000000..4647a7d --- /dev/null +++ b/features/patient/src/main/ets/components/PatientsListComp.ets @@ -0,0 +1,179 @@ +import { authStore, ChangeUtil, HdNav, PositionSelectedSheet, EmptyViewComp,HdLoadingDialog } from '@itcast/basic'; +import { promptAction, router } from '@kit.ArkUI' +import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' +import { BusinessError } from '@kit.BasicServicesKit'; +import HashMap from '@ohos.util.HashMap'; +import { patientListModel } from '../models/PatientsGroupModel' + +@Component +export struct PatientsListComp { + @State params:Record = router.getParams() as Record + @State patientsArray:patientListModel[] = [] + @State patientsList:patientListModel[] = [] + @State inputString:string = '' + @State naviRightTitle:string = '确定(0)' + @State isEmptyViewVisible: boolean = false; // 控制显隐的状态变量 + + aboutToAppear(): void { + this.getPatientsListData(); + } + + dialog: CustomDialogController = new CustomDialogController({ + builder: HdLoadingDialog({ message: '加载中...' }), + customStyle: true, + alignment: DialogAlignment.Center + }) + + getPatientsListData() { + const hashMap: HashMap = new HashMap(); + hashMap.set('group_uuid',String(this.params.group_uuid)); + this.dialog.open() + hdHttp.httpReq(BasicConstant.patientListNoInThisGroup,hashMap).then(async (res: HdResponse) => { + this.dialog.close(); + logger.info('Response patientListNoInThisGroup'+res); + let json:Record = JSON.parse(res+'') as Record; + if(json.code == '1') { + const patientsList = this.params?.selectedPatients as patientListModel[] | undefined; + if (patientsList?.length) { + const uuidSet = new Set(patientsList.map(item => item.uuid)); + const dataArray = json.data as patientListModel[]; + for (const model of dataArray) { + if (!uuidSet.has(model.uuid)) { + this.patientsList.push(model); + this.patientsArray.push(model); + } + } + } else { + this.patientsList = json.data as patientListModel[]; + this.patientsArray = json.data as patientListModel[]; + } + this.isEmptyViewVisible = this.patientsList.length>0?false:true + } else { + console.error('分组患者列表失败:'+json.message) + promptAction.showToast({ message: String(json.message), duration: 1000 }) + } + }).catch((err: BusinessError) => { + this.dialog.close(); + console.info(`Response fails: ${err}`); + }) + } + + searchPatientAction(){ + if (this.inputString.length > 0) { + this.patientsList = [] + for (const model of this.patientsArray) { + if (model.realname?.includes(this.inputString) || model.mobile?.includes(this.inputString) || model.nickname?.includes(this.inputString)) { + this.patientsList.push(model) + } + } + } else { + this.patientsList = this.patientsArray + } + this.isEmptyViewVisible = this.patientsList.length>0?false:true + } + + build() { + Column() { + HdNav({showLeftIcon:true,title:'选择患者',rightText:this.naviRightTitle,showRightText:true,rightTextColor:Color.White,rightBackColor:$r('app.color.main_color'),showRightIcon:false,rightItemAction:()=>{ + router.back({ + url:'pages/PatientsPage/BuildOrEditGroupPage', + params:{'selectedPatients':this.patientsList} + }) + }}) + + Row(){ + Row(){ + TextInput({placeholder:'搜索患者的备注名、昵称或手机号'}) + .fontSize(15) + .backgroundColor(Color.White) + .layoutWeight(1) + .onChange((value:string)=>{ + this.inputString = value + }) + .onSubmit(()=>{ + this.searchPatientAction() + }) + Blank() + .width(0.5) + .height(20) + .backgroundColor($r('app.color.main_color')) + Image($r('app.media.selected_hospital_ws')) + .width(30) + .height(30) + .margin({left:10,right:10}) + .onClick(()=>{ + this.searchPatientAction() + }) + } + .width('95%') + .height(50) + .borderRadius(5) + .borderWidth(1) + .margin({left:10}) + .borderColor($r('app.color.main_color')) + } + .backgroundColor(Color.White) + .width('100%') + .height(70) + + if (this.isEmptyViewVisible) { + EmptyViewComp({promptText:'无搜索结果',isVisibility:this.isEmptyViewVisible}) + .width('100%') + .height('calc(100% - 56vp - 70vp)') + } else { + List(){ + ForEach(this.patientsList,(model:patientListModel)=>{ + ListItem() { + this.patientListItem(model) + } + }) + } + .width('100%') + .height('calc(100% - 56vp - 70vp)') + .backgroundColor('#f4f4f4') + .scrollBar(BarState.Off) + } + } + .width('100%') + .height('calc(100% - 56vp)') + .backgroundColor('#f4f4f4') + .justifyContent(FlexAlign.Start) + } + + @Builder + patientListItem(item:patientListModel) { + Column() { + Row() { + Image(BasicConstant.urlImage + item.photo) + .alt($r('app.media.userPhoto_default')) + .borderRadius(6) + .width(50) + .height(50) + .margin({ left: 15 }) + Text(item.nickname ? item.nickname : item.realname) + .fontSize(16) + .fontColor('#333333') + .margin({ left: 15 }) + Blank() + Image(item.isSelected?$r('app.media.patiemts_list_selected'):$r('app.media.patients_list_noSelect')) + .width(22).height(22) + .objectFit(ImageFit.Fill) + .margin({ right: 15 }) + } + .width('100%') + .height(80) + .backgroundColor(Color.White) + .onClick(()=>{ + item.isSelected = !item.isSelected; + this.patientsList = [...this.patientsList]; + const selectedNum = this.patientsList.filter(item => item.isSelected == true).length; + this.naviRightTitle = '确定('+selectedNum+')' + }) + Blank() + .width('80%') + .height(1) + .backgroundColor(Color.Gray) + .margin({left:60}) + } + } +} diff --git a/features/patient/src/main/ets/models/ApplyModel.ets b/features/patient/src/main/ets/models/ApplyModel.ets new file mode 100644 index 0000000..366fb71 --- /dev/null +++ b/features/patient/src/main/ets/models/ApplyModel.ets @@ -0,0 +1,47 @@ +export interface applyListCallBacl { + code:number, + msg:string, + data:applyListModel[], + message:string, +} + +export interface applyHistoryCallBacl { + code:number, + msg:string, + data:historyObjectModel, + message:string, +} + +export class applyListModel { + mobile?:string; + photo?:string; + birthDate?:string; + sex?:number; + realName?:string; + checkDate?:string; + uuid?:string; + createDate?:string; + patientUuid?:string; + expertUuid?:string; + status?:number; + content?:string; +} + +export interface historyObjectModel { + list:historyModel[]; + isFirstPage?:string; + isLastPage?:string; + pageNum?:string; + pages?:string; + pageSize?:string; + total?:string; +} + +export class historyModel { + status?:string;//审核状态(1.待审核2.审核通过3.拒绝4.已过期 5.患者取消 6专家解除) + patient_photo?:string; + create_date?:string; + content?:string; + nickname?:string; + patient_name?:string; +} \ No newline at end of file diff --git a/features/patient/src/main/ets/models/PatientsGroupModel.ets b/features/patient/src/main/ets/models/PatientsGroupModel.ets new file mode 100644 index 0000000..fdcbdc2 --- /dev/null +++ b/features/patient/src/main/ets/models/PatientsGroupModel.ets @@ -0,0 +1,62 @@ +export interface groupRequest { + expert_uuid:string, + group_sort:string, + list_sort:string, +} + +export interface groupRequestCall { + code:number, + msg:string, + data:groupModel[], + message:string, +} + +export class groupModel { + patientList:patientListModel[] = [] + patientNum:number = 0 + expert_uuid:string = '' + name:string = '' + type:number = 0 + uuid:string = '' + isShow:boolean = false; + + constructor(data: groupModel) { + this.patientList = data.patientList + this.patientNum = data.patientNum + this.expert_uuid = data.expert_uuid + this.name = data.name + this.type = data.type + this.uuid = data.uuid + this.isShow = data.isShow + } +} + +export class patientListModel { + nickname?:string; + is_start?:string; + join_date?:string; + note?:string; + type?:string; + photo?:string; + birthDate?:string; + uuid?:string; + isEnable?:string; + height?:string; + ctdidId?:string; + mobile?:string; + nation?:string; + bloodType?:string; + fixedTelephone?:string; + mailingAddress?:string; + postalCode?:string; + detailed_address?:string; + weight?:string; + diagnosis?:string; + sex?:string; + provId?:string; + countyId?:string; + cityId?:string; + realName?:string; + realname?:string; + isSelected:boolean = false; +} \ No newline at end of file diff --git a/features/patient/src/main/ets/views/ApplyViews.ets b/features/patient/src/main/ets/views/ApplyViews.ets new file mode 100644 index 0000000..d00c0c7 --- /dev/null +++ b/features/patient/src/main/ets/views/ApplyViews.ets @@ -0,0 +1,72 @@ +import { applyListModel,historyModel } from '../models/ApplyModel' +import { BasicConstant } from '@itcast/basic/Index' + +@Component +export struct ApplyViews { + @Prop applyItme:applyListModel; + @Prop historyItem:historyModel; + @Prop isApply:boolean = true;//随访申请的样式还是申请记录的样式 + + private applyItemAction: (status: string,model:applyListModel) => void = () => {}; + + build() { + Row() { + Column(){ + Row({space:10}){ + Image(this.isApply?this.applyItme.photo:BasicConstant.urlImage+this.historyItem.patient_photo) + .alt($r('app.media.userPhoto_default')) + .width(50).height(50).borderRadius(5) + Column(){ + Text(this.isApply?this.applyItme.createDate:this.historyItem.create_date) + .fontSize(14).fontColor('#333333').width('100%').textAlign(TextAlign.End) + if (!this.isApply) { + Text('昵称:'+this.historyItem.patient_name) + .fontSize(16).fontColor($r('app.color.main_color')).width('100%') + } + Text(this.isApply?this.applyItme.content:this.historyItem.content) + .fontSize(16).fontColor('#333333').width('100%').margin({top:this.isApply?10:0}) + .maxLines(this.isApply?3:1).textOverflow({ overflow: TextOverflow.Ellipsis }) + }.width('calc(100% - 60vp)') + }.width('calc(100% - 20vp)').margin({top:10,bottom:5}).alignItems(VerticalAlign.Top) + if (this.isApply) { + Row({ space: 40 }) { + Text('拒绝') + .fontColor($r('app.color.main_color')) + .borderColor($r('app.color.main_color')) + .customTextStyle() + .onClick(()=>this.applyItemAction('3',this.applyItme)) + Text('同意') + .fontColor(Color.White) + .backgroundColor($r('app.color.main_color')) + .customTextStyle() + .onClick(()=>this.applyItemAction('2',this.applyItme)) + }.padding({ top: 5, bottom: 10 }).justifyContent(FlexAlign.Center).width('calc(100% - 20vp)') + } else { + Row(){ + if (this.historyItem.status == '2') { + Image($r('app.media.Patients_Apply_History_Status')).width(14).height(14) + Text('已同意').height(28).fontColor('#999999').fontSize(15) + } else if (this.historyItem.status == '3') { + Text('已拒绝').height(28).fontColor('#999999').fontSize(15) + } else if (this.historyItem.status == '4' || this.historyItem.status == '5') { + Text('已过期').height(28).fontColor('#999999').fontSize(15) + } + }.padding({ top: 5, bottom: 5 }).justifyContent(FlexAlign.End).width('calc(100% - 20vp)') + } + Blank() + .width('100%').height(1) + .backgroundColor('#f4f4f4') + }.width('100%').margin({left:10,right:10}).alignItems(HorizontalAlign.Start) + }.width('100%').backgroundColor(Color.White) + } +} + +@Extend(Text) +function customTextStyle() { + .textAlign(TextAlign.Center) + .fontSize(15) + .borderRadius(4) + .borderWidth(0.5) + .width(77) + .height(30) +} \ No newline at end of file diff --git a/features/patient/src/main/module.json5 b/features/patient/src/main/module.json5 new file mode 100644 index 0000000..bc35643 --- /dev/null +++ b/features/patient/src/main/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "patient", + "type": "har", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ] + } +} diff --git a/features/patient/src/main/resources/base/element/float.json b/features/patient/src/main/resources/base/element/float.json new file mode 100644 index 0000000..a0a93dd --- /dev/null +++ b/features/patient/src/main/resources/base/element/float.json @@ -0,0 +1,8 @@ +{ + "float": [ + { + "name": "page_text_font_size", + "value": "50fp" + } + ] +} \ No newline at end of file diff --git a/features/patient/src/main/resources/base/element/string.json b/features/patient/src/main/resources/base/element/string.json new file mode 100644 index 0000000..f51a9c8 --- /dev/null +++ b/features/patient/src/main/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "page_show", + "value": "page from package" + } + ] +} diff --git a/features/patient/src/main/resources/base/media/Patients_Apply_History_Status.png b/features/patient/src/main/resources/base/media/Patients_Apply_History_Status.png new file mode 100644 index 0000000000000000000000000000000000000000..22ebe1a6c7a14a8c1ef649c64892f52af97c2fce GIT binary patch literal 1112 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 literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/chose_card.png b/features/patient/src/main/resources/base/media/chose_card.png new file mode 100644 index 0000000000000000000000000000000000000000..5a40d596d57338f8c0996061bc3fd148f643ac9f GIT binary patch literal 1399 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBBuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFe_$;M3hAM`dB6B=jtVb)aX^@765fKFxc2v6eK2Rr0UTq__OB&@Hb09I0xZL0)vRD^GUf^&XRs)DJWscy1?p^1XIrJkXw zp^1f|j)IYap|QS!vA&_PuAz~Yfu)t9sR9%z0c|TvNwW%aaf8|g1^l#~=$>Fbx5 zm+O@q>*W`v>l<2HTIw4Z=^Gj80#)c1SLT%@R_NvxE5l51Ni9w;$}A|!%+FH*nV6WA zUs__T1av9H3%LbwWAlok!2}F2{ffi_eM3D1ke6TzeSPsO&CP|YE-nd5MYtEM!Nnn! z1*!T$sm1xFMajU3OH&3}Rbb^@l$uzQUlfv`p92fUfQ~My_tA z#;&gB&aN=MF8Rr&xv6<2Fuf@Vy(T#If>J_m0nlcb)S}F?)D*X({9FaFkF7FsyTuTv zc~HG6xZPreQ?EYIG5Vl*MT%#b5HRt8nDA5!BEz#f-jbL$V zoN%|}#^0u^ZCcjy@oe1>gniWK6<-&WXHRBTc~Q!GLws$#lXODd^x!|Uoagj(>`*Q~ zk)m>tXL9_ZKc1DkQ`m%k>iCRi_pLQLd4uioo#ZB;OBYl11*;RLoIKw&9koduBm#DzPV-)&pMuzq)kS*7AFTy&SsPJ&{RvwbKKr|X6MwZjPU9C zQzkmqbDrE{d^a{N^a%T79|J2kAsj?%y4YIbToYJXg^%{8P#XcPD^~YJ9{` PP*LdV>gTe~DWM4f5ykhc literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/dele_patient_inThe_group.png b/features/patient/src/main/resources/base/media/dele_patient_inThe_group.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0fb18276c9df4d8ff8c9f70734785d646d4ee9 GIT binary patch literal 2062 zcmaJ?dr%X19^XKuK|vA;HDI+%XcevG!8?mVvPlrAp#efcaFCEJBtRY};S#Xekbr|! z47T{-&`QA4oJG{bf((pS9&HgQ@t`88Py|aH5fz0~kh@E)*!$zSo!R|8=JWY{U!R#h z$c^6NywYPO007RB5v*A23fo?cgY`Sk11#3XUBymR#mUlDYQ6#i7(!VZ0!B*s8AvR` z7v}EmLbd^by{DL$s7mDQgak4PiEqP@G!nU$4FKD=Yvg=E7NP>vkPNYuPJG;Ql?aN3 zbYemPhr*FF5s^3|Pl3ecMe_uCSpu4nxIGlyrh%*i5=6xZHIi(p64KC#uXQ2o-gZqU zg0CT}EIRRzq!Kw?kSS9jU;rt=M?j(agF!SBHGt|LOx+6lQmB4piXWNk=R@^{C^U!~ z0KR#M)@TZ0Iuy$ae-q1kq7y|bl^i0I)oL|K?MIR+GRRaKjYg*Ul6`%BtOy@vu2jX> z_(+vrOA0JRDNu;zDzQuo+7$U|vK$qiXwCGG5+w4svQp)nHdz}+*6`(IDv4q%X$i>T z{C}uK@)oUB#Ug*^`#*)1yj(d#jzyHR9EHF-xO6XDC^^JbAbgcf!IR0dm%7Lm$y73> zNG1oFOwc<)EEUSs%FVCg91avIRjT+>0TRig6RiRyu~-QCF&W|PAYV9;5)eeCvIB#e za4?HW3l3(m{h7>Qc!|rB334QeRJFtv{>f!7%e6T{BDY3nAqw$d5Mj7NCIMep42hS| zg|@8T8?JEqTo}u8$<}1Zwz>Y-oR_Yw=CN(xy4HGl>w83MHM_#<>Q5)L6953Mk7O}; zn&GLFqaWjA@IO7S-&MhOfn#}@1mO|4qw}%cTgki1aqQ<0jguQXw!#g^Up<}JUM6&j zNjfuIzNr1o+o$IH3#N!p%UXhI*Rk!5kNLk`k5^}?(=+Z?G;esJV5k2+vM|=OxOci@ z_Qm3UY8Kpca1Qm!&5XhpVQ9I%q_7eGsLJ1aMbv@T&+ZbQubX$wVVpKjnm;l*dmLV& zeRRI14uxwo>069;qIWL&>-MdwV%PcqYcz&*6*d?*S~mC#LkuU-68Hz z17P$E}K?5CGu z@G#?-o;zuNLi->IlXAMUJ)R&ryb-W3am4LMMMd zA70NL+*oj{ih_Z@sKESsu4IN!HllDXU-{8%=xyHQGT)tdt|mELyd(4)HoC$ggT+lZ zj(DCb{MEfxR^deKWOeGz(R1h!!g}H!*WstB-xMswPEgx=;IkY0f6+RRzVyKHCz6s9 zIa-R+{A{FFKf1@%_rq*T17O$KRSQ~Pb;`M$fwViUqJ`Ys(x4Xb(nNWLWcP-xo8u7U zwUrN^Hy5e*p4(QGJ3jD|I#<}g5q4zy7KgfmcKy>{#t&tOI&dA{5ZU|GNR+ znCcYtkidVYEy``(n%I#JH0CnqKC~wu!&)yh>!zXa@OClo+5K)$N~HK8H*Hw{l%u2W zTm%ZsHa3p^J%KwC_Wc*0+|j&tU1~^oNL15M&~#-n2HmQICl+>Nbn_Di0=iwZ@y^ls zgxaQW|KWTFaQJ2-pc$~69s6=*1>Y-5%b=kjtv>Jsj>yHB;BQ$JVvb5y_8r^!& zV2&<5g>Mqw4|GX((D9w%^0s~5D6FY;;AZhBtxh{mp=!!@q=xjZKG0vavuG5h4^N$k zey=G7xXCl^y{nJuQVA~Hx}q)FfKhjQ#yn+T4V9sspaI&ZOD?Hd<3QE1hqZx+SkJ)k zqH8Uc-Er{8jI(0_`CI?Fink+ha7f(AJQqD{^s}4a{j$}3K?{|&5Q8Al; z+%kF4BTvuX)ZaYilUz90;P?RU<{8mP(N{J-X>e4%JJg$uB)QFNIF$Fg)jfU-Opm`V au1y6RA{L4#?F>D(KXfEJnpF>{>iz?w)j=Hq literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/fuifangPlan_blackBtn.png b/features/patient/src/main/resources/base/media/fuifangPlan_blackBtn.png new file mode 100644 index 0000000000000000000000000000000000000000..d9ccce5ef0d0e533129518195ead207ca4bacd8a GIT binary patch literal 2151 zcmV-t2$=VYP)Px-AxT6*RCr$PTWf3_R~0^IW_A89SDYg0#xfP#T4YLgJs8sUV^ z?%acpxr@f*nSD5$oiW<`Z|8C6o^Q{09{1dX&>l+&U>k@3BG3jxYeE}{HW15=LR%wh zX@n3WWm(n+rBpWn^dO?yO2g`T-XZ|JDy2LD0Ccgx9jXzTOy=E&VSF9{t_Ohk0zm3w zFC=3ADFC=rO8Mt{zCScH^ft?~-sO3o>hA8IpPHKbcRlZgdX1r>q024HdX$KU0idN? z5VGih8iw(?LZR@pY61x%IuY?+BDxI#I#`1c@ijzzu&b-Yo4V5J z^mp`xaoZ23!=DiGGp_6AqXgN!dGm&Hxx5DvZziJPA6Eo`F#y;vr94{=jAg>www)oO z=K$bR{gMKJJR-VEsj3szLcG!Yh*(6#7hKo<4FK>uRt(2+-a$lriD)!RdlAtCfbn9n zxclhQqi;m5V;K-ah_3*^w<`}5(Ig_?wXm>oJd?@PRIf&bt@ixH!~_c;X(Sm0rPLz; za4i5BdM?j0TDrm zihQR{)4V;O&mRsN*3r>XbAEoFwWL=+{EBIsBl&#(AAZo*1Q7Lj2LL!lL_0Tb-1t+! z4HzCCzS{G=X8>TmwiSQm7)eUGu%>{Bpo8LoiHLeC0&1a7X#>vzn6hpALqzl=0CFbv~P0QjO(>K+|UR%Cr05pOk3^NiouZy!|oL$aX?g~0|!f$I3eIB(;Xch z4Y+oTfbh|QQ)x>tt}Z%gP55pAgf|fJoBjR$duq`kF+jvkheQC;N;-^;j9gkO zm3k5JY@tv%9=6XBi?Q^9uqrwb(f0xoVx4&`$Bv4AGCjl#(@Z*=U(YuqG&_#+6dM zz1FaI2%*Cn?H#HHONj!a)pQ61iOJB2IOvc#AmXA!qJW5#4v7IGiVleQ9G4R>hIa_d zNcmc<#!?7z007umSG)tSi8oNUR|A1_ht+zA!~r3M=tjg7M6|SPTp5TslS-woJ#ys8 znXpCc1gfE6DN6W6P-A#_c+~T}pNB0H5noKF)1S`eae8=5W*L5W7ZjmUsUAdEP4LQ#L&Ke8b&D^e_Ny(ARjc z;iXh6bz`|)zDg<8bUMTigyuQZ;xfIMeNxJuLI}<+_WG{#?LTOGR6yfvHFJl0=nyv$ zD1_Jn0N>IZsGez>*XQ&3U+MZI7iq?Qnh}(#uG}HN6IPZE)%7xJu9>b_Je^;RPKs8Z_2K;@~j z-lo$bRv;Y58B$8U005Whg>WhU902tB_8wY7hgg9SLhv!2{q?A^KH&8dBL1MKr)MIn z3|G%wwVr8th;7^UPl)JKAqy)30G~xzK*Zk>(bGz)!?Uxqf3GD(Gw#O$#Mg-E8~T_Z z7h3sr+si~Whlq3Obo%97E_bRK!Zk8S%Yo3xe{Ue7w*$a$)9Lid#l^)p=jP`4Om%Fd z~jBsPJwn zC8x=2b_gMMay6_X03tr_y6!DOfr6*=`KT^uk4pm)pEeBR9?$cRODR_zuDh!$5ZP>2AR-q`ITql-!XH7zLaX~VE)--$$+bGYlK{sWJ{FRTm&Q|JHy002ovPDHLkV1jTh2bcf= literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/group_turnDown.png b/features/patient/src/main/resources/base/media/group_turnDown.png new file mode 100644 index 0000000000000000000000000000000000000000..0fb84e4d7a6818aa71b65a0e721e6e7f80eb12bb GIT binary patch literal 1165 zcmeAS@N?(olHy`uVBq!ia0vp^ia;#F!3HFmgF^~|lw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlQpL=Wh>{3jAFJg2T)jk)8oi3#0-$aN1{?c|g2d$P)DnfH z)bz|eTc!8A_bVx6rr0WloBA5~7C5J7WO`H;r3P2|g(O#HCtIc{+1n}DR9FEG$W1Lt zRH(?!$t$+1uvG$^YXxM3g!Ppaz)DK8ZIvL7itr6kaLzAERWQ{v)lD`qG*K|O)H5_S zG_f$$Q7|$vG}bpT);BcPH8ip^u(UEXRe%B|plwAdX;wilZcw{`JX@uVl9B=|ef{$C za=mh6z5JqdeM3u2OML?)eIp}XpbFjM%Dj@q3f;V7Wta&rsl~}fnFS@8`FRQ;6BCp2 zOG|8(fG&l2A-4c-Y+f-mn1BJMUy)d#Z>VPg@)As;uP=V3xw&xF#U(+h2=`(&xHzP; zAXPsowK%`DC>a=WY04n03ap%qQWHz^i$e1Ab6}wukda@KU!0L&py2GRpb?&#my%yz ztO-``>ucqiS6q^qmz?V9Vygr+Krb^h#mdyg(%jYD+11q5#lXaZ8 z52`l>w_99r>eUB2MjsTfNbw940wx|16P{{;9C*4<%>yR(B4Da+{OiHQz`)4l>Eakt zaVzQ1|Nr)f9hlYH92p`KdcJ%q2~^lB(J{602fyr_TJ9r1jnmKBoNQ9r$lbyFLV`id zVUKjj&k2j2^B9tv#BF_d@H8kVU63u5>$v;kMT3k&T$gTn!=n>3`8bWaI+i~=z36wt zjX7<69W0NY9+@Kiyx~SC8=nW$Bhe)yVZR%1aCQ6-Jjr;`!Djv(q0bfq(M%uXwU#ud zP7%4iyy3>grXx~)^Vm+TwdQjcel2e#^iV4+EaCZr%qQX#Umf0{>Xdb5(elO{Q#y`F zaUEHm896n+N$r|_h0sH(sx9h%j?PbnXYH_G=;HC#?JTQ;S=%*B^Tr$E9p)301s*44 d++$>sU`YOAlG`WRVGJrDJYD@<);T3K0RXuWhzI}x literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/group_turnRight.png b/features/patient/src/main/resources/base/media/group_turnRight.png new file mode 100644 index 0000000000000000000000000000000000000000..a620868aff39161315445e0038b384ac5ab0f2f0 GIT binary patch literal 1196 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 literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/listBing_blackBtn.png b/features/patient/src/main/resources/base/media/listBing_blackBtn.png new file mode 100644 index 0000000000000000000000000000000000000000..c9e8af896a1a479736451cb7f07d8e310d81a363 GIT binary patch literal 2807 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 JPDHLkV1hpJW-tH% literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/patiemts_list_selected.png b/features/patient/src/main/resources/base/media/patiemts_list_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..c60f6bb3d7fe988d5a7da5c590f11e2d983770cb GIT binary patch literal 2304 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 literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/patients_list_noSelect.png b/features/patient/src/main/resources/base/media/patients_list_noSelect.png new file mode 100644 index 0000000000000000000000000000000000000000..5600189f4d2de7f9d8c2a5af2247410e7d3ee33a GIT binary patch literal 3033 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 MJGj}G+4;x*2R(BQ^Z)<= literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/sendMessage_blackBtn.png b/features/patient/src/main/resources/base/media/sendMessage_blackBtn.png new file mode 100644 index 0000000000000000000000000000000000000000..d8bc5c9b211398e81f757d4f8f7dcb1a6d22c9b9 GIT binary patch literal 3129 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 literal 0 HcmV?d00001 diff --git a/features/patient/src/main/resources/base/media/triangle_normal.png b/features/patient/src/main/resources/base/media/triangle_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..b4019bde48d287d431880a8ac764129e1889cf95 GIT binary patch literal 1111 zcmaJ=OK1~87~VcCzN(@{)YG`Wi<{la!z4>%O|q#?X-Z844LuY$yVGW^yR+_2O;Y2d zB2vMF2N6Z3lwMj7;zbX7uu>5!Djo#2H&GBTN_+62g3iV!dN3}`&dksEegAVVbax$W zT)AZ>!!V8Ub}32sEzVlrK>yPnoiFINnZ(kh2M>^(Y#}DB;tT?DT^>Y9B&&sEbEt)3 z>b7dBG)X5qMFs0F*}=H-x=Gm#)6$wZWn~BvkU@i*5n{hQc*p`x4Y9qxgpe?!D66%P zSg2>DE2WGKDM6KO-49ywA{EdPkwIQRYS>~v#FlhLdUnh_3rY|&6k>k|l}>bnD7Fyr zxqO@=KriqIUC;-;0k{{q1?b@g4-Y*Ybc;ezgg&tJvDBNT4v0yqZOIqCh1e`1rpWWT zT+WsAxUeOVd;_;P*=rZ`jio zjQSyr`6JVT1!qeYY*R(a9Z+&%-xmrlC_jvZtaNnLkBWv3C)Gi!s zo}XR&cgZ_56dM-j!bh6IVEpa7 zOZKCW8?2}IXVZ?gzpv>Q7%wmj9QeBH$u40gFh8*A?%3yzHT9dA { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/features/patient/src/ohosTest/ets/test/List.test.ets b/features/patient/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..794c7dc --- /dev/null +++ b/features/patient/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/features/patient/src/ohosTest/module.json5 b/features/patient/src/ohosTest/module.json5 new file mode 100644 index 0000000..a37e6e2 --- /dev/null +++ b/features/patient/src/ohosTest/module.json5 @@ -0,0 +1,13 @@ +{ + "module": { + "name": "patient_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/features/patient/src/test/List.test.ets b/features/patient/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/features/patient/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/features/patient/src/test/LocalUnit.test.ets b/features/patient/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..165fc16 --- /dev/null +++ b/features/patient/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/oh-package-lock.json5 b/oh-package-lock.json5 index 015f8d4..fd5ec9e 100644 --- a/oh-package-lock.json5 +++ b/oh-package-lock.json5 @@ -5,6 +5,8 @@ "lockfileVersion": 3, "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", "specifiers": { + "@hshare/hshare-flipper@^1.0.0": "@hshare/hshare-flipper@1.0.0", + "@nimsdk/vendor@1.0.0": "@nimsdk/vendor@1.0.0", "@ohos/crypto-js@^2.0.4": "@ohos/crypto-js@2.0.4", "@ohos/dataorm@^2.2.6": "@ohos/dataorm@2.2.6", "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", @@ -12,6 +14,20 @@ "reflect-metadata@^0.1.13": "reflect-metadata@0.2.1" }, "packages": { + "@hshare/hshare-flipper@1.0.0": { + "name": "@hshare/hshare-flipper", + "version": "1.0.0", + "integrity": "sha512-TzXUyViTzIISLFtS4w6djfFDuiNUA0bQHOERMxQaPpXD8U3mEoLjaDUoEnlwRG2IsDjnW/Q0lO1KFT0QyFwLTw==", + "resolved": "https://repo.harmonyos.com/ohpm/@hshare/hshare-flipper/-/hshare-flipper-1.0.0.har", + "registryType": "ohpm" + }, + "@nimsdk/vendor@1.0.0": { + "name": "@nimsdk/vendor", + "version": "1.0.0", + "integrity": "sha512-q49MJM6PfucNs8jvLP56a2etyqRfZCeJaMa1BT9vO4sIgwt15bin+hpUWZ1qkflBs9YkDb2nMIX5O8zt556muw==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/vendor/-/vendor-1.0.0.har", + "registryType": "ohpm" + }, "@ohos/crypto-js@2.0.4": { "name": "@ohos/crypto-js", "version": "2.0.4", diff --git a/oh-package.json5 b/oh-package.json5 index 2f5b43a..93432d5 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -3,7 +3,8 @@ "description": "Please describe the basic information.", "dependencies": { "@ohos/crypto-js": "^2.0.4", - "@ohos/dataorm": "^2.2.6" + "@ohos/dataorm": "^2.2.6", + "@hshare/hshare-flipper": "^1.0.0" }, "devDependencies": { "@ohos/hypium": "1.0.21", diff --git a/products/expert/oh-package-lock.json5 b/products/expert/oh-package-lock.json5 index 4ba637f..b2c135e 100644 --- a/products/expert/oh-package-lock.json5 +++ b/products/expert/oh-package-lock.json5 @@ -9,9 +9,33 @@ "@aliyun/httpdns@1.1.1": "@aliyun/httpdns@1.1.1", "@aliyun/logger@1.0.2": "@aliyun/logger@1.0.2", "@itcast/basic@../../commons/basic": "@itcast/basic@../../commons/basic", + "@nimkit/chatkit@../../chatkit": "@nimkit/chatkit@../../chatkit", + "@nimkit/chatkit_ui@../../chatkit_ui": "@nimkit/chatkit_ui@../../chatkit_ui", + "@nimkit/common@../../common": "@nimkit/common@../../common", + "@nimkit/conversationkit_ui@../../conversationkit_ui": "@nimkit/conversationkit_ui@../../conversationkit_ui", + "@nimkit/corekit@../../corekit": "@nimkit/corekit@../../corekit", + "@nimkit/localconversationkit_ui@../../localconversationkit_ui": "@nimkit/localconversationkit_ui@../../localconversationkit_ui", + "@nimkit/markdown@1.1.0": "@nimkit/markdown@1.1.0", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+conversation@10.9.10/oh_modules/@nimsdk/conversation/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+friend@10.9.10/oh_modules/@nimsdk/friend/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+localconversation@10.9.10/oh_modules/@nimsdk/localconversation/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+message@10.9.10/oh_modules/@nimsdk/message/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+team@10.9.10/oh_modules/@nimsdk/team/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+user@10.9.10/oh_modules/@nimsdk/user/libs/base.har": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/base@10.9.10": "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "@nimsdk/conversation@10.9.10": "@nimsdk/conversation@10.9.10", + "@nimsdk/friend@10.9.10": "@nimsdk/friend@10.9.10", + "@nimsdk/localconversation@10.9.10": "@nimsdk/localconversation@10.9.10", + "@nimsdk/message@10.9.10": "@nimsdk/message@10.9.10", + "@nimsdk/nim@10.9.10": "@nimsdk/nim@10.9.10", + "@nimsdk/team@10.9.10": "@nimsdk/team@10.9.10", + "@nimsdk/user@10.9.10": "@nimsdk/user@10.9.10", + "@nimsdk/vendor@1.0.0": "@nimsdk/vendor@1.0.0", "@ohos/crypto-js@2.0.3": "@ohos/crypto-js@2.0.4", "@ohos/crypto-js@^2.0.2": "@ohos/crypto-js@2.0.4", "@ohos/httpclient@2.0.2": "@ohos/httpclient@2.0.2", + "@ohos/pinyin4js@^2.0.0": "@ohos/pinyin4js@2.0.1", "@polyvharmony/httpdns-api@1.0.2": "@polyvharmony/httpdns-api@1.0.2", "@polyvharmony/httpdns-impl-ali@1.0.2": "@polyvharmony/httpdns-impl-ali@1.0.2", "@polyvharmony/httpdns-impl-local@1.0.2": "@polyvharmony/httpdns-impl-local@1.0.2", @@ -22,10 +46,15 @@ "@polyvharmony/media-player-sdk-addon-cache-down@2.5.0": "@polyvharmony/media-player-sdk-addon-cache-down@2.5.0", "@polyvharmony/media-player-sdk@2.5.0": "@polyvharmony/media-player-sdk@2.5.0", "base64-js@^1.5.1": "base64-js@1.5.1", + "class-transformer@^0.5.1": "class-transformer@0.5.1", "home@../../features/Home": "home@../../features/Home", "media-player-common@../../polyv": "media-player-common@../../polyv", "mypage@../../features/mypage": "mypage@../../features/mypage", + "netease@../../features/netease": "netease@../../features/netease", "pako@^2.1.0": "pako@2.1.0", + "patient@../../features/patient": "patient@../../features/patient", + "reflect-metadata@^0.1.13": "reflect-metadata@0.2.1", + "refreshlib@../../RefreshLib": "refreshlib@../../RefreshLib", "register@../../features/register": "register@../../features/register", "scene_single_video@../../scene_single_video": "scene_single_video@../../scene_single_video" }, @@ -61,6 +90,177 @@ "resolved": "../../commons/basic", "registryType": "local" }, + "@nimkit/chatkit@../../chatkit": { + "name": "@nimkit/chatkit", + "version": "10.1.0", + "resolved": "../../chatkit", + "registryType": "local", + "dependencies": { + "@nimsdk/conversation": "10.9.10", + "@nimsdk/message": "10.9.10", + "@nimsdk/team": "10.9.10", + "@nimsdk/user": "10.9.10", + "@nimsdk/friend": "10.9.10", + "@nimsdk/nim": "10.9.10", + "@nimsdk/base": "10.9.10", + "@nimkit/corekit": "file:../corekit", + "class-transformer": "^0.5.1", + "reflect-metadata": "^0.1.13" + } + }, + "@nimkit/chatkit_ui@../../chatkit_ui": { + "name": "@nimkit/chatkit_ui", + "version": "10.1.0", + "resolved": "../../chatkit_ui", + "registryType": "local", + "dependencies": { + "@nimkit/common": "file:../common", + "@nimkit/chatkit": "file:../chatkit", + "@nimkit/corekit": "file:../corekit", + "@nimsdk/base": "10.9.10", + "class-transformer": "^0.5.1", + "reflect-metadata": "^0.1.13", + "@nimkit/markdown": "1.1.0", + "@itcast/basic": "file:../commons/basic" + } + }, + "@nimkit/common@../../common": { + "name": "@nimkit/common", + "version": "1.1.0", + "resolved": "../../common", + "registryType": "local", + "dependencies": { + "@ohos/pinyin4js": "^2.0.0" + } + }, + "@nimkit/conversationkit_ui@../../conversationkit_ui": { + "name": "@nimkit/conversationkit_ui", + "version": "10.1.0", + "resolved": "../../conversationkit_ui", + "registryType": "local", + "dependencies": { + "@nimkit/common": "file:../common", + "@nimkit/chatkit": "file:../chatkit", + "@nimsdk/base": "10.9.10" + } + }, + "@nimkit/corekit@../../corekit": { + "name": "@nimkit/corekit", + "version": "1.1.0", + "resolved": "../../corekit", + "registryType": "local" + }, + "@nimkit/localconversationkit_ui@../../localconversationkit_ui": { + "name": "@nimkit/localconversationkit_ui", + "version": "10.1.0", + "resolved": "../../localconversationkit_ui", + "registryType": "local", + "dependencies": { + "@nimkit/common": "file:../common", + "@nimkit/chatkit": "file:../chatkit", + "@nimsdk/base": "10.9.10" + } + }, + "@nimkit/markdown@1.1.0": { + "name": "@nimkit/markdown", + "version": "1.1.0", + "integrity": "sha512-ITTM5bIkvcK+KsWHxn7vta1W3XGulMQ4vWHT37NidayhTlo04lG6JMABtsxCYYR7H6OiwuUcVpLzDvOyjScYSA==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimkit/markdown/-/markdown-1.1.0.har", + "registryType": "ohpm" + }, + "@nimsdk/base@../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har": { + "name": "@nimsdk/base", + "version": "10.9.10", + "resolved": "../../oh_modules/.ohpm/@nimsdk+nim@10.9.10/oh_modules/@nimsdk/nim/libs/base.har", + "registryType": "local", + "dependencies": { + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/conversation@10.9.10": { + "name": "@nimsdk/conversation", + "version": "10.9.10", + "integrity": "sha512-1HLvs19/GJAHeIOCN0OiKlowkg6dzZwvZK0Jqu7tAcYGcLl4+G/Z3pwsGHhv+E2Tzs8FHZCqbESMgSh+LNyt/g==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/conversation/-/conversation-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/friend@10.9.10": { + "name": "@nimsdk/friend", + "version": "10.9.10", + "integrity": "sha512-JVACpT8xqLLaN8D26YHmwfsS1dHFQvBnP3Jyk9El89P2trn/2ZFLvnQjxzyBDsqJRUtNFfIrN+TK7Idmud4ACQ==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/friend/-/friend-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/localconversation@10.9.10": { + "name": "@nimsdk/localconversation", + "version": "10.9.10", + "integrity": "sha512-TDwE/zBLRX+WKZcpEodCXQATLHS4ZSfRw92FbWoKqxq1LwlJSdumvN1elFlnUmCVi5E2/tFzSxqXUBn8fYAZ8A==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/localconversation/-/localconversation-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/message@10.9.10": { + "name": "@nimsdk/message", + "version": "10.9.10", + "integrity": "sha512-f59rWiM4SjhhxNftRUt9vg7lIwkGycV/aL8J3omH+Te4SMbUGolwDGErDr7adtZ3tDUThtxxgU8n5tD28TBRtA==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/message/-/message-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/nim@10.9.10": { + "name": "@nimsdk/nim", + "version": "10.9.10", + "integrity": "sha512-WpT8vBTld92ExtH30Ffsm+xq6BW6/UFj8SuhJrcQaZY3AYf9sg+d+euqx/dFzjZin5cWRxd/yoodBiVcGfsM4w==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/nim/-/nim-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/team@10.9.10": { + "name": "@nimsdk/team", + "version": "10.9.10", + "integrity": "sha512-T4YSN395VXQr1TDX2B24DmGYuvUgUqE7wndbleR980wEyki9IfhC2VxxJ1yajhxVlVkfmuBjCB/eKWL0zLzu5A==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/team/-/team-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/user@10.9.10": { + "name": "@nimsdk/user", + "version": "10.9.10", + "integrity": "sha512-KyWVDDPbymj3qoC8Y0mB8umgvLg89Y2cB02tM35oSG8IW95C936v5ogip2Jk7qAfabXxI/XTyy5wQoW1z950JA==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/user/-/user-10.9.10.har", + "registryType": "ohpm", + "dependencies": { + "@nimsdk/base": "file:./libs/base.har", + "@nimsdk/vendor": "1.0.0" + } + }, + "@nimsdk/vendor@1.0.0": { + "name": "@nimsdk/vendor", + "version": "1.0.0", + "integrity": "sha512-q49MJM6PfucNs8jvLP56a2etyqRfZCeJaMa1BT9vO4sIgwt15bin+hpUWZ1qkflBs9YkDb2nMIX5O8zt556muw==", + "resolved": "https://repo.harmonyos.com/ohpm/@nimsdk/vendor/-/vendor-1.0.0.har", + "registryType": "ohpm" + }, "@ohos/crypto-js@2.0.4": { "name": "@ohos/crypto-js", "version": "2.0.4", @@ -80,6 +280,13 @@ "base64-js": "^1.5.1" } }, + "@ohos/pinyin4js@2.0.1": { + "name": "@ohos/pinyin4js", + "version": "2.0.1", + "integrity": "sha512-qmYDelku5gcgKVmJyMqa7kWf0a+e8nnGS9ts5FRLA0LdRf+Iz36X/4Vub6hhh/RusuDmmWG9h153KZe+kraIVg==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/pinyin4js/-/pinyin4js-2.0.1.har", + "registryType": "ohpm" + }, "@polyvharmony/httpdns-api@1.0.2": { "name": "@polyvharmony/httpdns-api", "version": "1.0.2", @@ -186,6 +393,14 @@ "shasum": "1b1b440160a5bf7ad40b650f095963481903930a", "registryType": "ohpm" }, + "class-transformer@0.5.1": { + "name": "class-transformer", + "version": "0.5.1", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "resolved": "https://repo.harmonyos.com/ohpm/class-transformer/-/class-transformer-0.5.1.tgz", + "shasum": "24147d5dffd2a6cea930a3250a677addf96ab336", + "registryType": "ohpm" + }, "home@../../features/Home": { "name": "home", "version": "1.0.0", @@ -218,6 +433,21 @@ "@itcast/basic": "file:../../commons/basic" } }, + "netease@../../features/netease": { + "name": "netease", + "version": "1.0.0", + "resolved": "../../features/netease", + "registryType": "local", + "dependencies": { + "@itcast/basic": "file:../../commons/basic", + "@nimkit/conversationkit_ui": "file:../../conversationkit_ui", + "@nimkit/chatkit_ui": "file:../../chatkit_ui", + "@nimkit/chatkit": "file:../../chatkit", + "@nimsdk/base": "10.9.10", + "@nimkit/common": "file:../../common", + "@nimkit/localconversationkit_ui": "file:../../localconversationkit_ui" + } + }, "pako@2.1.0": { "name": "pako", "version": "2.1.0", @@ -226,6 +456,30 @@ "shasum": "266cc37f98c7d883545d11335c00fbd4062c9a86", "registryType": "ohpm" }, + "patient@../../features/patient": { + "name": "patient", + "version": "1.0.0", + "resolved": "../../features/patient", + "registryType": "local", + "dependencies": { + "@itcast/basic": "file:../../commons/basic", + "refreshlib": "file:../../RefreshLib" + } + }, + "reflect-metadata@0.2.1": { + "name": "reflect-metadata", + "version": "0.2.1", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "resolved": "https://repo.harmonyos.com/ohpm/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "shasum": "8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74", + "registryType": "ohpm" + }, + "refreshlib@../../RefreshLib": { + "name": "refreshlib", + "version": "1.0.0", + "resolved": "../../RefreshLib", + "registryType": "local" + }, "register@../../features/register": { "name": "register", "version": "1.0.0", diff --git a/products/expert/oh-package.json5 b/products/expert/oh-package.json5 index be8bcb6..83e968c 100644 --- a/products/expert/oh-package.json5 +++ b/products/expert/oh-package.json5 @@ -10,6 +10,7 @@ "mypage":"file:../../features/mypage", "home": 'file:../../features/Home', "register": 'file:../../features/register', + "patient": 'file:../../features/patient', "scene_single_video": "file:../../scene_single_video", "media-player-common": "file:../../polyv", "@polyvharmony/media-player-sdk": "2.5.0", diff --git a/products/expert/src/main/ets/contants/TabBarItems.ets b/products/expert/src/main/ets/contants/TabBarItems.ets index d013770..8eda892 100644 --- a/products/expert/src/main/ets/contants/TabBarItems.ets +++ b/products/expert/src/main/ets/contants/TabBarItems.ets @@ -2,6 +2,11 @@ import { TabBarCompModel } from '../models/TabBarCompModel' export const TabBarItems: TabBarCompModel[] = [ + { + defaultIcon: $r('app.media.home_default_icon'), + activeIcon: $r('app.media.home_selected_icon'), + label: '首页' + }, { defaultIcon: $r('app.media.home_my_meeting_nor'), activeIcon: $r('app.media.home_my_meeting_sel'), diff --git a/products/expert/src/main/ets/entryability/EntryAbility.ets b/products/expert/src/main/ets/entryability/EntryAbility.ets index 2b86595..fc3754b 100644 --- a/products/expert/src/main/ets/entryability/EntryAbility.ets +++ b/products/expert/src/main/ets/entryability/EntryAbility.ets @@ -4,12 +4,15 @@ import { window } from '@kit.ArkUI'; import notificationManager from '@ohos.notificationManager'; import { PLVMediaPlayerStartUp } from '../startup/PLVMediaPlayerStartUp'; import contextConstant from '@ohos.app.ability.contextConstant'; +import { patientDbManager } from '@itcast/basic'; const DOMAIN = 0x0000; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { AppStorage.Set('notificationEnabled', false); // 假设默认关闭 this.checkNotificationStatus(); // 首次检查 + // 初始化患者数据库 + this.initPatientDatabase(); this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); @@ -19,6 +22,15 @@ export default class EntryAbility extends UIAbility { PLVMediaPlayerStartUp.start(this.context) } + private async initPatientDatabase() { + try { + await patientDbManager.initDatabase(this.context); + console.info('患者数据库初始化成功'); + } catch (error) { + console.error('患者数据库初始化失败:', error); + } + } + onDestroy(): void { hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); } diff --git a/products/expert/src/main/ets/pages/Home.ets b/products/expert/src/main/ets/pages/Home.ets index a74fafe..4ec1a4c 100644 --- a/products/expert/src/main/ets/pages/Home.ets +++ b/products/expert/src/main/ets/pages/Home.ets @@ -1,8 +1,10 @@ import { TabBarComp } from '../pages/Tabbar/TabBarComp'; -import { BasicConstant, themeManager } from '@itcast/basic'; +import { BasicConstant, themeManager,hdHttp, HdResponse, authStore } from '@itcast/basic'; import { emitter } from '@kit.BasicServicesKit'; import { common } from '@kit.AbilityKit'; import { promptAction } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { patientDbManager, PatientEntity } from '@itcast/basic'; import { NimRepository } from '../entryability/NimRepository' import { AppConfig } from '../constants/AppConfig' @@ -12,6 +14,7 @@ struct Home { @State @Watch('onChangeIndex') activeIndex: number = 0 + @State patients: PatientEntity[] = []; login = async (accountId: string, token: string) => { @@ -37,11 +40,20 @@ struct Home { else themeManager.settingStatusBarBlack() } + async loadPatients() { + try { + await patientDbManager.patientsToFMDB(); + } catch (error) { + console.error('加载患者数据失败:', error); + } + } + onPageShow(): void { console.log('--onPageShow--'); emitter.emit('notification_status_changed') this.onChangeIndex() // emitter.emit({eventId:BasicConstant.notification_home_tab_change},{data:{'changeIndex':this.activeIndex}}) + this.loadPatients(); } onPageHide(): void { @@ -62,8 +74,6 @@ struct Home { const ctx = getContext() as common.UIAbilityContext ctx.terminateSelf() } - - return true } @@ -73,4 +83,8 @@ struct Home { } .backgroundColor($r('app.color.white')) } -} \ No newline at end of file +} + +interface updateExtraData { + expertUuid: string +} diff --git a/products/expert/src/main/ets/pages/PatientsPage/BuildOrEditGroupPage.ets b/products/expert/src/main/ets/pages/PatientsPage/BuildOrEditGroupPage.ets new file mode 100644 index 0000000..a2f81a0 --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/BuildOrEditGroupPage.ets @@ -0,0 +1,19 @@ +import { BuildOrEditGroupPage } from 'patient' + +@Entry +@Component +struct BuildGroupPage { + @Provide refreshFlag:boolean = false; + + onPageShow(): void { + this.refreshFlag = !this.refreshFlag; + } + + build() { + RelativeContainer() { + BuildOrEditGroupPage(); + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/PatientsPage/PatientDetailsPage.ets b/products/expert/src/main/ets/pages/PatientsPage/PatientDetailsPage.ets new file mode 100644 index 0000000..f4fa52f --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/PatientDetailsPage.ets @@ -0,0 +1,19 @@ +import { PatientDetailsComp } from 'patient' + +@Entry +@Component +struct PatientDetailsPage { + @Provide refreshFlag:boolean = false; + + onPageShow(): void { + this.refreshFlag = !this.refreshFlag; + } + + build() { + RelativeContainer() { + PatientDetailsComp() + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/PatientsPage/PatientMsgSetPage.ets b/products/expert/src/main/ets/pages/PatientsPage/PatientMsgSetPage.ets new file mode 100644 index 0000000..6b0df7b --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/PatientMsgSetPage.ets @@ -0,0 +1,14 @@ +import { PatientSetMsgPage } from 'patient' + + +@Entry +@Component +struct PatientMsgSetPage { + build() { + RelativeContainer() { + PatientSetMsgPage(); + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/PatientsPage/PatientPages.ets b/products/expert/src/main/ets/pages/PatientsPage/PatientPages.ets new file mode 100644 index 0000000..f4b076d --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/PatientPages.ets @@ -0,0 +1,13 @@ +import { PatientApplyPage } from 'patient' + +@Entry +@Component +struct PatientPages { + build() { + RelativeContainer() { + PatientApplyPage(); + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/PatientsPage/PatientsGroupPage.ets b/products/expert/src/main/ets/pages/PatientsPage/PatientsGroupPage.ets new file mode 100644 index 0000000..30c778f --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/PatientsGroupPage.ets @@ -0,0 +1,19 @@ +import { PatientsGroup } from 'patient' + +@Entry +@Component +struct PatientsGroupPage { + @Provide refreshFlag:boolean = false; + + onPageShow(): void { + this.refreshFlag = !this.refreshFlag; + } + + build() { + RelativeContainer() { + PatientsGroup(); + } + .height('100%') + .width('100%') + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/PatientsPage/PatientsListPage.ets b/products/expert/src/main/ets/pages/PatientsPage/PatientsListPage.ets new file mode 100644 index 0000000..eda15c3 --- /dev/null +++ b/products/expert/src/main/ets/pages/PatientsPage/PatientsListPage.ets @@ -0,0 +1,12 @@ +import { PatientsListComp } from 'patient' + +@Entry +@Component +struct PatientsListPage { + + build() { + RelativeContainer(){ + PatientsListComp(); + } + } +} \ No newline at end of file diff --git a/products/expert/src/main/ets/pages/Tabbar/TabBarComp.ets b/products/expert/src/main/ets/pages/Tabbar/TabBarComp.ets index bd4d48e..205cdf6 100644 --- a/products/expert/src/main/ets/pages/Tabbar/TabBarComp.ets +++ b/products/expert/src/main/ets/pages/Tabbar/TabBarComp.ets @@ -1,4 +1,4 @@ -import { VideoPage,VideoGandan } from 'home' +import { VideoPage,VideoGandan, HomePage } from 'home' import { MyHomePage } from 'mypage' import { TabBarCompModel } from '../../models/TabBarCompModel' import { TabBarItems } from '../../contants/TabBarItems' @@ -50,10 +50,10 @@ export struct TabBarComp { }) { ForEach(TabBarItems, (item: TabBarCompModel, index: number) => { TabContent() { - if (index === 0) VideoPage() - else if (index === 1) VideoGandan() - else if (index === 2) MyHomePage() - // else MyHomePage() + if (index === 0) HomePage() + else if (index === 1) VideoPage() + else if (index === 2) VideoGandan() + else MyHomePage() } .tabBar(this.TabBarBuilder(item, index)) .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) diff --git a/products/expert/src/main/ets/pages/VideoPage/VideoGandanPage.ets b/products/expert/src/main/ets/pages/VideoPage/VideoGandanPage.ets index 96f19cb..bf59844 100644 --- a/products/expert/src/main/ets/pages/VideoPage/VideoGandanPage.ets +++ b/products/expert/src/main/ets/pages/VideoPage/VideoGandanPage.ets @@ -1,9 +1,7 @@ import { VideoGandan } from 'home' + @Entry -@Component struct VideoGandanPage { - - build() { RelativeContainer() { VideoGandan() diff --git a/products/expert/src/main/ets/pages/WebView/WebPage.ets b/products/expert/src/main/ets/pages/WebView/WebPage.ets index 9bb3736..dbc12b3 100644 --- a/products/expert/src/main/ets/pages/WebView/WebPage.ets +++ b/products/expert/src/main/ets/pages/WebView/WebPage.ets @@ -1,6 +1,12 @@ import webview from '@ohos.web.webview'; import { HdNav } from '@itcast/basic'; import router from '@ohos.router'; +import { image } from '@kit.ImageKit'; +import { photoAccessHelper } from '@kit.MediaLibraryKit'; +import { promptAction } from '@kit.ArkUI'; +import { componentSnapshot } from '@kit.ArkUI'; +import { fileIo, fileUri } from '@kit.CoreFileKit'; +const TAG = 'WebViewSaveImage'; @Entry @Component @@ -8,29 +14,117 @@ struct WebPage { private controller: webview.WebviewController = new webview.WebviewController(); @State params:RouteParams = router.getParams() as RouteParams; + @State contentWidth: number = 0; + @State contentHeight: number = 0; @State url: string = this.params.url; @State title: string = this.params.title; customUserAgent: string = 'gdxz-expert'; + onBackPress(): boolean | void { + if (this.controller.accessStep(-1)) { + this.controller.backward(); + return true; + } + return false; + } + build() { Column() { - HdNav({ title: this.title, showRightIcon: false, hasBorder: true }) + HdNav({ title: this.title, showRightIcon: false, hasBorder: true ,isLeftAction:true,leftItemAction:()=>{ + if (this.controller.accessBackward()) { + this.controller.backward(); + } else { + router.back(); + } + }}) Web({ src: this.url, controller: this.controller }) + .id('webView') .mixedMode(MixedMode.All) + .overScrollMode(OverScrollMode.ALWAYS) .domStorageAccess(true) .onControllerAttached(() => { let userAgent = this.controller.getUserAgent() + this.customUserAgent; this.controller.setCustomUserAgent(userAgent); }) + .onPageEnd(() => { + // 注入JS获取body高度 + this.controller.runJavaScript( + 'document.documentElement.scrollWidth', (error, result) => { + if (!error) this.contentWidth = Number(result); + } + ); + this.controller.runJavaScript('document.body.scrollHeight',(error,result)=>{ + if (!error) this.contentHeight = Number(result); + }) + }) .width('100%') .layoutWeight(1)// 占据剩余空间 .height('100%') + if (this.title == '我的二维码') { + Row(){ + SaveButton({text:SaveDescription.SAVE_IMAGE,buttonType:ButtonType.Normal}) + .fontSize(16) + .fontColor(Color.White) + .backgroundColor('rgb(63,199,193)') + .width('100%').height(50) + .onClick(async (event, result) => { + // if (result === SaveButtonOnClickResult.SUCCESS) { + await this.saveWebViewImage(); + // } + }) + }.width('100%').height(56).backgroundColor(Color.White).alignItems(VerticalAlign.Top) + } } .height('100%')// 关键:约束父容器高度 } + + async saveWebViewImage() { + try { + // 1. 获取整个网页的长截图 + this.controller.webPageSnapshot({ + id: "webView", + size: { width: this.contentWidth, height:this.contentHeight } // 确保捕获全尺寸 + }, async (error, result) => { + if (error) { + promptAction.showToast({ message: `截图失败: ${error.message}` }); + console.error('webPageSnapshot error:', error); + return; + } + + if (!result?.imagePixelMap) { + promptAction.showToast({ message: '获取截图数据失败' }); + return; + } + + // 2. 转为JPEG并写入沙箱 + const ctx = getContext(this); + const imagePacker = image.createImagePacker(); + const arrayBuffer = await imagePacker.packToData( + result.imagePixelMap, + { format: 'image/jpeg', quality: 100 } // 平衡质量与大小 + ); + + const sandboxPath = ctx.cacheDir + `/web_fullpage_${Date.now()}.jpg`; + const file = fileIo.openSync(sandboxPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE); + fileIo.writeSync(file.fd, arrayBuffer); + fileIo.closeSync(file.fd); + + // 3. 保存到相册 + const helper = photoAccessHelper.getPhotoAccessHelper(ctx); + const sandboxUri = fileUri.getUriFromPath(sandboxPath); + const request = photoAccessHelper.MediaAssetChangeRequest.createImageAssetRequest(ctx, sandboxUri); + await helper.applyChanges(request); + + promptAction.showToast({ message: '网页已保存至相册' }); + }); + } catch (e) { + promptAction.showToast({ message: '保存失败: ' + e.message }); + console.error('saveWebViewImage error:', e); + } + } } interface RouteParams { diff --git a/products/expert/src/main/module.json5 b/products/expert/src/main/module.json5 index 306322f..ebe40ea 100644 --- a/products/expert/src/main/module.json5 +++ b/products/expert/src/main/module.json5 @@ -63,6 +63,11 @@ "name": "ohos.permission.READ_MEDIA", "reason": "$string:media_reason", "usedScene": {} + }, + { + "name": "ohos.permission.WRITE_MEDIA", + "reason": "$string:media_reason", + "usedScene": {} } ] } diff --git a/products/expert/src/main/resources/base/media/home_default_icon.png b/products/expert/src/main/resources/base/media/home_default_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8f771c1eeefe21e1181125286c510b6cee9a213a GIT binary patch literal 2714 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 UpmyzXg8%>k07*qoM6N<$f`u3lzW@LL literal 0 HcmV?d00001 diff --git a/products/expert/src/main/resources/base/media/home_selected_icon.png b/products/expert/src/main/resources/base/media/home_selected_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef29cc40faa80ae29418ec7f6e2c5c8ee1abfec GIT binary patch literal 1519 zcmVPx)sYygZRCr$PoM~)SRTRhn=f0MGFU)iq3)1NT30k7j@Wlj(7-I}ZgGBsda0%)M z5!yoHM(PJ6Fi28@34xdx5g}S6BKpC&2BIOF7?lseG-WB!DND7jg`xBAF|RCX<;~2! zGjAEfdmr0#&$;LK&i|fw-!g>YxQPG-Ol~F+K(VO`U!kl8#OPU5YK$wifMNVv-Fo)- zC4lL7_4)?I0y3s1JdX%ln0Y1`Z6tVgL*m?Lw=IxxGz4AngYkPoZ3w48ZRLn2|AILL0$9 zDR~NIQ*G6pwCvwCL^7Gk_$Vb$pyZTH7BGHD$#p2olF1Clb1AtFMN2Y8A|=(+mHOcrOCC8w+ zDjAQZR4|l5lCkzO5vP_8Wj`)-rGlV%E*X!cR1g$jB;$dU@`B=7mr>TxHA9D_qz$EL zB;%r#w4pe%m(i^NUHbxWJ19fRd;71OkW=;rNJ$Aw(M!f*DJem@B_!iuSIP>dXuFKA z#5ARp5y~wq83&}45lT^AnCVtVfl|5&O0jmC@a#IweQ5>CVl!~^=L0yjbu+rpoCcWn zkSR!M2owv*_}b6{DY1pGKDjH3V?y;bEZNb5ag|s1n=B=ce7z6fywQMEPq+RYb39iX z0wo?RzYnI|$w1AGN16H|9UpP;vKKM`^`;?y`!1Zv&LxlH?-OnL#mjPmHvnbe>3_6h z4zal(KzV*q+#6N*^fOra_WB_KdXpXa<=@`I0u$Nj0D% z7QwyI6Q@nb?8Qq_RvSb6pw z0T7PN!gD9oMc~UBa_f^2FRb~0rC5vV#+u3qNm*exP?C(Wz&0=@d7h600U4sPMB*n~ zS0(a%hSP5;LMjjfvzwC0o_D8$hwa89)i3Tzdg8huEx2 z0plRSzB{RE)i=9fO|d~AKnb9zDKe;(pi+WLQ6u1Cr8wAYFqK4w&hH#sa8n$9pd;6tq32%soHiEIr2`GZ-vHC>v z3sql!?23t1r#BS#r4O|wkQ!% zy~z~6vtlZQ`5gn(RUK!s^L$N_20gguX!2XtxvN#mv?=An5T7%6uBziAf+B}g>C{&e zr`uKMt^{RkW#y=zQRZf5epc0S(Lf>kt2dFXYXYR|+?AjV-0hEsmq~Cl7^794XRz~B zJ}gAz>SG;oojI#Q!87WPI82zWZc|=J^E70VoV`RzO7P2BWd3wR3x}Ztz~T;vo~JCDq9}lCwZ`GUx~d4hohn zcO=gJQOFNTbZHn91>3KEEXWiReQC(cBOtmjL^`^3*AKgS6a*z#Vz=u511L6UE`VYK