diff --git a/package.json b/package.json
index d8b9fad..f0f8ed2 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
+ "postinstall": "patch-package",
"build-android-common": "react-native bundle --platform android --dev false --entry-file src/app/app.ts --bundle-output bundle/android/common/common.android.bundle --assets-dest ./bundle/android/common --config metro.common.config.js --minify true --reset-cache",
"build-android-buz": "react-native bundle --platform android --dev false --entry-file src/buz.ts --bundle-output ./bundle/android/buz/buz.android.bundle --assets-dest ./bundle/android/buz --config metro.main.config.js --minify true --reset-cache",
"build-ios-common": "react-native bundle --platform ios --dev false --entry-file src/app/app.ts --bundle-output bundle/ios/common.ios.bundle --assets-dest ./bundle/ios --config metro.common.config.js --minify true --reset-cache",
@@ -25,22 +26,29 @@
},
"dependencies": {
"@react-native-async-storage/async-storage": "^2.2.0",
+ "@react-native-community/hooks": "^100.1.0",
"@react-native/new-app-screen": "0.80.1",
"@react-navigation/native": "^7.1.14",
"@react-navigation/stack": "^7.4.2",
+ "@szyx-mobile/hooks": "^1.2.0",
+ "@szyx-mobile/use-request": "^1.2.3",
"babel-plugin-module-resolver": "^5.0.2",
+ "patch-package": "^8.0.0",
"react": "19.1.0",
"react-native": "0.80.1",
"react-native-bundle-splitter": "^3.0.1",
"react-native-copilot": "^3.3.3",
"react-native-device-info": "^14.0.4",
+ "react-native-exit-app": "^2.0.0",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.27.2",
+ "react-native-linear-gradient": "^2.8.3",
"react-native-root-siblings": "^5.0.1",
"react-native-safe-area-context": "^5.5.2",
"react-native-spinkit": "^1.5.1",
"react-native-storage": "^1.0.1",
"react-native-toast-message": "^2.3.3",
+ "react-native-webview": "^13.16.0",
"react-native-zip-archive": "^7.0.2"
},
"devDependencies": {
diff --git a/src/app/App.tsx b/src/app/App.tsx
index 969d6fa..7884471 100644
--- a/src/app/App.tsx
+++ b/src/app/App.tsx
@@ -14,15 +14,7 @@ function App() {
- {/* null}*/}
- {/* arrowSize={0}*/}
- {/* tooltipStyle={styles.tooltip}*/}
- {/* tooltipComponent={TooltipComponent}*/}
- {/*>*/}
- {/**/}
BottomSheet.setRef(ref)} />
Alert.setRef(ref)} />
diff --git a/src/app/assets/images/common/common_appicon.png b/src/app/assets/images/common/common_appicon.png
new file mode 100644
index 0000000..3d8d2a7
Binary files /dev/null and b/src/app/assets/images/common/common_appicon.png differ
diff --git a/src/app/assets/images/common/common_arrow_back.png b/src/app/assets/images/common/common_arrow_back.png
new file mode 100644
index 0000000..f5ee547
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_back.png differ
diff --git a/src/app/assets/images/common/common_arrow_back@2x.png b/src/app/assets/images/common/common_arrow_back@2x.png
new file mode 100644
index 0000000..802a4a5
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_back@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_back@3x.png b/src/app/assets/images/common/common_arrow_back@3x.png
new file mode 100644
index 0000000..55294ea
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_back@3x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_5.png b/src/app/assets/images/common/common_arrow_right_5.png
new file mode 100644
index 0000000..78030d0
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_5.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_5@2x.png b/src/app/assets/images/common/common_arrow_right_5@2x.png
new file mode 100644
index 0000000..d2214c3
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_5@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_5@3x.png b/src/app/assets/images/common/common_arrow_right_5@3x.png
new file mode 100644
index 0000000..51e2692
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_5@3x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_6.png b/src/app/assets/images/common/common_arrow_right_6.png
new file mode 100644
index 0000000..841f708
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_6.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_6@2x.png b/src/app/assets/images/common/common_arrow_right_6@2x.png
new file mode 100644
index 0000000..280c0d9
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_6@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_6@3x.png b/src/app/assets/images/common/common_arrow_right_6@3x.png
new file mode 100644
index 0000000..f4ee1fd
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_6@3x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_7.png b/src/app/assets/images/common/common_arrow_right_7.png
new file mode 100644
index 0000000..0c7c53e
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_7.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_7@2x.png b/src/app/assets/images/common/common_arrow_right_7@2x.png
new file mode 100644
index 0000000..d484780
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_7@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_7@3x.png b/src/app/assets/images/common/common_arrow_right_7@3x.png
new file mode 100644
index 0000000..7f9211d
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_7@3x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_8.png b/src/app/assets/images/common/common_arrow_right_8.png
new file mode 100644
index 0000000..fdc684c
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_8.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_8@2x.png b/src/app/assets/images/common/common_arrow_right_8@2x.png
new file mode 100644
index 0000000..6a07ead
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_8@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_8@3x.png b/src/app/assets/images/common/common_arrow_right_8@3x.png
new file mode 100644
index 0000000..96d1aee
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_8@3x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_9.png b/src/app/assets/images/common/common_arrow_right_9.png
new file mode 100644
index 0000000..0a7d4c1
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_9.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_9@2x.png b/src/app/assets/images/common/common_arrow_right_9@2x.png
new file mode 100644
index 0000000..c7a4a9c
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_9@2x.png differ
diff --git a/src/app/assets/images/common/common_arrow_right_9@3x.png b/src/app/assets/images/common/common_arrow_right_9@3x.png
new file mode 100644
index 0000000..a702178
Binary files /dev/null and b/src/app/assets/images/common/common_arrow_right_9@3x.png differ
diff --git a/src/app/assets/images/common/common_clear.png b/src/app/assets/images/common/common_clear.png
new file mode 100644
index 0000000..ff89568
Binary files /dev/null and b/src/app/assets/images/common/common_clear.png differ
diff --git a/src/app/assets/images/common/common_clear@2x.png b/src/app/assets/images/common/common_clear@2x.png
new file mode 100644
index 0000000..fac6e55
Binary files /dev/null and b/src/app/assets/images/common/common_clear@2x.png differ
diff --git a/src/app/assets/images/common/common_clear@3x.png b/src/app/assets/images/common/common_clear@3x.png
new file mode 100644
index 0000000..efcdee7
Binary files /dev/null and b/src/app/assets/images/common/common_clear@3x.png differ
diff --git a/src/app/assets/images/common/common_list_empty.png b/src/app/assets/images/common/common_list_empty.png
new file mode 100644
index 0000000..bd24918
Binary files /dev/null and b/src/app/assets/images/common/common_list_empty.png differ
diff --git a/src/app/assets/images/common/common_list_empty@2x.png b/src/app/assets/images/common/common_list_empty@2x.png
new file mode 100644
index 0000000..10bd177
Binary files /dev/null and b/src/app/assets/images/common/common_list_empty@2x.png differ
diff --git a/src/app/assets/images/common/common_list_empty@3x.png b/src/app/assets/images/common/common_list_empty@3x.png
new file mode 100644
index 0000000..f9e0c4d
Binary files /dev/null and b/src/app/assets/images/common/common_list_empty@3x.png differ
diff --git a/src/app/assets/images/common/common_logo.png b/src/app/assets/images/common/common_logo.png
new file mode 100644
index 0000000..3b43296
Binary files /dev/null and b/src/app/assets/images/common/common_logo.png differ
diff --git a/src/app/assets/images/common/common_logo@2x.png b/src/app/assets/images/common/common_logo@2x.png
new file mode 100644
index 0000000..56123ef
Binary files /dev/null and b/src/app/assets/images/common/common_logo@2x.png differ
diff --git a/src/app/assets/images/common/common_logo@3x.png b/src/app/assets/images/common/common_logo@3x.png
new file mode 100644
index 0000000..a703bd9
Binary files /dev/null and b/src/app/assets/images/common/common_logo@3x.png differ
diff --git a/src/app/assets/images/common/common_name_authenticated.png b/src/app/assets/images/common/common_name_authenticated.png
new file mode 100644
index 0000000..34757e5
Binary files /dev/null and b/src/app/assets/images/common/common_name_authenticated.png differ
diff --git a/src/app/assets/images/common/common_name_authenticated@2x.png b/src/app/assets/images/common/common_name_authenticated@2x.png
new file mode 100644
index 0000000..4c37365
Binary files /dev/null and b/src/app/assets/images/common/common_name_authenticated@2x.png differ
diff --git a/src/app/assets/images/common/common_name_authenticated@3x.png b/src/app/assets/images/common/common_name_authenticated@3x.png
new file mode 100644
index 0000000..821f352
Binary files /dev/null and b/src/app/assets/images/common/common_name_authenticated@3x.png differ
diff --git a/src/app/assets/images/common/common_name_unauthenticated.png b/src/app/assets/images/common/common_name_unauthenticated.png
new file mode 100644
index 0000000..220fb6f
Binary files /dev/null and b/src/app/assets/images/common/common_name_unauthenticated.png differ
diff --git a/src/app/assets/images/common/common_name_unauthenticated@2x.png b/src/app/assets/images/common/common_name_unauthenticated@2x.png
new file mode 100644
index 0000000..98c6f20
Binary files /dev/null and b/src/app/assets/images/common/common_name_unauthenticated@2x.png differ
diff --git a/src/app/assets/images/common/common_name_unauthenticated@3x.png b/src/app/assets/images/common/common_name_unauthenticated@3x.png
new file mode 100644
index 0000000..e7bb2f0
Binary files /dev/null and b/src/app/assets/images/common/common_name_unauthenticated@3x.png differ
diff --git a/src/app/assets/images/common/common_radio_button_selected.png b/src/app/assets/images/common/common_radio_button_selected.png
new file mode 100644
index 0000000..d8ecde8
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_selected.png differ
diff --git a/src/app/assets/images/common/common_radio_button_selected@2x.png b/src/app/assets/images/common/common_radio_button_selected@2x.png
new file mode 100644
index 0000000..1e86c5f
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_selected@2x.png differ
diff --git a/src/app/assets/images/common/common_radio_button_selected@3x.png b/src/app/assets/images/common/common_radio_button_selected@3x.png
new file mode 100644
index 0000000..12809b4
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_selected@3x.png differ
diff --git a/src/app/assets/images/common/common_radio_button_unselected.png b/src/app/assets/images/common/common_radio_button_unselected.png
new file mode 100644
index 0000000..bfc5870
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_unselected.png differ
diff --git a/src/app/assets/images/common/common_radio_button_unselected@2x.png b/src/app/assets/images/common/common_radio_button_unselected@2x.png
new file mode 100644
index 0000000..e0c0aab
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_unselected@2x.png differ
diff --git a/src/app/assets/images/common/common_radio_button_unselected@3x.png b/src/app/assets/images/common/common_radio_button_unselected@3x.png
new file mode 100644
index 0000000..3c2a979
Binary files /dev/null and b/src/app/assets/images/common/common_radio_button_unselected@3x.png differ
diff --git a/src/app/assets/images/common/common_share_session.png b/src/app/assets/images/common/common_share_session.png
new file mode 100644
index 0000000..3fb39f8
Binary files /dev/null and b/src/app/assets/images/common/common_share_session.png differ
diff --git a/src/app/assets/images/common/common_share_session@2x.png b/src/app/assets/images/common/common_share_session@2x.png
new file mode 100644
index 0000000..6ec51ad
Binary files /dev/null and b/src/app/assets/images/common/common_share_session@2x.png differ
diff --git a/src/app/assets/images/common/common_share_session@3x.png b/src/app/assets/images/common/common_share_session@3x.png
new file mode 100644
index 0000000..fe2fa03
Binary files /dev/null and b/src/app/assets/images/common/common_share_session@3x.png differ
diff --git a/src/app/assets/images/common/common_share_timeline.png b/src/app/assets/images/common/common_share_timeline.png
new file mode 100644
index 0000000..bca053f
Binary files /dev/null and b/src/app/assets/images/common/common_share_timeline.png differ
diff --git a/src/app/assets/images/common/common_share_timeline@2x.png b/src/app/assets/images/common/common_share_timeline@2x.png
new file mode 100644
index 0000000..000af9c
Binary files /dev/null and b/src/app/assets/images/common/common_share_timeline@2x.png differ
diff --git a/src/app/assets/images/common/common_share_timeline@3x.png b/src/app/assets/images/common/common_share_timeline@3x.png
new file mode 100644
index 0000000..41d94f7
Binary files /dev/null and b/src/app/assets/images/common/common_share_timeline@3x.png differ
diff --git a/src/app/assets/images/common/tab_home.png b/src/app/assets/images/common/tab_home.png
new file mode 100644
index 0000000..1811b92
Binary files /dev/null and b/src/app/assets/images/common/tab_home.png differ
diff --git a/src/app/assets/images/common/tab_home@2x.png b/src/app/assets/images/common/tab_home@2x.png
new file mode 100644
index 0000000..a833cf0
Binary files /dev/null and b/src/app/assets/images/common/tab_home@2x.png differ
diff --git a/src/app/assets/images/common/tab_home@3x.png b/src/app/assets/images/common/tab_home@3x.png
new file mode 100644
index 0000000..032cc4f
Binary files /dev/null and b/src/app/assets/images/common/tab_home@3x.png differ
diff --git a/src/app/assets/images/common/tab_home_s.png b/src/app/assets/images/common/tab_home_s.png
new file mode 100644
index 0000000..a00ef72
Binary files /dev/null and b/src/app/assets/images/common/tab_home_s.png differ
diff --git a/src/app/assets/images/common/tab_home_s@2x.png b/src/app/assets/images/common/tab_home_s@2x.png
new file mode 100644
index 0000000..7958931
Binary files /dev/null and b/src/app/assets/images/common/tab_home_s@2x.png differ
diff --git a/src/app/assets/images/common/tab_home_s@3x.png b/src/app/assets/images/common/tab_home_s@3x.png
new file mode 100644
index 0000000..ed65816
Binary files /dev/null and b/src/app/assets/images/common/tab_home_s@3x.png differ
diff --git a/src/app/assets/images/common/tab_mine.png b/src/app/assets/images/common/tab_mine.png
new file mode 100644
index 0000000..47eb684
Binary files /dev/null and b/src/app/assets/images/common/tab_mine.png differ
diff --git a/src/app/assets/images/common/tab_mine@2x.png b/src/app/assets/images/common/tab_mine@2x.png
new file mode 100644
index 0000000..7338baf
Binary files /dev/null and b/src/app/assets/images/common/tab_mine@2x.png differ
diff --git a/src/app/assets/images/common/tab_mine@3x.png b/src/app/assets/images/common/tab_mine@3x.png
new file mode 100644
index 0000000..ca77a99
Binary files /dev/null and b/src/app/assets/images/common/tab_mine@3x.png differ
diff --git a/src/app/assets/images/common/tab_mine_s.png b/src/app/assets/images/common/tab_mine_s.png
new file mode 100644
index 0000000..a9b7c22
Binary files /dev/null and b/src/app/assets/images/common/tab_mine_s.png differ
diff --git a/src/app/assets/images/common/tab_mine_s@2x.png b/src/app/assets/images/common/tab_mine_s@2x.png
new file mode 100644
index 0000000..59f77f8
Binary files /dev/null and b/src/app/assets/images/common/tab_mine_s@2x.png differ
diff --git a/src/app/assets/images/common/tab_mine_s@3x.png b/src/app/assets/images/common/tab_mine_s@3x.png
new file mode 100644
index 0000000..19cf3f0
Binary files /dev/null and b/src/app/assets/images/common/tab_mine_s@3x.png differ
diff --git a/src/app/assets/images/common/tab_recommend.png b/src/app/assets/images/common/tab_recommend.png
new file mode 100644
index 0000000..d4c9dcb
Binary files /dev/null and b/src/app/assets/images/common/tab_recommend.png differ
diff --git a/src/app/assets/images/common/tab_recommend@2x.png b/src/app/assets/images/common/tab_recommend@2x.png
new file mode 100644
index 0000000..debce57
Binary files /dev/null and b/src/app/assets/images/common/tab_recommend@2x.png differ
diff --git a/src/app/assets/images/common/tab_recommend@3x.png b/src/app/assets/images/common/tab_recommend@3x.png
new file mode 100644
index 0000000..98c7099
Binary files /dev/null and b/src/app/assets/images/common/tab_recommend@3x.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_down.png b/src/app/assets/images/guide/app_tour_arrow_down.png
new file mode 100644
index 0000000..a0b7711
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_down.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_down@2x.png b/src/app/assets/images/guide/app_tour_arrow_down@2x.png
new file mode 100644
index 0000000..b609f67
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_down@2x.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_down@3x.png b/src/app/assets/images/guide/app_tour_arrow_down@3x.png
new file mode 100644
index 0000000..908581f
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_down@3x.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_up.png b/src/app/assets/images/guide/app_tour_arrow_up.png
new file mode 100644
index 0000000..d4779dd
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_up.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_up@2x.png b/src/app/assets/images/guide/app_tour_arrow_up@2x.png
new file mode 100644
index 0000000..7f8893a
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_up@2x.png differ
diff --git a/src/app/assets/images/guide/app_tour_arrow_up@3x.png b/src/app/assets/images/guide/app_tour_arrow_up@3x.png
new file mode 100644
index 0000000..8f8d4c2
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_arrow_up@3x.png differ
diff --git a/src/app/assets/images/guide/app_tour_star.png b/src/app/assets/images/guide/app_tour_star.png
new file mode 100644
index 0000000..e9bec39
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_star.png differ
diff --git a/src/app/assets/images/guide/app_tour_star@2x.png b/src/app/assets/images/guide/app_tour_star@2x.png
new file mode 100644
index 0000000..7fb16fb
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_star@2x.png differ
diff --git a/src/app/assets/images/guide/app_tour_star@3x.png b/src/app/assets/images/guide/app_tour_star@3x.png
new file mode 100644
index 0000000..2925f7a
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_star@3x.png differ
diff --git a/src/app/assets/images/guide/app_tour_thumb.png b/src/app/assets/images/guide/app_tour_thumb.png
new file mode 100644
index 0000000..91000b8
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_thumb.png differ
diff --git a/src/app/assets/images/guide/app_tour_thumb@2x.png b/src/app/assets/images/guide/app_tour_thumb@2x.png
new file mode 100644
index 0000000..095cf23
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_thumb@2x.png differ
diff --git a/src/app/assets/images/guide/app_tour_thumb@3x.png b/src/app/assets/images/guide/app_tour_thumb@3x.png
new file mode 100644
index 0000000..131108c
Binary files /dev/null and b/src/app/assets/images/guide/app_tour_thumb@3x.png differ
diff --git a/src/app/assets/images/guide/guide_logo.png b/src/app/assets/images/guide/guide_logo.png
new file mode 100644
index 0000000..e570125
Binary files /dev/null and b/src/app/assets/images/guide/guide_logo.png differ
diff --git a/src/app/assets/images/guide/guide_logo@2x.png b/src/app/assets/images/guide/guide_logo@2x.png
new file mode 100644
index 0000000..c2bb80f
Binary files /dev/null and b/src/app/assets/images/guide/guide_logo@2x.png differ
diff --git a/src/app/assets/images/guide/guide_logo@3x.png b/src/app/assets/images/guide/guide_logo@3x.png
new file mode 100644
index 0000000..fa10f3b
Binary files /dev/null and b/src/app/assets/images/guide/guide_logo@3x.png differ
diff --git a/src/app/assets/images/guide/guide_text.png b/src/app/assets/images/guide/guide_text.png
new file mode 100644
index 0000000..8f08b44
Binary files /dev/null and b/src/app/assets/images/guide/guide_text.png differ
diff --git a/src/app/assets/images/guide/guide_text@2x.png b/src/app/assets/images/guide/guide_text@2x.png
new file mode 100644
index 0000000..11af281
Binary files /dev/null and b/src/app/assets/images/guide/guide_text@2x.png differ
diff --git a/src/app/assets/images/guide/guide_text@3x.png b/src/app/assets/images/guide/guide_text@3x.png
new file mode 100644
index 0000000..d0b05ca
Binary files /dev/null and b/src/app/assets/images/guide/guide_text@3x.png differ
diff --git a/src/app/assets/images/home/home_background.png b/src/app/assets/images/home/home_background.png
new file mode 100644
index 0000000..e610fdb
Binary files /dev/null and b/src/app/assets/images/home/home_background.png differ
diff --git a/src/app/assets/images/home/home_background@2x.png b/src/app/assets/images/home/home_background@2x.png
new file mode 100644
index 0000000..18ac17a
Binary files /dev/null and b/src/app/assets/images/home/home_background@2x.png differ
diff --git a/src/app/assets/images/home/home_background@3x.png b/src/app/assets/images/home/home_background@3x.png
new file mode 100644
index 0000000..dfc1fc5
Binary files /dev/null and b/src/app/assets/images/home/home_background@3x.png differ
diff --git a/src/app/assets/images/home/home_bulletin.png b/src/app/assets/images/home/home_bulletin.png
new file mode 100644
index 0000000..34425ec
Binary files /dev/null and b/src/app/assets/images/home/home_bulletin.png differ
diff --git a/src/app/assets/images/home/home_bulletin@2x.png b/src/app/assets/images/home/home_bulletin@2x.png
new file mode 100644
index 0000000..dfe5ec6
Binary files /dev/null and b/src/app/assets/images/home/home_bulletin@2x.png differ
diff --git a/src/app/assets/images/home/home_bulletin@3x.png b/src/app/assets/images/home/home_bulletin@3x.png
new file mode 100644
index 0000000..68d15e6
Binary files /dev/null and b/src/app/assets/images/home/home_bulletin@3x.png differ
diff --git a/src/app/assets/images/home/home_notifications.png b/src/app/assets/images/home/home_notifications.png
new file mode 100644
index 0000000..47e9cc0
Binary files /dev/null and b/src/app/assets/images/home/home_notifications.png differ
diff --git a/src/app/assets/images/home/home_notifications@2x.png b/src/app/assets/images/home/home_notifications@2x.png
new file mode 100644
index 0000000..a62c147
Binary files /dev/null and b/src/app/assets/images/home/home_notifications@2x.png differ
diff --git a/src/app/assets/images/home/home_notifications@3x.png b/src/app/assets/images/home/home_notifications@3x.png
new file mode 100644
index 0000000..aa8b913
Binary files /dev/null and b/src/app/assets/images/home/home_notifications@3x.png differ
diff --git a/src/app/assets/images/home/home_scan.png b/src/app/assets/images/home/home_scan.png
new file mode 100644
index 0000000..c449a05
Binary files /dev/null and b/src/app/assets/images/home/home_scan.png differ
diff --git a/src/app/assets/images/home/home_scan@2x.png b/src/app/assets/images/home/home_scan@2x.png
new file mode 100644
index 0000000..b0a80b5
Binary files /dev/null and b/src/app/assets/images/home/home_scan@2x.png differ
diff --git a/src/app/assets/images/home/home_scan@3x.png b/src/app/assets/images/home/home_scan@3x.png
new file mode 100644
index 0000000..0b3c0b2
Binary files /dev/null and b/src/app/assets/images/home/home_scan@3x.png differ
diff --git a/src/app/assets/images/home/home_section_background.png b/src/app/assets/images/home/home_section_background.png
new file mode 100644
index 0000000..1ab4dd6
Binary files /dev/null and b/src/app/assets/images/home/home_section_background.png differ
diff --git a/src/app/assets/images/home/home_section_background@2x.png b/src/app/assets/images/home/home_section_background@2x.png
new file mode 100644
index 0000000..b9fcb60
Binary files /dev/null and b/src/app/assets/images/home/home_section_background@2x.png differ
diff --git a/src/app/assets/images/home/home_section_background@3x.png b/src/app/assets/images/home/home_section_background@3x.png
new file mode 100644
index 0000000..6d5b48e
Binary files /dev/null and b/src/app/assets/images/home/home_section_background@3x.png differ
diff --git a/src/app/assets/images/home/home_service_bingli.png b/src/app/assets/images/home/home_service_bingli.png
new file mode 100644
index 0000000..6f7cde6
Binary files /dev/null and b/src/app/assets/images/home/home_service_bingli.png differ
diff --git a/src/app/assets/images/home/home_service_bingli@2x.png b/src/app/assets/images/home/home_service_bingli@2x.png
new file mode 100644
index 0000000..c40edaf
Binary files /dev/null and b/src/app/assets/images/home/home_service_bingli@2x.png differ
diff --git a/src/app/assets/images/home/home_service_bingli@3x.png b/src/app/assets/images/home/home_service_bingli@3x.png
new file mode 100644
index 0000000..82f131f
Binary files /dev/null and b/src/app/assets/images/home/home_service_bingli@3x.png differ
diff --git a/src/app/assets/images/home/home_service_contract.png b/src/app/assets/images/home/home_service_contract.png
new file mode 100644
index 0000000..0097f56
Binary files /dev/null and b/src/app/assets/images/home/home_service_contract.png differ
diff --git a/src/app/assets/images/home/home_service_contract@2x.png b/src/app/assets/images/home/home_service_contract@2x.png
new file mode 100644
index 0000000..e561cf2
Binary files /dev/null and b/src/app/assets/images/home/home_service_contract@2x.png differ
diff --git a/src/app/assets/images/home/home_service_contract@3x.png b/src/app/assets/images/home/home_service_contract@3x.png
new file mode 100644
index 0000000..6212da3
Binary files /dev/null and b/src/app/assets/images/home/home_service_contract@3x.png differ
diff --git a/src/app/assets/images/home/home_service_hospital.png b/src/app/assets/images/home/home_service_hospital.png
new file mode 100644
index 0000000..3ab7bef
Binary files /dev/null and b/src/app/assets/images/home/home_service_hospital.png differ
diff --git a/src/app/assets/images/home/home_service_hospital@2x.png b/src/app/assets/images/home/home_service_hospital@2x.png
new file mode 100644
index 0000000..d989284
Binary files /dev/null and b/src/app/assets/images/home/home_service_hospital@2x.png differ
diff --git a/src/app/assets/images/home/home_service_hospital@3x.png b/src/app/assets/images/home/home_service_hospital@3x.png
new file mode 100644
index 0000000..d518350
Binary files /dev/null and b/src/app/assets/images/home/home_service_hospital@3x.png differ
diff --git a/src/app/assets/images/home/home_service_ketang.png b/src/app/assets/images/home/home_service_ketang.png
new file mode 100644
index 0000000..1d54650
Binary files /dev/null and b/src/app/assets/images/home/home_service_ketang.png differ
diff --git a/src/app/assets/images/home/home_service_ketang@2x.png b/src/app/assets/images/home/home_service_ketang@2x.png
new file mode 100644
index 0000000..453194c
Binary files /dev/null and b/src/app/assets/images/home/home_service_ketang@2x.png differ
diff --git a/src/app/assets/images/home/home_service_ketang@3x.png b/src/app/assets/images/home/home_service_ketang@3x.png
new file mode 100644
index 0000000..02c0134
Binary files /dev/null and b/src/app/assets/images/home/home_service_ketang@3x.png differ
diff --git a/src/app/assets/images/home/home_service_suifang.png b/src/app/assets/images/home/home_service_suifang.png
new file mode 100644
index 0000000..572e2b0
Binary files /dev/null and b/src/app/assets/images/home/home_service_suifang.png differ
diff --git a/src/app/assets/images/home/home_service_suifang@2x.png b/src/app/assets/images/home/home_service_suifang@2x.png
new file mode 100644
index 0000000..2e3df10
Binary files /dev/null and b/src/app/assets/images/home/home_service_suifang@2x.png differ
diff --git a/src/app/assets/images/home/home_service_suifang@3x.png b/src/app/assets/images/home/home_service_suifang@3x.png
new file mode 100644
index 0000000..eccb01c
Binary files /dev/null and b/src/app/assets/images/home/home_service_suifang@3x.png differ
diff --git a/src/app/assets/images/home/home_service_yiwangqian.png b/src/app/assets/images/home/home_service_yiwangqian.png
new file mode 100644
index 0000000..a78edcd
Binary files /dev/null and b/src/app/assets/images/home/home_service_yiwangqian.png differ
diff --git a/src/app/assets/images/home/home_service_yiwangqian@2x.png b/src/app/assets/images/home/home_service_yiwangqian@2x.png
new file mode 100644
index 0000000..61463ac
Binary files /dev/null and b/src/app/assets/images/home/home_service_yiwangqian@2x.png differ
diff --git a/src/app/assets/images/home/home_service_yiwangqian@3x.png b/src/app/assets/images/home/home_service_yiwangqian@3x.png
new file mode 100644
index 0000000..7bd76da
Binary files /dev/null and b/src/app/assets/images/home/home_service_yiwangqian@3x.png differ
diff --git a/src/app/assets/images/home/home_share.png b/src/app/assets/images/home/home_share.png
new file mode 100644
index 0000000..c580dc6
Binary files /dev/null and b/src/app/assets/images/home/home_share.png differ
diff --git a/src/app/assets/images/home/home_share@2x.png b/src/app/assets/images/home/home_share@2x.png
new file mode 100644
index 0000000..89fd99b
Binary files /dev/null and b/src/app/assets/images/home/home_share@2x.png differ
diff --git a/src/app/assets/images/home/home_share@3x.png b/src/app/assets/images/home/home_share@3x.png
new file mode 100644
index 0000000..4b6f9b2
Binary files /dev/null and b/src/app/assets/images/home/home_share@3x.png differ
diff --git a/src/app/assets/images/home/home_todo_arrow.png b/src/app/assets/images/home/home_todo_arrow.png
new file mode 100644
index 0000000..0f469c8
Binary files /dev/null and b/src/app/assets/images/home/home_todo_arrow.png differ
diff --git a/src/app/assets/images/home/home_todo_arrow@2x.png b/src/app/assets/images/home/home_todo_arrow@2x.png
new file mode 100644
index 0000000..c0e06c3
Binary files /dev/null and b/src/app/assets/images/home/home_todo_arrow@2x.png differ
diff --git a/src/app/assets/images/home/home_todo_arrow@3x.png b/src/app/assets/images/home/home_todo_arrow@3x.png
new file mode 100644
index 0000000..dc0baef
Binary files /dev/null and b/src/app/assets/images/home/home_todo_arrow@3x.png differ
diff --git a/src/app/assets/images/home/home_todo_hospital.png b/src/app/assets/images/home/home_todo_hospital.png
new file mode 100644
index 0000000..7436af5
Binary files /dev/null and b/src/app/assets/images/home/home_todo_hospital.png differ
diff --git a/src/app/assets/images/home/home_todo_hospital@2x.png b/src/app/assets/images/home/home_todo_hospital@2x.png
new file mode 100644
index 0000000..5cadb10
Binary files /dev/null and b/src/app/assets/images/home/home_todo_hospital@2x.png differ
diff --git a/src/app/assets/images/home/home_todo_hospital@3x.png b/src/app/assets/images/home/home_todo_hospital@3x.png
new file mode 100644
index 0000000..10fad35
Binary files /dev/null and b/src/app/assets/images/home/home_todo_hospital@3x.png differ
diff --git a/src/app/assets/images/home/home_todo_suifang.png b/src/app/assets/images/home/home_todo_suifang.png
new file mode 100644
index 0000000..ce9fe05
Binary files /dev/null and b/src/app/assets/images/home/home_todo_suifang.png differ
diff --git a/src/app/assets/images/home/home_todo_suifang@2x.png b/src/app/assets/images/home/home_todo_suifang@2x.png
new file mode 100644
index 0000000..c7a52d6
Binary files /dev/null and b/src/app/assets/images/home/home_todo_suifang@2x.png differ
diff --git a/src/app/assets/images/home/home_todo_suifang@3x.png b/src/app/assets/images/home/home_todo_suifang@3x.png
new file mode 100644
index 0000000..715bd39
Binary files /dev/null and b/src/app/assets/images/home/home_todo_suifang@3x.png differ
diff --git a/src/app/assets/images/home/home_todo_yiwangqian.png b/src/app/assets/images/home/home_todo_yiwangqian.png
new file mode 100644
index 0000000..ebe7d59
Binary files /dev/null and b/src/app/assets/images/home/home_todo_yiwangqian.png differ
diff --git a/src/app/assets/images/home/home_todo_yiwangqian@2x.png b/src/app/assets/images/home/home_todo_yiwangqian@2x.png
new file mode 100644
index 0000000..dc4bac9
Binary files /dev/null and b/src/app/assets/images/home/home_todo_yiwangqian@2x.png differ
diff --git a/src/app/assets/images/home/home_todo_yiwangqian@3x.png b/src/app/assets/images/home/home_todo_yiwangqian@3x.png
new file mode 100644
index 0000000..15e4e68
Binary files /dev/null and b/src/app/assets/images/home/home_todo_yiwangqian@3x.png differ
diff --git a/src/app/assets/images/login/login_id_card.png b/src/app/assets/images/login/login_id_card.png
new file mode 100644
index 0000000..2ff41c0
Binary files /dev/null and b/src/app/assets/images/login/login_id_card.png differ
diff --git a/src/app/assets/images/login/login_id_card@2x.png b/src/app/assets/images/login/login_id_card@2x.png
new file mode 100644
index 0000000..bc7949c
Binary files /dev/null and b/src/app/assets/images/login/login_id_card@2x.png differ
diff --git a/src/app/assets/images/login/login_id_card@3x.png b/src/app/assets/images/login/login_id_card@3x.png
new file mode 100644
index 0000000..0e77334
Binary files /dev/null and b/src/app/assets/images/login/login_id_card@3x.png differ
diff --git a/src/app/assets/images/login/login_invitation_code.png b/src/app/assets/images/login/login_invitation_code.png
new file mode 100644
index 0000000..ab07478
Binary files /dev/null and b/src/app/assets/images/login/login_invitation_code.png differ
diff --git a/src/app/assets/images/login/login_invitation_code@2x.png b/src/app/assets/images/login/login_invitation_code@2x.png
new file mode 100644
index 0000000..1c0697b
Binary files /dev/null and b/src/app/assets/images/login/login_invitation_code@2x.png differ
diff --git a/src/app/assets/images/login/login_invitation_code@3x.png b/src/app/assets/images/login/login_invitation_code@3x.png
new file mode 100644
index 0000000..5d662f4
Binary files /dev/null and b/src/app/assets/images/login/login_invitation_code@3x.png differ
diff --git a/src/app/assets/images/login/login_logo.png b/src/app/assets/images/login/login_logo.png
new file mode 100644
index 0000000..1783241
Binary files /dev/null and b/src/app/assets/images/login/login_logo.png differ
diff --git a/src/app/assets/images/login/login_logo@2x.png b/src/app/assets/images/login/login_logo@2x.png
new file mode 100644
index 0000000..44714d8
Binary files /dev/null and b/src/app/assets/images/login/login_logo@2x.png differ
diff --git a/src/app/assets/images/login/login_logo@3x.png b/src/app/assets/images/login/login_logo@3x.png
new file mode 100644
index 0000000..d3505ed
Binary files /dev/null and b/src/app/assets/images/login/login_logo@3x.png differ
diff --git a/src/app/assets/images/login/login_password.png b/src/app/assets/images/login/login_password.png
new file mode 100644
index 0000000..a0707ad
Binary files /dev/null and b/src/app/assets/images/login/login_password.png differ
diff --git a/src/app/assets/images/login/login_password@2x.png b/src/app/assets/images/login/login_password@2x.png
new file mode 100644
index 0000000..e6c1de3
Binary files /dev/null and b/src/app/assets/images/login/login_password@2x.png differ
diff --git a/src/app/assets/images/login/login_password@3x.png b/src/app/assets/images/login/login_password@3x.png
new file mode 100644
index 0000000..65da00b
Binary files /dev/null and b/src/app/assets/images/login/login_password@3x.png differ
diff --git a/src/app/assets/images/login/login_phone_number.png b/src/app/assets/images/login/login_phone_number.png
new file mode 100644
index 0000000..df04782
Binary files /dev/null and b/src/app/assets/images/login/login_phone_number.png differ
diff --git a/src/app/assets/images/login/login_phone_number@2x.png b/src/app/assets/images/login/login_phone_number@2x.png
new file mode 100644
index 0000000..c057994
Binary files /dev/null and b/src/app/assets/images/login/login_phone_number@2x.png differ
diff --git a/src/app/assets/images/login/login_phone_number@3x.png b/src/app/assets/images/login/login_phone_number@3x.png
new file mode 100644
index 0000000..3a626aa
Binary files /dev/null and b/src/app/assets/images/login/login_phone_number@3x.png differ
diff --git a/src/app/assets/images/login/login_sms_code.png b/src/app/assets/images/login/login_sms_code.png
new file mode 100644
index 0000000..16bb7b3
Binary files /dev/null and b/src/app/assets/images/login/login_sms_code.png differ
diff --git a/src/app/assets/images/login/login_sms_code@2x.png b/src/app/assets/images/login/login_sms_code@2x.png
new file mode 100644
index 0000000..296e0d2
Binary files /dev/null and b/src/app/assets/images/login/login_sms_code@2x.png differ
diff --git a/src/app/assets/images/login/login_sms_code@3x.png b/src/app/assets/images/login/login_sms_code@3x.png
new file mode 100644
index 0000000..0baf62f
Binary files /dev/null and b/src/app/assets/images/login/login_sms_code@3x.png differ
diff --git a/src/app/assets/images/login/login_username.png b/src/app/assets/images/login/login_username.png
new file mode 100644
index 0000000..a1088e5
Binary files /dev/null and b/src/app/assets/images/login/login_username.png differ
diff --git a/src/app/assets/images/login/login_username@2x.png b/src/app/assets/images/login/login_username@2x.png
new file mode 100644
index 0000000..5cc7a60
Binary files /dev/null and b/src/app/assets/images/login/login_username@2x.png differ
diff --git a/src/app/assets/images/login/login_username@3x.png b/src/app/assets/images/login/login_username@3x.png
new file mode 100644
index 0000000..b41d5a8
Binary files /dev/null and b/src/app/assets/images/login/login_username@3x.png differ
diff --git a/src/app/assets/images/mine/mine_avatar.png b/src/app/assets/images/mine/mine_avatar.png
new file mode 100644
index 0000000..3143783
Binary files /dev/null and b/src/app/assets/images/mine/mine_avatar.png differ
diff --git a/src/app/assets/images/mine/mine_avatar@2x.png b/src/app/assets/images/mine/mine_avatar@2x.png
new file mode 100644
index 0000000..1b8b849
Binary files /dev/null and b/src/app/assets/images/mine/mine_avatar@2x.png differ
diff --git a/src/app/assets/images/mine/mine_avatar@3x.png b/src/app/assets/images/mine/mine_avatar@3x.png
new file mode 100644
index 0000000..5ee51e9
Binary files /dev/null and b/src/app/assets/images/mine/mine_avatar@3x.png differ
diff --git a/src/app/assets/images/mine/mine_background.png b/src/app/assets/images/mine/mine_background.png
new file mode 100644
index 0000000..b096c13
Binary files /dev/null and b/src/app/assets/images/mine/mine_background.png differ
diff --git a/src/app/assets/images/mine/mine_background@2x.png b/src/app/assets/images/mine/mine_background@2x.png
new file mode 100644
index 0000000..d93fdb3
Binary files /dev/null and b/src/app/assets/images/mine/mine_background@2x.png differ
diff --git a/src/app/assets/images/mine/mine_background@3x.png b/src/app/assets/images/mine/mine_background@3x.png
new file mode 100644
index 0000000..6f72a97
Binary files /dev/null and b/src/app/assets/images/mine/mine_background@3x.png differ
diff --git a/src/app/assets/images/mine/mine_background_logo.png b/src/app/assets/images/mine/mine_background_logo.png
new file mode 100644
index 0000000..0564f2d
Binary files /dev/null and b/src/app/assets/images/mine/mine_background_logo.png differ
diff --git a/src/app/assets/images/mine/mine_background_logo@2x.png b/src/app/assets/images/mine/mine_background_logo@2x.png
new file mode 100644
index 0000000..702abdc
Binary files /dev/null and b/src/app/assets/images/mine/mine_background_logo@2x.png differ
diff --git a/src/app/assets/images/mine/mine_background_logo@3x.png b/src/app/assets/images/mine/mine_background_logo@3x.png
new file mode 100644
index 0000000..672b59f
Binary files /dev/null and b/src/app/assets/images/mine/mine_background_logo@3x.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support.png b/src/app/assets/images/mine/mine_contact_support.png
new file mode 100644
index 0000000..69bfe73
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support@2x.png b/src/app/assets/images/mine/mine_contact_support@2x.png
new file mode 100644
index 0000000..31e8034
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support@2x.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support@3x.png b/src/app/assets/images/mine/mine_contact_support@3x.png
new file mode 100644
index 0000000..c5847fd
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support@3x.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support_background.png b/src/app/assets/images/mine/mine_contact_support_background.png
new file mode 100644
index 0000000..d9fbfa1
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support_background.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support_background@2x.png b/src/app/assets/images/mine/mine_contact_support_background@2x.png
new file mode 100644
index 0000000..fa4a319
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support_background@2x.png differ
diff --git a/src/app/assets/images/mine/mine_contact_support_background@3x.png b/src/app/assets/images/mine/mine_contact_support_background@3x.png
new file mode 100644
index 0000000..ea45a28
Binary files /dev/null and b/src/app/assets/images/mine/mine_contact_support_background@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_feedback.png b/src/app/assets/images/mine/mine_feature_feedback.png
new file mode 100644
index 0000000..9436033
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_feedback.png differ
diff --git a/src/app/assets/images/mine/mine_feature_feedback@2x.png b/src/app/assets/images/mine/mine_feature_feedback@2x.png
new file mode 100644
index 0000000..aaf897d
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_feedback@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_feedback@3x.png b/src/app/assets/images/mine/mine_feature_feedback@3x.png
new file mode 100644
index 0000000..88b8a4a
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_feedback@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_hospitals.png b/src/app/assets/images/mine/mine_feature_hospitals.png
new file mode 100644
index 0000000..88ab6f4
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_hospitals.png differ
diff --git a/src/app/assets/images/mine/mine_feature_hospitals@2x.png b/src/app/assets/images/mine/mine_feature_hospitals@2x.png
new file mode 100644
index 0000000..e108e1c
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_hospitals@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_hospitals@3x.png b/src/app/assets/images/mine/mine_feature_hospitals@3x.png
new file mode 100644
index 0000000..42f0768
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_hospitals@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_notifications.png b/src/app/assets/images/mine/mine_feature_notifications.png
new file mode 100644
index 0000000..4dede29
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_notifications.png differ
diff --git a/src/app/assets/images/mine/mine_feature_notifications@2x.png b/src/app/assets/images/mine/mine_feature_notifications@2x.png
new file mode 100644
index 0000000..425fd8e
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_notifications@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_notifications@3x.png b/src/app/assets/images/mine/mine_feature_notifications@3x.png
new file mode 100644
index 0000000..5fc9d6d
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_notifications@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_password.png b/src/app/assets/images/mine/mine_feature_password.png
new file mode 100644
index 0000000..b3c851a
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_password.png differ
diff --git a/src/app/assets/images/mine/mine_feature_password@2x.png b/src/app/assets/images/mine/mine_feature_password@2x.png
new file mode 100644
index 0000000..9e36a23
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_password@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_password@3x.png b/src/app/assets/images/mine/mine_feature_password@3x.png
new file mode 100644
index 0000000..d513ca0
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_password@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_privilege.png b/src/app/assets/images/mine/mine_feature_privilege.png
new file mode 100644
index 0000000..0a6a0b3
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_privilege.png differ
diff --git a/src/app/assets/images/mine/mine_feature_privilege@2x.png b/src/app/assets/images/mine/mine_feature_privilege@2x.png
new file mode 100644
index 0000000..6570fa6
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_privilege@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_privilege@3x.png b/src/app/assets/images/mine/mine_feature_privilege@3x.png
new file mode 100644
index 0000000..40b38e9
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_privilege@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_role.png b/src/app/assets/images/mine/mine_feature_role.png
new file mode 100644
index 0000000..07d3144
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_role.png differ
diff --git a/src/app/assets/images/mine/mine_feature_role@2x.png b/src/app/assets/images/mine/mine_feature_role@2x.png
new file mode 100644
index 0000000..5e93f5e
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_role@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_role@3x.png b/src/app/assets/images/mine/mine_feature_role@3x.png
new file mode 100644
index 0000000..f5b1f0d
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_role@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_service.png b/src/app/assets/images/mine/mine_feature_service.png
new file mode 100644
index 0000000..6239a2d
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_service.png differ
diff --git a/src/app/assets/images/mine/mine_feature_service@2x.png b/src/app/assets/images/mine/mine_feature_service@2x.png
new file mode 100644
index 0000000..853cf2b
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_service@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_service@3x.png b/src/app/assets/images/mine/mine_feature_service@3x.png
new file mode 100644
index 0000000..cca1545
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_service@3x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_settings.png b/src/app/assets/images/mine/mine_feature_settings.png
new file mode 100644
index 0000000..e49fed7
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_settings.png differ
diff --git a/src/app/assets/images/mine/mine_feature_settings@2x.png b/src/app/assets/images/mine/mine_feature_settings@2x.png
new file mode 100644
index 0000000..1b9da4c
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_settings@2x.png differ
diff --git a/src/app/assets/images/mine/mine_feature_settings@3x.png b/src/app/assets/images/mine/mine_feature_settings@3x.png
new file mode 100644
index 0000000..1a2f134
Binary files /dev/null and b/src/app/assets/images/mine/mine_feature_settings@3x.png differ
diff --git a/src/app/assets/images/scan/scan_album.png b/src/app/assets/images/scan/scan_album.png
new file mode 100644
index 0000000..65c5e5f
Binary files /dev/null and b/src/app/assets/images/scan/scan_album.png differ
diff --git a/src/app/assets/images/scan/scan_album@2x.png b/src/app/assets/images/scan/scan_album@2x.png
new file mode 100644
index 0000000..d16f6a9
Binary files /dev/null and b/src/app/assets/images/scan/scan_album@2x.png differ
diff --git a/src/app/assets/images/scan/scan_album@3x.png b/src/app/assets/images/scan/scan_album@3x.png
new file mode 100644
index 0000000..41a03ba
Binary files /dev/null and b/src/app/assets/images/scan/scan_album@3x.png differ
diff --git a/src/app/assets/images/scan/scan_animation_line.png b/src/app/assets/images/scan/scan_animation_line.png
new file mode 100644
index 0000000..d31e124
Binary files /dev/null and b/src/app/assets/images/scan/scan_animation_line.png differ
diff --git a/src/app/assets/images/scan/scan_animation_line@2x.png b/src/app/assets/images/scan/scan_animation_line@2x.png
new file mode 100644
index 0000000..72ddc4b
Binary files /dev/null and b/src/app/assets/images/scan/scan_animation_line@2x.png differ
diff --git a/src/app/assets/images/scan/scan_animation_line@3x.png b/src/app/assets/images/scan/scan_animation_line@3x.png
new file mode 100644
index 0000000..9b5e558
Binary files /dev/null and b/src/app/assets/images/scan/scan_animation_line@3x.png differ
diff --git a/src/app/assets/images/scan/scan_back.png b/src/app/assets/images/scan/scan_back.png
new file mode 100644
index 0000000..ec0b6df
Binary files /dev/null and b/src/app/assets/images/scan/scan_back.png differ
diff --git a/src/app/assets/images/scan/scan_back@2x.png b/src/app/assets/images/scan/scan_back@2x.png
new file mode 100644
index 0000000..3ddcb76
Binary files /dev/null and b/src/app/assets/images/scan/scan_back@2x.png differ
diff --git a/src/app/assets/images/scan/scan_back@3x.png b/src/app/assets/images/scan/scan_back@3x.png
new file mode 100644
index 0000000..36416b4
Binary files /dev/null and b/src/app/assets/images/scan/scan_back@3x.png differ
diff --git a/src/app/contexts/AppProvider.tsx b/src/app/contexts/AppProvider.tsx
index ac73408..c01e8fb 100644
--- a/src/app/contexts/AppProvider.tsx
+++ b/src/app/contexts/AppProvider.tsx
@@ -1,6 +1,6 @@
import React, { ReactNode } from 'react';
-import { CommonProvider } from './CommonContext';
-import { AuthProvider } from './AuthContext';
+import { AuthProvider } from '@common/contexts/AuthContext.tsx';
+import { CommonProvider } from '@common/contexts/CommonContext.tsx';
export const AppProvider = ({ children }: { children: ReactNode }) => {
return (
diff --git a/src/app/hooks/useLogin.ts b/src/app/hooks/useLogin.ts
new file mode 100644
index 0000000..f168239
--- /dev/null
+++ b/src/app/hooks/useLogin.ts
@@ -0,0 +1,36 @@
+import { useCallback } from 'react';
+import { showMessage } from '@common/ToastHelper.ts';
+import { useAuth } from '@common/contexts/useAuth.ts';
+import { useCommon } from '@common/contexts/useCommon.ts';
+import { RequiredUserInfo } from '@common/contexts/AuthContext.tsx';
+
+/**
+ * @description: 处理登录的 hook,可以合并处理登录时的一些操作
+ */
+const useLogin = () => {
+ const {
+ actions: { login },
+ } = useAuth();
+
+ const {
+ actions: { save },
+ } = useCommon();
+
+ return useCallback(
+ (token: string, userInfo: RequiredUserInfo) => {
+ login(token, userInfo).catch(e => {
+ showMessage(e.message);
+ });
+ // 如果手机号不是上次登录的手机号,则清除本地证书
+ // if (userInfo.phone !== info.lastLoginPhoneNumber) {
+ // clearCert();
+ save('lastLoginPhoneNumber', userInfo.phone).catch(e => {
+ showMessage(e.message);
+ });
+ // }
+ },
+ [login, save],
+ );
+};
+
+export { useLogin };
diff --git a/src/app/hooks/useLogout.ts b/src/app/hooks/useLogout.ts
new file mode 100644
index 0000000..3cce7bd
--- /dev/null
+++ b/src/app/hooks/useLogout.ts
@@ -0,0 +1,56 @@
+import { useMemo } from 'react';
+import { COMMONINFO_KEY, TOKEN_KEY, USERINFO_KEY } from '@common/constants';
+import { useCopilot } from 'react-native-copilot';
+import { getAllKeys, removeAllItems } from '@common/StorageHelper.ts';
+import { useAuth } from '@common/contexts/useAuth.ts';
+
+/**
+ * @description: 处理退出登录的 hook,可以合并处理退出登录时的一些操作
+ */
+const useLogout = (): {
+ actions: {
+ logout: () => Promise;
+ };
+} => {
+ const {
+ actions: { logout },
+ } = useAuth();
+
+ const { stop } = useCopilot();
+
+ const actions = useMemo(
+ () => ({
+ logout: async () => {
+ // ImManager.getInstance().logout();
+ // PushManager.getInstance().logout();
+
+ // 退出登录将旧模块中的内容清空
+ // --- 这部分内容等旧模块不再使用后就可以删除了 ---
+ const keys = await getAllKeys();
+ // 保留的 key
+ const keysToKeep = [
+ COMMONINFO_KEY,
+ TOKEN_KEY,
+ USERINFO_KEY,
+ 'follow_first',
+ ];
+ // 过滤掉需要保留的 key
+ const keysToDelete = keys.filter(key => !keysToKeep.includes(key));
+ // 删除剩余的 key
+ await removeAllItems(keysToDelete);
+ // --- 这部分内容等旧模块不再使用后就可以删除了 ---
+
+ stop().catch(() => {
+ return;
+ });
+ await logout();
+ },
+ }),
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ [logout],
+ );
+
+ return { actions: actions };
+};
+
+export { useLogout };
diff --git a/src/app/hooks/useUpdateRef.ts b/src/app/hooks/useUpdateRef.ts
new file mode 100644
index 0000000..2e95697
--- /dev/null
+++ b/src/app/hooks/useUpdateRef.ts
@@ -0,0 +1,11 @@
+import {useRef, useEffect} from 'react';
+
+export default function useUpdateRef(value: T) {
+ const ref = useRef(value);
+
+ useEffect(() => {
+ ref.current = value;
+ }, [value]);
+
+ return ref;
+}
diff --git a/src/app/routes/MainStack.tsx b/src/app/routes/MainStack.tsx
index 1e687cd..b912d13 100644
--- a/src/app/routes/MainStack.tsx
+++ b/src/app/routes/MainStack.tsx
@@ -9,7 +9,7 @@ import {
HEADER_TINT_COLOR,
HEADER_TITLE_FONT_SIZE,
HEADER_TITLE_FONT_WEIGHT,
-} from '@app/constants';
+} from '@common/constants';
import HeaderBackImage from '@common/components/HeaderBackImage.tsx';
const MainViewScreen = register({
diff --git a/src/app/routes/NavigationContainer.tsx b/src/app/routes/NavigationContainer.tsx
index 7aa67be..927b11f 100644
--- a/src/app/routes/NavigationContainer.tsx
+++ b/src/app/routes/NavigationContainer.tsx
@@ -1,11 +1,91 @@
-import React from 'react';
+import React, { useCallback, useEffect } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { MainStack } from '@app/routes/MainStack.tsx';
+import useUpdateRef from '@app/hooks/useUpdateRef.ts';
+import { useAuth } from '@common/contexts/useAuth.ts';
+import { useAccountInfo } from '@app/routes/api';
+import { showErrorMessage } from '@common/ToastHelper.ts';
+import { Image, StyleSheet, View } from 'react-native';
+import { LoginStack } from '@app/routes/login/LoginStack.tsx';
export default function Container() {
+ const {
+ state: { loading: authLoading, token, userInfo },
+ actions: { update },
+ } = useAuth();
+ const userInfoRef = useUpdateRef(userInfo);
+
+ const { fetchAsync: accountInfoFetch } = useAccountInfo(); // 获取用户信息
+
+ // 获取用户信息接口调用的处理
+ const handleAccountInfoFetch = useCallback(() => {
+ if (token === undefined) {
+ return;
+ }
+ accountInfoFetch()
+ .then(r => {
+ if (userInfoRef.current) {
+ update({
+ ...userInfoRef.current,
+ nickname: r.nickname,
+ phone: r.phoneNum,
+ moduleList: r.advertisementModuleList,
+ realNameStatus: r.realNameStatus,
+ picUrl: r.picUrl,
+ userIdCardNum: r.userIdCardNum,
+ faceDetectFirms: r.faceDetectFirms,
+ enableCert: r.enableCert,
+ gxLeader: r.gxLeader,
+ hasBindFirm: r.hasBindFirm,
+ hasFaceDetect: r.hasFaceDetect,
+ });
+ }
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }, [accountInfoFetch, token, update, userInfoRef]);
+
+ // 已经登录状态获取一次用户信息
+ useEffect(() => {
+ if (token === undefined) {
+ return;
+ }
+ handleAccountInfoFetch();
+ }, [handleAccountInfoFetch, token]);
+
+ // 启动加载界面
+ if (authLoading) {
+ return (
+
+
+
+ );
+ }
+
return (
-
+ {token === undefined ? (
+
+ ) : userInfo?.currentUserType === 'guanxin' ? (
+ 冠心
+ ) : (
+
+ )}
);
}
+const styles = StyleSheet.create({
+ loading: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ icon: {
+ width: 90,
+ height: 90,
+ },
+});
diff --git a/src/app/routes/api/index.ts b/src/app/routes/api/index.ts
new file mode 100644
index 0000000..06adac3
--- /dev/null
+++ b/src/app/routes/api/index.ts
@@ -0,0 +1,4 @@
+export * from './useImSign';
+export * from './useAccountInfo';
+export * from './useAppVersion';
+export * from './useAutoSignRequestInfo';
diff --git a/src/app/routes/api/useAccountInfo.ts b/src/app/routes/api/useAccountInfo.ts
new file mode 100644
index 0000000..b95ba8c
--- /dev/null
+++ b/src/app/routes/api/useAccountInfo.ts
@@ -0,0 +1,84 @@
+import {z} from 'zod';
+import { useAuth } from '@common/contexts/useAuth.ts';
+import { useApi } from '@common/api/useApi.ts';
+
+// 实名认证状态 0 未实名认证 1 已实名认证 2 实名认证失败 3 证件号不存在 4 证件号已被其他用户使用
+const RealNameStatus = {
+ Unverified: 0, // 尚未认证
+ Success: 1, // 认证成功
+ Failure: 2, // 实名认证失败
+ Inexistent: 3, // 证件号不存在
+ Registered: 4, // 证件号已被其他用户使用
+} as const;
+
+const accountInfoSchema = z.object({
+ nickname: z.string().optional().default(''), // 昵称
+ phoneNum: z.string(), // 手机号
+ realNameStatus: z.nativeEnum(RealNameStatus).transform(val => {
+ switch (val) {
+ case RealNameStatus.Unverified:
+ return 'unverified';
+ case RealNameStatus.Success:
+ return 'success';
+ case RealNameStatus.Failure:
+ return 'failure';
+ case RealNameStatus.Inexistent:
+ return 'inexistent';
+ case RealNameStatus.Registered:
+ return 'registered';
+ }
+ }), // 实名状态
+ picUrl: z.string().url().optional(), // 头像
+ userIdCardNum: z.string().optional(), // 身份证号
+ // 1、开屏
+ // 2、工作台-banner
+ // 3、热点资讯-列表
+ // 4、热点资讯-详情
+ // 5、我的页面
+ // 6、医信课堂-banner
+ // 7、消息-列表
+ // 8、消息-详情
+ // 9、热点资讯-图片菜单
+ // 10、自动签名页面
+ // 11、授权签名-我的授权码页面
+ // 12、我的授权码-授权成功页面
+ // 13、授权签名-指定授权页面
+ // 14、指定授权-授权签名后页面
+ advertisementModuleList: z.array(z.number()).optional(), // 广告列表
+ faceDetectFirms: z
+ .array(
+ z.object({
+ clientId: z.string(), // 厂商id
+ firmName: z.string(), // 厂商名
+ }),
+ )
+ .optional(), // 人脸识别厂商?
+ enableCert: z.boolean(), // 是否可以下证
+ gxLeader: z.boolean().optional().default(false), // 冠新leader
+ hasBindFirm: z.boolean().optional().default(false), // 是否绑定厂商
+ hasFaceDetect: z.boolean().optional().default(false), // 是否进行了人脸识别
+});
+
+type AccountInfo = z.infer;
+
+// 登录用户个人信息
+// {"changeDevice":false,"enableCert":true,"faceDetectFirms":[],"gxLeader":false,"hasBindFirm":true,"hasFaceDetect":false,"hasPwd":true,"isRealName":true,"nickname":"汪新","phoneNum":"17801465917","picUrl":"https://dev.51trust.com/am/doctor/pic/img?userId=99b00468e337456cb0005ad10d6d88da&version=1720598887000","realName":true,"realNameStatus":1,"userId":"99b00468e337456cb0005ad10d6d88da","userIdCardNum":"630101198001011955","userName":"汪新","userType":2}
+// {"changeDevice":false,"enableCert":true,"faceDetectFirms":[],"gxLeader":false,"hasBindFirm":true,"hasFaceDetect":false,"hasPwd":false,"isRealName":true,"nickname":"齐卫鹏","phoneNum":"13263367627","picUrl":"https://dev.51trust.com/am/doctor/pic/img?userId=551dba9326d34f349902deaffada5af4&version=1679899882000","realName":true,"realNameStatus":1,"userId":"551dba9326d34f349902deaffada5af4","userIdCardNum":"410802198905300075","userName":"齐卫鹏","userType":2}
+const useAccountInfo = () => {
+ const {
+ state: {userInfo},
+ } = useAuth();
+
+ return useApi(
+ '/am/v3/userCenter/getAccountInfo',
+ 'GET',
+ {
+ userId: userInfo?.userId ?? '',
+ },
+ accountInfoSchema,
+ {log: false},
+ );
+};
+
+export type {AccountInfo};
+export {useAccountInfo};
diff --git a/src/app/routes/api/useAppVersion.ts b/src/app/routes/api/useAppVersion.ts
new file mode 100644
index 0000000..8d7b35e
--- /dev/null
+++ b/src/app/routes/api/useAppVersion.ts
@@ -0,0 +1,62 @@
+import {Platform} from 'react-native';
+import {z} from 'zod';
+import { useApi } from '@common/api/useApi.ts';
+
+const appVersionSchema = z.object({
+ version: z.string(), // 版本号
+ msg: z.string().optional().default('无'), // 更新说明
+ force: z.boolean().optional().default(false), // 是否强制更新
+ downloadUrl: z.string().optional(), // 安卓官网下载地址
+ url: z.string().url().optional(), // 安卓下载地址
+ internal: z.boolean().optional().default(true), // 是否应用内下载
+ huawei: z
+ .object({
+ market: z.string().optional(),
+ enable: z.boolean().optional().default(false),
+ })
+ .optional(),
+ xiaomi: z
+ .object({
+ market: z.string().optional(),
+ enable: z.boolean().optional().default(false),
+ })
+ .optional(),
+ oppo: z
+ .object({
+ market: z.string().optional(),
+ enable: z.boolean().optional().default(false),
+ })
+ .optional(),
+ vivo: z
+ .object({
+ market: z.string().optional(),
+ enable: z.boolean().optional().default(false),
+ })
+ .optional(),
+ honor: z
+ .object({
+ market: z.string().optional(),
+ enable: z.boolean().optional().default(false),
+ })
+ .optional(),
+});
+
+// 获取当前最新app版本号,用于检查是否需要更新
+// 安卓:{"downloadUrl": "https://www.51trust.com/download.html", "force": false, "htmlVersion": "1.0.5", "isMaintain": false, "msg": "更新说明 1.新增一键全签功能 2.支持拍摄手写笔记设置签名图片 3.新增患者文件夹展示签名列表 4.修正已知bug", "url": "http://tms-dev.oss-cn-beijing.aliyuncs.com/online_android/yiwangxin.6.6.0.apk", "version": "7.0.1"}
+// iOS:{"force": false, "htmlVersion": "1.0.4", "isMaintain": false, "msg": "更新说明 1. 为医生提供资料库功能,可以手动群发提醒和宣传文章", "url": "", "version": "6.6.6"}
+const useAppVersion = () => {
+ return useApi(
+ Platform.select({
+ android: '/am/v3/common/version/android',
+ ios: '/am/v3/common/version/ios',
+ }) ?? '',
+ 'GET',
+ {},
+ appVersionSchema,
+ {
+ loadingDelay: 500,
+ },
+ );
+};
+
+export {useAppVersion};
diff --git a/src/app/routes/api/useAutoSignRequestInfo.ts b/src/app/routes/api/useAutoSignRequestInfo.ts
new file mode 100644
index 0000000..0ca3d41
--- /dev/null
+++ b/src/app/routes/api/useAutoSignRequestInfo.ts
@@ -0,0 +1,59 @@
+import {z} from 'zod';
+import { useAuth } from '@common/contexts/useAuth.ts';
+import { useApi } from '@common/api/useApi.ts';
+
+const autoSignRequestInfoSchema = z
+ .object({
+ clientId: z.string(), // 厂商id
+ clientNme: z.string().optional(), // 厂商名
+ clientName: z.string().optional(), // 另一个可能的厂商名
+ userId: z.string().optional(),
+ sysTag: z.string().optional(),
+ cossSignId: z.string().nullish(),
+ })
+ .transform(data => {
+ // 如果没有 clientName 但有 clientNme,将其重命名为 clientName
+ if (!data.clientName && data.clientNme) {
+ return {...data, clientName: data.clientNme};
+ }
+ return data;
+ });
+
+// 获取用户是否发起了自动签名授权的请求以及请求信息
+// {"clientId":"2015112716143758","clientNme":"数字医信开发","forceQr":false,"notifyUrl":"http://8.131.71.70:12015/ywxSignBC/autoSignBC","sessionTime":4,"sysTag":"zht","userId":"99b00468e337456cb0005ad10d6d88da"}
+const useAutoSignRequestInfo = () => {
+ const {
+ state: {userInfo},
+ } = useAuth();
+ return useApi(
+ '/am/v3/selfSign/getSelfSignRequest',
+ 'POSTFORM',
+ {
+ userId: userInfo?.userId ?? '',
+ },
+ autoSignRequestInfoSchema,
+ {
+ loadingDelay: 500,
+ },
+ );
+};
+const useConfirmGrantAsk = (firmId?: string) => {
+ return useApi(
+ 'am/v3/grant/getGrantToConfirm',
+ 'POSTJSON',
+ {
+ firmId,
+ },
+ z.array(
+ z.object({
+ businessType: z.string(), // 业务类型
+ content: z.string(), // 授权信息
+ }),
+ ),
+ {
+ loadingDelay: 500,
+ },
+ );
+};
+
+export {useAutoSignRequestInfo, useConfirmGrantAsk};
diff --git a/src/app/routes/api/useImSign.ts b/src/app/routes/api/useImSign.ts
new file mode 100644
index 0000000..f252d4f
--- /dev/null
+++ b/src/app/routes/api/useImSign.ts
@@ -0,0 +1,25 @@
+import {z} from 'zod';
+import { useApi } from '@common/api/useApi.ts';
+
+const imInfoSchema = z.object({
+ account: z.string().min(1),
+ userSig: z.string().min(1),
+});
+
+// 获取腾讯im账号
+// {"account": "fbf2ea4fcbeb4c5da305faa76d2e1b70"}
+const useImSign = () => {
+ return useApi(
+ '/am/v4/userCenter/getImSign',
+ 'POSTJSON',
+ {
+ pushSwitch: '1',
+ },
+ imInfoSchema,
+ {
+ loadingDelay: 500,
+ },
+ );
+};
+
+export {useImSign};
diff --git a/src/app/routes/hooks/useSetSignEnv.ts b/src/app/routes/hooks/useSetSignEnv.ts
new file mode 100644
index 0000000..8c16b44
--- /dev/null
+++ b/src/app/routes/hooks/useSetSignEnv.ts
@@ -0,0 +1,31 @@
+// import { useEffect } from 'react';
+// import { useEnv } from '@common/env/useEnv.ts';
+
+/**
+ * @description: 初始化医网签环境
+ */
+// const useSetSignEnv = () => {
+// const { env } = useEnv();
+ // const {
+ // actions: { setServerUrl },
+ // } = useYWXSign();
+
+// useEffect(() => {
+// switch (env) {
+// case 'production':
+// setServerUrl(SignManager.envType.public);
+// break;
+// case 'acceptance':
+// setServerUrl(SignManager.envType.integrate);
+// break;
+// case 'testing':
+// setServerUrl(SignManager.envType.test);
+// break;
+// case 'development':
+// setServerUrl(SignManager.envType.dev);
+// break;
+// }
+// }, [env, setServerUrl]);
+// };
+
+// export { useSetSignEnv };
diff --git a/src/app/routes/login/LoginParamList.ts b/src/app/routes/login/LoginParamList.ts
new file mode 100644
index 0000000..f8eab77
--- /dev/null
+++ b/src/app/routes/login/LoginParamList.ts
@@ -0,0 +1,18 @@
+export type LoginParamList = {
+ WebView: {
+ url: string; // 地址
+ title?: string; // 标题
+ }; // 通用的 webview 页面
+ Environment: undefined; // 环境切换
+ Agreement: undefined; // 用户协议和隐私政策同意
+ AgreementContent: undefined; // 用户协议内容
+ Login: undefined; // 登录主页面
+ Register: undefined; // 注册
+ LoginWithSMS: {
+ phoneNumber: string;
+ }; // 验证码登录页面
+ ForgotPassword: {
+ phoneNumber: string;
+ }; // 忘记密码
+ ChangePhone: undefined; // 修改手机号
+};
diff --git a/src/app/routes/login/LoginStack.tsx b/src/app/routes/login/LoginStack.tsx
new file mode 100644
index 0000000..4b08881
--- /dev/null
+++ b/src/app/routes/login/LoginStack.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import {
+ createStackNavigator,
+ TransitionPresets,
+} from '@react-navigation/stack';
+
+import HeaderBackImage from '@common/components/HeaderBackImage.tsx';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import {
+ HEADER_TINT_COLOR,
+ HEADER_TITLE_FONT_SIZE,
+ HEADER_TITLE_FONT_WEIGHT,
+} from '@common/constants';
+import { CommonParamList } from '@common/router/CommonParamList.ts';
+import LoginScreen from '@app/screens/login/login/LoginScreen.tsx';
+import RegisterScreen from '@app/screens/login/register/RegisterScreen.tsx';
+import EnvironmentScreen from '@app/screens/login/environment/EnvironmentScreen.tsx';
+import LoginWithSMSScreen from '@app/screens/login/loginWithSMS/LoginWithSMSScreen.tsx';
+import ForgotPasswordScreen from '@app/screens/login/forgotPassword/ForgotPasswordScreen.tsx';
+import ChangePhoneScreen from '@app/screens/login/changePhone/ChangePhoneScreen.tsx';
+import AgreementScreen from '@app/screens/login/agreement/AgreementScreen.tsx';
+import { AgreementContentScreen } from '@app/routes/login/index.ts';
+import WebViewScreen from '@common/screens/webview/WebViewScreen.tsx';
+// import LoginScreen from '@app/screens/login/login/LoginScreen.tsx';
+
+// const AgreementContentScreen = register({
+// loader: () =>
+// import('@app/screens/login/agreement/AgreementContentScreen.tsx'),
+// });
+// const LoginScreen = register({
+// loader: () => import('@app/screens/login/login/LoginScreen.tsx'),
+// });
+// const RegisterScreen = register({
+// loader: () => import('@app/screens/login/register/RegisterScreen.tsx'),
+// });
+// const WebViewScreen = register({
+// loader: () => import('@common/screens/webview/WebViewScreen.tsx'),
+// });
+// const EnvironmentScreen = register({
+// loader: () => import('@app/screens/login/environment/EnvironmentScreen.tsx'),
+// });
+// const LoginWithSMSScreen = register({
+// loader: () => import('@app/screens/login/loginWithSMS/LoginWithSMSScreen.tsx'),
+// });
+// const ForgotPasswordScreen = register({
+// loader: () =>
+// import('@app/screens/login/forgotPassword/ForgotPasswordScreen.tsx'),
+// });
+// const ChangePhoneScreen = register({
+// loader: () => import('@app/screens/login/changePhone/ChangePhoneScreen.tsx'),
+// });
+// const AgreementScreen = register({
+// loader: () => import('@app/screens/login/agreement/AgreementScreen.tsx'),
+// });
+
+const Stack = createStackNavigator();
+
+export function LoginStack() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {/* 普通栈路由 --> 写这里 */}
+
+
+
+ {/* 对话框形式路由 --> 写这里 */}
+
+
+ );
+}
diff --git a/src/app/routes/login/index.ts b/src/app/routes/login/index.ts
new file mode 100644
index 0000000..d5a8341
--- /dev/null
+++ b/src/app/routes/login/index.ts
@@ -0,0 +1,5 @@
+import { register } from 'react-native-bundle-splitter';
+
+export const AgreementContentScreen = register({
+ loader: () => import('@app/screens/login/agreement/AgreementContentScreen.tsx'),
+});
diff --git a/src/app/screens/login/agreement/AgreementContentScreen.tsx b/src/app/screens/login/agreement/AgreementContentScreen.tsx
new file mode 100644
index 0000000..f4b272e
--- /dev/null
+++ b/src/app/screens/login/agreement/AgreementContentScreen.tsx
@@ -0,0 +1,245 @@
+import React, {useLayoutEffect, useMemo} from 'react';
+import {ScrollView, StyleSheet, Text, View} from 'react-native';
+import {StackScreenProps} from '@react-navigation/stack';
+import {LoginParamList} from '@app/routes/login/LoginParamList.ts';
+
+type Props = StackScreenProps;
+
+export default function AgreementContentScreen(props: Props) {
+ const {navigation} = props;
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '用户服务协议',
+ });
+ }, [navigation]);
+
+ const agreementContent = useMemo(() => {
+ //
+ return `
+ 发布日期: 2022年1月
+ 生效日期: 2022年1月
+
+ 尊敬的用户,欢迎您使用“医网信APP”软件,该软件由北京数字医信科技有限公司(下称“医网信”或“本网站”)负责运营。本协议双方为医网信与医网信注册用户(下称“用户”或“您”),本协议具有合同效力,适用于您在本网站的全部活动。
+ 一、注册声明
+ (一)请您在注册成为医网信用户前务必仔细阅读本协议,若您不同意本协议的任意内容,或者无法准确理解医网信对条款的解释,请不要进行后续操作;若您注册成为医网信用户,则表示您对本协议的全部内容已充分阅读并认可和同意遵守。同时,承诺遵守中国法律、法规、规章及其他政府规范性文件的规定,如有违反而造成任何法律后果,您将以本人名义独立承担所有相应的法律责任。
+ (二)本协议内容包括以下条款及已经发布的或将来可能发布的各类规则。所有规则为本协议不可分割的一部分,与协议正文具有同等法律效力。医网信有权根据需要不定时地制定、修改本协议或各类规则,如本协议及规则有任何变更,一切变更以本网站最新公布的内容为准。经修订的协议、规则一经公布,立即自动生效,对新协议、规则生效之后注册的用户发生法律效力。对于协议、规则生效之前注册的用户,若用户在新规则生效后继续使用本网站提供的各项服务,则表明用户已充分阅读并认可和同意遵守新的协议或规则。若您拒绝接受新的协议和规则,您必须放弃使用医网信提供的各项服务(包括但不限于使用用户名和密码登录医网信、在医网信上进行电子签名、合同签署等)。
+ (三)您使用医网信电子签名服务即表示您同意医网信将您的身份信息、实名认证数据、签署过程数据、签署文档的摘要值保存,且该等信息不因您的账户停用或注销而停止保存,并在签署相关方申请出证时向其提供。
+ (四)用户使用医网信电子签名服务签署处方、合同或其他医疗文件等文书,即应视为同意使用电子签名及数据电文,并认可电子签名与手写签名或者盖章具有同等的法律效力。用户使用电子签名服务前,应了解并同意遵守数据证书和电子印章的相关使用规则,详见本协议附件。
+
+ 二、定义及解释
+ (一)医网信账户
+ 指您获得医网信账号以供您使用医网信各项服务的账户。
+ (二)本网站
+ 除本协议另有规定外,指www.51trust.com及相关移动客户端应用程序。
+ (三)CA机构
+ 指获得《电子认证服务许可证》的第三方数字证书签发机构。
+ (四)电子签名认证证书
+ 即数字证书,指由第三方CA签发的,含有签发电子签名认证证书的电子认证服务机构名称、证书持有人名称、证书序列号、证书有效期、证书持有人的电子签名验证数据、电子认证服务机构的电子签名、工业和信息化部规定的其他内容的身份证明数据。
+ (五)电子签名
+ 指数据电文中以电子形式所含、所附用于识别签名人身份并表明签名人认可其中内容的数据。
+ (六)数据电文
+ 指以电子、光学、磁或者类似手段生成、发送、接收或者储存的信息。
+
+ 三、医网信服务简介
+ (一)电子签名服务
+ 采用数字签名、时间戳、电子印章等技术,实现对数据电文的安全保护和签章信息的图形化展现,明确电子文件签署者各方的身份信息、签署行为的过程以及签署完成后文件改动,从而使用户在线完成文件的签署。
+ (二)文档及证据保存、下载服务
+ 用户签署的文件在完成后,将由医网信对签署完成的文件本身以及签署的流程信息等内容进行保存,签署方可随时下载、管理签署完成的文件。
+ 四、服务费用
+ (一)当用户使用本网站服务时,医网信有权向用户收取相应服务费用。
+ (二)医网信在避免用户损失的前提下,有权单方决定暂时或永久地改变或停止提供某些服务。
+ (三)用户购买签名服务后,未开始消耗套餐用量的,自付款之日起七个日历天内,可向医网信申请退还已支付的相应服务费用。
+ (四)除上述情形外,医网信不接受其它任何情况下非医网信原因的退费申请。医网信不因用户账号注销、套餐有效期届满等原因而负有退还用户已支付费用的义务。
+
+ 五、用户账户
+ (一)医网信用户
+ 医网信的用户为购买服务的医院下用户。指通过医院提供的账号完成全部认证程序后,使用医网信提供的服务,并且符合中华人民共和国法律规定的年满14周岁的自然人。
+ (二)用户注册与实名认证
+ 1.成为医网信用户须进行实名认证。在注册与实名认证过程中个人及企业必须根据本网站的要求提供最新、真实、有效及完整的资料。其中,根据实名认证方式的不同,个人所需提供的实名认证资料将有所差异,具体所需资料请以本网站要求为准。
+ 2.实名认证具体操作流程请严格按照本网站实名认证规定的流程履行,否则将影响实名审核的结果。
+ 3.用户同意并授权医网信审核、核对用户资料信息以确认用户身份,但用户信息是由用户本人自行提供的,故医网信无法保证该信息之准确、有效和完整。所提交的用户认证信息将用于核发其专属的电子签名文件,医网信将对用户的信息进行严格保密。用户信息及隐私保护请详见《隐私政策》。
+ 4.用户实名认证信息变更的,应及时在医网信进行更新并重新进行实名认证,未及时更新导致数字证书签发错误的,医网信不承担责任。
+ (三)账户安全
+ 1.医网信将通过用户的登录名、密码以及登录后的其他操作识别用户的相关指示,同时医网信会通过本服务应用识别来自用户使用的各类产品或设备的指示,用户应当妥善保管医网信登录名、密码、身份信息,各类产品或设备,对于因用户泄露登录名、密码、身份信息或是因产品或设备遗失导致的损失,由用户自行承担。用户应在登录本系统时段结束时,以正确步骤离开网站。用户如果发现有他人冒用或盗用其医网信登录名及密码或任何其他未经合法授权之情形,或发生与医网信账户关联的手机或其他设备遗失或其他可能危及到医网信账户安全情形时,应立即以有效方式通知医网信,并申请账户停用。
+ 2.用户应对其持有的医网信账户负责,只有用户本人方可使用该账户。该账户不可转让、不可继承,在您决定不再使用该账户时,您可以将该账户内的数据导出或删除,并按医网信规定流程申请停用该账户。
+ 3.医网信可以暂时停止提供或者限制本服务部分功能,或提供新的功能,在任何功能减少、增加或者变化时,只要用户仍然使用本服务,即表示同意本协议或者变更后的协议。
+ 4.医网信对因用户未能遵守本协议的约定而发生的任何直接或间接损失等不利后果不承担任何责任。
+ 5.医网信有权了解您使用本产品或服务的真实背景及目的,用户应如实提供医网信所需的真实、全面、准确的信息。如果医网信有合理理由怀疑用户提供虚假信息、进行欺诈等违法违规行为的,医网信有权根据相关国家法律法规的规定对用户个人信息以及签署的文件信息等进行核实、查询、披露,有权停用相应账户并配合相关机关进行后续调查。
+ (四)账户停用
+ 1. 用户在需要停止本协议约定的服务时,可申请停用医网信账户。用户申请停用的医网信账户应当是依照本协议的约定注册并由医网信提供给用户本人的账户,用户应当依照医网信的规定程序申请账户停用。
+ 2.如果用户在使用本系统相关服务时有涉及违法违规行为的,医网信有权通知您并停用您名下的全部或部分账户。
+ (五)账户注销
+ 1. 在您需要终止使用我们的服务时,您可以申请注销您的账户,您可以联系我们的人工客服(电话:010-58543633),申请注销您的账户。
+ 我们在此善意地提醒您,您注销账户的行为会使您无法继续使用医网信的相关服务。注销账户后您的个人信息会保持不可被检索、访问的状态,我们将不会再使用或对外提供与该账户相关的个人信息,但您在使用医网信服务期间提供或产生的信息我们仍需按照监管要求的保存5年以上,且在保存的时间内依法配合有权机关的查询。
+
+ 六、通知与送达
+ (一)通知方式
+ 本协议条款及任何其他的协议、公告或其他关于您使用本服务账号及服务的通知,您同意本网站使用电子方式通知您。电子方式包括但不限于以电子邮件方式、或于本网站或者合作网站上公布、或无线通讯装置通知等方式。
+ (二)送达
+ 网站的通知如以公示方式做出,一经在本网站公示即视为已经送达。除此之外,其他向您个人发布的具有专属性的通知将由本网站在您注册时或者注册后变更用户信息时向本网站提供的电子邮箱或用户注册后在本网站绑定的手机号码发送,一经发送即视为已经送达。请您密切关注用户的电子邮箱以及手机中的短信信息。因信息传输或您变更邮箱但未在本网站的注册信息中填写您变更后的邮箱等原因导致您未在前述通知发出当日收到该等通知的,本网站不承担责任。
+ (三)通知内容
+ 您同意医网信利用在本网站登记的联系方式与您联络并向您传递相关的信息,包括但不限于行政管理方面的通知、产品信息、有关您使用本网站的通讯以及针对性的广告等。
+
+ 七、使用事项
+ (一)您在使用医网信服务时,必须遵守国家各项法律、法规、规章以及政府规范性文件,并接受医网信相关的协议、规则、规定、程序和惯例的约束。禁止在本网站进行任何可能违反国家法律、法规、规章和政府规范性文件的行为(包括但不限于侵犯任何第三方著作权、专利权、商标权、商业秘密、隐私权、名誉权或其它权利的行为)或者任何未经授权使用本网站的行为(包括但不限于擅自进入本网站未公开的系统、不正当的使用密码和网站的任何内容等)。
+ (二)每位用户都拥有一个属于自己的用户名和登录密码,通过使用该用户名和登录密码您可以登录医网信,也可以通过手机接收验证码的方式登录医网信,从而使用医网信服务。您须妥善保管您在医网信上的用户名和密码。本网站通过您的用户名、密码以及登录后的操作来识别您的指令。您确认,使用您的用户名和密码登录本网站后在本网站的一切行为均代表您本人意志。使用您的用户名和密码登录操作所产生的电子信息记录均为您行为的有效凭据,并由您本人承担相应的法律后果。不得在未经医网信许可的情况下出售或授权使用账号。
+ (三)您对通过医网信发布的所有信息(内容包括但不限于个人信息、商业信息,形式包括但不限于文字、图片)的真实性、准确性、即时性、完整性和合法性独立承担所有责任。同时,在您所发布的信息中不得含有蓄意毁坏、恶意干扰、秘密地截取或侵占任何系统、数据或个人资料的任何病毒、伪装破坏程序、电脑蠕虫、定时程序炸弹或其他电脑程序。
+ (四)请注意:根据《电子签名法》的规定下列情形不适合使用电子文书:涉及婚姻、收养、继承等人身关系的;涉及停止供水、供热、供气等公用事业服务的;法律、行政法规规定的不适用电子文书的其他情形。因此,用户不得在医网信平台上签订上述法律文件。若用户在医网信平台上传并签订上述法律文件,而根据法律之规定上述签约行为无效的,医网信不承担任何责任。
+ (五)本网站内容可能涉及由第三方所有、控制或运营的其它网站(以下称“第三方网站”)。本网站不能保证也没有义务保证第三方网站上的信息的真实性和有效性。您应按照第三方网站的相关协议与规则使用第三方网站,而不是按照本协议。第三方网站的内容、产品、广告和其他任何信息均由您自行判断并承担风险,而与本网站无关。
+
+ 八、用户间纠纷处理
+ (一)用户应在使用医网信服务时,应审慎核查签署各方的身份信息,对于签署方中有未经医网信实名认证的用户,可能产生签约主体不明或签约主体错误的风险,如需避免此风险,请各签署方进行实名认证并审慎核查签署方身份。否则,因此而产生的风险和责任,由用户自行承担。
+ (二)在使用医网信的过程中,用户发现任何可能侵害自己或医网信权利的事实时,应及时通知医网信并提供相应的证明材料。因投诉不实给医网信或第三方造成损失的,用户应承担法律责任。
+ (三)医网信有权接受并处理您与其他用户间因使用医网信服务而产生的纠纷及投诉,有权通过电话、短信、电子邮件等联系方式向您了解情况,并将所了解的情况通过上述方式通知对方。您有义务在收到医网信通知后,在指定的时间内提供相应的资料,以配合纠纷及投诉的情况了解与处理,否则,医网信有权单方面独立判断其他用户对您的投诉,一旦成立,并做出对您不利的处理结果。同时,医网信有权立即中止或终止您的相关医网信服务。
+ (四)医网信受理、处理纠纷、争议及投诉完全依据您的授权。因医网信对证据的鉴别能力及对纠纷的处理能力有限,不保证处理结果一定符合您的期望。医网信有权决定是否参与争议的处理。
+ (五)经生效法律文书确认用户存在违法或违反本协议行为或者医网信自行判断您涉嫌存在违法或违反本协议行为的,医网信有权公布用户的违法行为并做出相应处理,包括但不限于终止服务、永久禁止使用医网信等。
+
+ 九、隐私条款
+ 请您详见隐私政策。
+
+ 十、知识产权声明
+ 无论是否明示,医网信对网站内所有非公有领域或非他方专有的信息内容享有知识产权(包括但不限于商标权、专利权、著作权),信息内容包括但不限于文字、图片、软件、音频、视频、数据、源代码、设计。非经医网信书面授权同意,任何组织或个人都不得复制、打印和传播属于医网信的信息内容用于其他用途。网站所有的产品、技术及程序均属于医网信知识产权,未经医网信书面授权许可,任何人不得擅自使用(包括但不限于以非法的方式打印、复制、传播、展示、下载等)。否则,医网信将依法追究其法律责任。
+
+ 十一、免责声明
+ (一)您有义务在注册时提供自己的真实、最新、完整的资料,并保证电子邮件地址、联系电话、联系地址、邮政编码等内容的最新、有效性及安全性。您有义务维护并立即更新您的个人资料,确保其真实、最新、有效及完整。若您提供任何错误、虚假、过时或不完整的资料,或者本网站有合理的理由怀疑您提供的为错误、虚假、过时或不完整的资料时,本网站有权暂停或终止您的账号,并拒绝您使用本网站部分或全部服务。在此情况下,本网站不承担任何责任,并且您同意自行承担因此所产生的直接或间接的任何支出或损失。
+ (二)在如下情况,医网信有权中止、终止对您提供部分或全部服务而不承担任何责任:
+ 1.在医网信未向您收费的情况下,医网信可自行全权决定以合理理由 (包括但不限于您已违反本协议的字面意义和精神,或您以不符合本协议的字面意义和精神的方式行事) 终止您的登录密码、用户名 (或其任何部分) 或您对服务的使用。
+ 2.在出现下列任一情况时,医网信可立即发出警告,中止或终止您的账号,删除您的任何现有信息,以及您在网站上展示的任何其他资料:(a)您违反本协议;(b)医网信无法核实或鉴定您向医网信提供的任何资料;(c)医网信相信您的行为可能会使您、医网信其他用户或通过医网信提供服务的第三者服务供应商发生任何法律责任;(d)发现您从事涉及网站的诈骗等违法违规活动,医网信有权中止或终止您的服务。对于(a)、(b)、(c)三项,医网信不限制用户采取相关补救措施。
+ 3.对于本协议规定的采取的中止或终止措施,医网信不对结果承担任何责任。
+ 4.根据本协议的任何规定中止或终止您使用服务之措施,医网信将本着审慎的态度实施,并将履行告知义务,即使通知您不能继续使用医网信的服务,但在您的账号内的所有相关资料,医网信将继续为您保存,且可以查询和为您提供相关文件内容的证明。
+ 5.由于互联网本身所具有的不稳定性,医网信无法保证服务不会中断。系统因有关状况无法正常运作,使用户无法使用任何医网信服务或使用医网信服务受到任何影响时,医网信对用户或第三方不负任何责任,前述状况包括但不限于:
+ (1)医网信系统停机维护期间。
+ (2)电信设备出现故障不能进行数据传输的。
+ (3)由于黑客攻击、网络供应商技术调整或故障、网站升级、银行方面的问题等原因而造成的医网信服务中断或延迟。
+ (4)因台风、地震、海啸、洪水、停电、战争、恐怖袭击等不可抗力之因素,造成医网信系统障碍不能执行业务的。
+ 6.因用户的过错导致的任何损失,该过错包括但不限于:操作不当、遗忘或泄露密码、密码被他人破解、用户使用的计算机系统被第三方侵入、用户委托他人代理签署时他人恶意或不当操作而造成的损失,医网信不承担任何责任。
+
+ 十二、违约责任
+ (一)因您违反本协议或经在此提及而纳入本协议的其他文件,或因您违反了法律、法规、规章、政府规范性文件或侵害了第三方的权利,而使第三方对医网信及其股东、职员、代理人提出索赔要求(包括司法费用和其他专业人士的费用),您必须对医网信及股东 、职员、代理人承担赔偿责任,使其等免遭损失。
+ (二)如因医网信违反有关法律、法规或本协议项下的任何条款而给用户造成损失,医网信同意承担由此造成的损害赔偿责任,基于您遭受的实际损失,以以下两者之中金额较高者为限:(1)人民币5000元;或(2)导致该等实际损失的事件发生前12个月内您为使用对应服务所支付的金额。
+
+ 十三、法律适用与管辖
+ (一)本协议的订立、变更、执行和解释,以及与本协议有关的争议解决,均应适用中华人民共和国法律。如与本协议有关的某一特定事项没有法律规定或规定不明确,则应参照通用的国际商业惯例和行业惯例。
+ (二)如因本协议或医网信服务所引起或与其有关的任何争议应向北京数字医信科技有限公司所在地人民法院起诉。
+
+ 十四、其他
+ (一)本协议自您同意勾选并成功注册为本网站用户之日起生效,除非本网站终止本服务协议或者用户丧失本网站用户资格,否则本服务协议始终有效。本服务协议终止并不免除用户根据本服务协议或其他有关协议、规则所应承担的义务和责任。
+ (二)本服务协议部分条款被认定为无效时,不影响本服务协议其他条款的效力。
+ (三)本协议中的标题仅为方便而设,在解释本协议时应被忽略。
+ (四)医网信对本服务协议享有最终的解释权。
+
+
+ 自动签名许可及服务协议
+ 《医网信自动签名许可及服务协议》 由您与医网信服务提供方 (北京数字医信科技有限公司)共同结缔,本协议具有合同效力,请您务必审慎阅读、充分理解各条款内容。
+ 除非您已经阅读并接受本协议所有条款,否则您无权使用自动签名服务。如果您对本协议或医网信有意见或者建议,可与医网信服务部门联系,我们会给予您必要的帮助,您点击同意或使用医网信服务均视为您已阅读并同意签署本协议。
+ 一、您自愿同意在医网信APP中开通自动签名服务。您同意首次开通自动签名服务后,其在使用自动签名服务期间均遵循本协议之全部内容。
+ 二、您自愿同意在使用自动签名服务期间,允许推送到医网信APP的数据实现 APP 自动签名动作,代替您主动干预的电子签名动作。您承诺前述委托操作视同其本人作出。
+ 三、您自愿同意当推送到医网信APP的数据超过14天未进行处理,允许APP开启自动签名动作代替您主动干预该数据。您承诺前述委托操作视同其本人作出。
+ 四、您承诺,按照医网信要求准确提供并在取得该账户后及时更新正确、最新及完整的身份信息及相关资料。对于您没有按时更新信息所导致的后果,数字医信不承担任何责任。若数字医信有合理理由怀疑您提供的身份信息及相关资料错误、不实、过时或不完整的,数字医信有权暂停或终止向您提供部分或全部自动签名服务,对此数字医信不承担任何责任。若因国家法律法规、部门规章或监管机构的要求,数字医信需要您补充提供任何相关资料但您不能及时配合提供的,数字医信有杈暂停或终止提供部分或全部自动签名服务,对此数字医信不承担任何责任。
+ 五、数字医信在已谨慎地遵循了国家法律、法规有关规定的情况下,由于数字医信的软硬件设备或网络故障等无法预见或不可避免的因素而导致自动签名服务延迟、停顿、中断或无法签发,而有损失产生的,数字医信不承担任何责任。
+ 六、您理解数宇医信已采取了有效措施保护用户资料和服务的安全,但仍然可能存在且不限于下列风险,您同意承担该风险和由此带来的一切可能损失:
+ (1)数据在互联网上的传输途径不完全确定,互联网本身并非完全安全可靠的网络环境;
+ (2)如果因密码泄露、身份信息泄露,他人有可能仿冒用户身份使用自动签名服务;
+ (3)在自动签名服务开通期间,若您未控制好使用者(包括但不限于您未以正确步骤离开业务系统PC端或医网信移动端),自动签名可能在非您自主行为下发生;
+ (4)在网络上传输的数据有可能被某些个人、团体或机构通过某种渠道获得,但他们并不一定能够了解该数据的真实内容。
+ 七、因其它原因导致的自动误签名操作,由过错方承担责任,数字医信将协助您追究相关责任。
+ 八、数字医信在发现异常签名或有合理理由怀疑签名有疑义或有违反法律规定或本协议约定之虞时,有权不经通知先行暂停或终止您名下全部或部分医网信账户的使用,并通过线下方式通知您。对此数字医信不承担任何责任。
+ 九、数宇医信依法保护用户的敏感信息,但以下情况除外:
+ (1)事先取得您的明确授权;
+ (2)根据有关的法律法规要求;
+ (3)按照相关政府主管部门的要求;
+ (4)为维护社会公众的利益;
+ (5)为维护数字医信的合法权益。
+ 十、数字医信有权随时对本协议内容进行单方面修订。数宇医信修订本协议的,并以在数字医信网站(www.51trust.com)公告的方式予以公布,但无需另行单独通知您。
+ 若您在本协议内容公告变更后继续使用本服务的,表示您已充分阅读、理解并接受修订后的协议内容,也将遵循修订后的协议内容使用本服务;若您不同意修订后的协议内容,应停止使用本服务。
+
+
+ 数字证书申请及使用协议
+
+ 本协议由北京数字医信科技有限公司(下称“医网信”)与数字证书用户(下称“用户”或“您”)之间签署,本协议具有合同效力。医网信作为BJCA的授权审核机构,有权与用户签署本协议。BJCA作为权威、可信、公正的第三方电子认证服务机构,根据国家相关法律、法规,为用户提供合法的数字证书申请、审核、签发和管理等电子认证服务。为明确各方权利和义务,医网信和用户就数字证书的申请和使用等事宜达成以下协议,共同遵守执行。
+
+ 第一条 定义
+ 1. 数字证书:电子认证服务机构签发的包含数字证书使用者身份信息和公开密钥的电子文件。
+ 2. CA机构:工业和信息化部批准的电子认证服务机构和国家密码管理局批准的电子政务电子认证服务机构,遵照《中华人民共和国电子签名法》为用户提供数字证书相关的电子认证服务。
+ 3. 医网信:北京数字医信科技有限公司,BJCA的授权审核机构,为用户提供数字证书颁发的申请、审核等服务。
+ 4. 用户:数字证书持有人以及申请使用数字证书的主体。
+
+ 第二条 声明
+ 1. 您应在申请数字证书以前务必仔细阅读本协议及BJCA公布的《电子认证业务规则(CPS)》,若您不同意本协议或CPS的任意内容,或者无法准确理解医网信对条款的解释,请不要点击同意并进行后续操作; 若您点击同意,则表示您对本协议及CPS的全部内容已充分阅读并认可和同意遵守,同时,承诺遵守中国法律、法规、规章及其他政府规范性文件的规定, 如有违反而造成任何法律后果,您将以本人名义独立承担所有相应的法律责任。
+ 2. 您点击同意即表示您同意医网信将您的身份信息(包括姓名、身份证号码、手机号码、人脸信息)传输至实名认证机构与BJCA用于申请数字证书。
+
+ 第三条 申请
+ 1. 用户同意通过医网信向BJCA申请并使用该机构颁发给用户的数字证书,承诺在申请证书时提供真实、完整和准确的信息及证明材料。如因故意或过失未提供真实、完整和准确的信息,导致BJCA签发证书错误,造成相关各方损失的,由用户承担相关责任。
+ 2. 医网信作为BJCA的授权审核机构,负责用户的信息录入、身份审核等工作。用户在申请数字证书时应遵照BJCA的规程办理手续。
+ 3. 医网信应积极响应用户发出的证书申请请求,及时通过BJCA为用户签发证书。如果由于设备或网络故障而导致签发数字证书错误、延迟、中断或者无法签发,医网信和BJCA不承担任何赔偿责任。
+ 4. 用户确认委托医网信托管数字证书,医网信应确保用户可通过安全授权认证控制数字证书调用,用户调用数字证书实施电子签名,则视为接受证书并承担该证书使用的责任和后果。
+ 5. 用户确认委托医网信生成及托管签名密钥对,医网信应确保用户可通过安全授权认证控制私钥,不需要将私钥传送给用户。
+
+
+ 第四条 使用
+ 1. 数字证书用于网络上的用户身份标识、数字签名验证及密钥分配,各应用系统可根据需要对其用途进行定义,但不包括涉及违反国家法律、法规或危害国家安全的用途。
+ 2. 用户应确保其应用系统能为数字证书提供安全的应用环境,若因网络、主机、操作系统或其他软硬件环境等存在安全漏洞,由此导致的安全事故及相关后果,医网信和BJCA不承担责任。
+ 3. 用户应当妥善保管调用数字证书的验证码或密码,不得泄漏或交付他人。如用户保管不善导致数字证书遭盗用、冒用、伪造或者篡改,用户应当自行承担相关责任。
+ 4. 数字证书对应的私钥为用户本身访问和使用,用户对使用数字证书的行为负责。所有使用数字证书在网络上签署电子文件或进行其他活动均视为用户所为,因此而产生的相关后果应当由用户自行承担。
+ 5. 数字证书一律不得转让、转借或转用。因转让、转借或转用而产生的相关后果应当由用户自行承担。
+ 6. 如遇数字证书遗失、被窃,或数字证书私钥泄露,用户应当立即到BJCA申请注销证书。
+
+ 第五条 更新
+ 1. 用户授权医网信在用户申请电子印章或使用电子签名服务时使用其实名认证信息自动更新已到期数字证书,医网信保证用户在使用电子签名服务时数字证书处于有效期内。
+ 2. 因技术需要,如遇证书中的信息发生变更,用户应及时在医网信进行更新及重新进行实名认证,并通过医网信向BJCA申请更新证书。若用户逾期没有更新实名认证信息及证书,因此而产生的相关后果应当由用户自行负责。
+
+ 第六条 吊销
+ 1. 如遇数字证书私钥泄露丢失、证书中的信息发生重大变更、或用户不希望继续使用数字证书的情况,用户应当立即向BJCA申请吊销证书。吊销手续遵循各注册机构的规定。用户应当承担在证书吊销之前所有因使用数字证书而造成的责任。
+ 2. 如果用户主体资格灭失(如企业注销等),法定代表人或授权代表通过医网信向BJCA请求吊销用户证书,用户主体及相关责任人应当承担其数字证书在吊销前所有使用数字证书而造成的相关后果。
+ 3. 对于下列情形之一,BJCA有权主动吊销所签发的证书:
+ (1)用户申请证书时,提供不真实信息;
+ (2)证书对应的私钥泄露或出现其他证书的安全性得不到保证的情况;
+ (3)用户不能履行或违反了相关法律、法规和本协议和CPS所规定的责任和义务;
+ (4)法律、法规规定的其他情形。
+
+ 第七条 其他
+ 1. 医网信与BJCA不对由于意外事件或其他不可抗力事件而导致暂停或终止全部或部分证书服务承担任何责任。
+ 2. 本协议的解释适用中华人民共和国法律。若发生任何纠纷或争议,首先应友好协商解决,协商不成的,争议方均可向北京数字医信科技有限公司所在地人民法院起诉。
+ 3. 用户确认已经认真阅读并完全理解本协议中的各项规定,用户一旦在本页面点击同意即表明接受本协议的约束,本协议即时生效。`;
+ }, []);
+
+ return (
+
+
+ {'医网信用户服务协议'}
+ {agreementContent}
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ scroll: {
+ paddingVertical: 10,
+ },
+ title: {
+ paddingLeft: 10,
+ paddingRight: 10,
+ fontSize: 18,
+ textAlign: 'center',
+ },
+ contentText: {
+ padding: 5,
+ paddingLeft: 10,
+ paddingRight: 10,
+ fontSize: 15,
+ color: '#333333',
+ lineHeight: 25,
+ },
+ bottom: {
+ height: 40,
+ },
+});
diff --git a/src/app/screens/login/agreement/AgreementScreen.tsx b/src/app/screens/login/agreement/AgreementScreen.tsx
new file mode 100644
index 0000000..31a3f79
--- /dev/null
+++ b/src/app/screens/login/agreement/AgreementScreen.tsx
@@ -0,0 +1,216 @@
+import React, { useCallback } from 'react';
+import {
+ BackHandler,
+ Image,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import { useFocusEffect } from '@react-navigation/native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import RNExitApp from 'react-native-exit-app';
+import LinearGradient from 'react-native-linear-gradient';
+import { useCommon } from '@common/contexts/useCommon.ts';
+import { showErrorMessage } from '@common/ToastHelper.ts';
+
+type Props = StackScreenProps;
+
+export default function AgreementScreen(props: Props) {
+ const { navigation } = props;
+
+ useFocusEffect(
+ useCallback(() => {
+ const backHandler = BackHandler.addEventListener(
+ 'hardwareBackPress',
+ () => true,
+ );
+ return () => backHandler.remove();
+ }, []),
+ );
+
+ const {
+ actions: { save },
+ } = useCommon();
+
+ return (
+
+
+
+
+
+
+ 用户协议和个人信息保护政策
+
+
+
+
+ {
+ ' 欢迎您使用医网信!\n 为了给您更好的体验,我们依据相关法律制定了'
+ }
+ {
+ navigation.navigate('AgreementContent');
+ }}
+ >
+ {'《用户服务协议》'}
+
+ {'及'}
+ {
+ navigation.navigate('WebView', {
+ url: 'https://www.51trust.com/privacy.html',
+ title: '用户隐私政策',
+ });
+ }}
+ >
+ {'《隐私政策》'}
+
+ {
+ ',特此向您推送本提示。请您仔细阅读并充分理解相关协议和隐私政策各条款,包括但不限于服务使用须知、用户行为规范,以及为了向您提供服务而收集、使用、存储您个人信息的情况。如果您选择"同意并继续",代表您已同意前述协议,我们将尽力保障您的权益并为您提供更优质的产品和服务。如您不同意,很遗憾我们将无法继续为您提供服务。'
+ }
+
+
+
+
+ {
+ RNExitApp.exitApp();
+ }}
+ >
+
+ {'不同意'}
+
+
+ {
+ save('policyAccepted', true)
+ .then(() => {
+ navigation.pop();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ >
+
+ 同意并继续
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ background: {
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
+ },
+ content: {
+ width: 250,
+ backgroundColor: '#FFFFFF',
+ borderRadius: 5,
+ },
+ header: {
+ height: 60,
+ backgroundColor: 'red',
+ borderTopLeftRadius: 5,
+ borderTopRightRadius: 5,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ headerImage: {
+ position: 'absolute',
+ width: 60,
+ height: 49,
+ right: 40,
+ bottom: 2,
+ },
+ title: {
+ fontSize: 16,
+ color: '#FFFFFF',
+ fontWeight: '700',
+ },
+ scroll: {
+ paddingHorizontal: 15,
+ minHeight: 100,
+ maxHeight: 300,
+ },
+ message: {
+ fontSize: 14,
+ marginVertical: 15,
+ color: '#11102C',
+ },
+ highlightText: {
+ fontSize: 14,
+ lineHeight: 18,
+ color: '#18ABFB',
+ },
+ bottom: {
+ borderBottomLeftRadius: 5,
+ borderBottomRightRadius: 5,
+ padding: 15,
+ alignItems: 'center',
+ },
+ buttonView: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ leftButton: {
+ flexShrink: 1,
+ minWidth: 90,
+ minHeight: 34,
+ paddingHorizontal: 8,
+ paddingVertical: 7,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: '#F6F8FB',
+ borderRadius: 5,
+ marginRight: 10,
+ },
+ leftText: {
+ fontSize: 14,
+ color: '#8685A3',
+ fontWeight: '500',
+ },
+ rightButton: {
+ flexShrink: 1,
+ minWidth: 90,
+ minHeight: 34,
+ paddingHorizontal: 8,
+ paddingVertical: 7,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: '#0E84FA',
+ borderRadius: 5,
+ },
+ rightText: {
+ fontSize: 14,
+ color: '#FFFFFF',
+ fontWeight: '500',
+ },
+ bottomButton: {
+ marginTop: 15,
+ },
+});
diff --git a/src/app/screens/login/api/index.ts b/src/app/screens/login/api/index.ts
new file mode 100644
index 0000000..c3ddb0d
--- /dev/null
+++ b/src/app/screens/login/api/index.ts
@@ -0,0 +1 @@
+export * from './useSMSVerifyCode';
diff --git a/src/app/screens/login/api/loginSchema.ts b/src/app/screens/login/api/loginSchema.ts
new file mode 100644
index 0000000..63d3438
--- /dev/null
+++ b/src/app/screens/login/api/loginSchema.ts
@@ -0,0 +1,53 @@
+import {z} from 'zod';
+
+// 用户类型
+const UserType = {
+ Guanxin: 1, // 冠心用户
+ General: 2, // 一般用户
+ GeneralAndGuanxin: 3, // 同时是冠心和一般用户
+} as const;
+
+// 实名认证状态 0 未实名认证 1 已实名认证 2 实名认证失败 3 证件号不存在 4 证件号已被其他用户使用
+const RealNameStatus = {
+ Unverified: 0, // 尚未认证
+ Success: 1, // 认证成功
+ Failure: 2, // 实名认证失败
+ Inexistent: 3, // 证件号不存在
+ Registered: 4, // 证件号已被其他用户使用
+} as const;
+
+// 登录/注册后信息模型
+const loginSchema = z.object({
+ sessionId: z.string().min(1), // token
+ userId: z.string().min(1), // 用户 id
+ userType: z.nativeEnum(UserType).transform(val => {
+ switch (val) {
+ case UserType.Guanxin:
+ return 'guanxin';
+ case UserType.General:
+ return 'general';
+ case UserType.GeneralAndGuanxin:
+ return 'generalandguanxin';
+ }
+ }), // 用户类型
+ realNameStatus: z.nativeEnum(RealNameStatus).transform(val => {
+ switch (val) {
+ case RealNameStatus.Unverified:
+ return 'unverified';
+ case RealNameStatus.Success:
+ return 'success';
+ case RealNameStatus.Failure:
+ return 'failure';
+ case RealNameStatus.Inexistent:
+ return 'inexistent';
+ case RealNameStatus.Registered:
+ return 'registered';
+ }
+ }), // 实名状态
+ enableCert: z.boolean(), // 是否可以下证
+ gxLeader: z.boolean().optional().default(false), // 冠新leader
+ hasBindFirm: z.boolean().optional().default(false), // 是否绑定厂商
+ hasFaceDetect: z.boolean().optional().default(false), // 是否进行了人脸识别
+});
+
+export {loginSchema};
diff --git a/src/app/screens/login/api/useSMSVerifyCode.ts b/src/app/screens/login/api/useSMSVerifyCode.ts
new file mode 100644
index 0000000..636e7db
--- /dev/null
+++ b/src/app/screens/login/api/useSMSVerifyCode.ts
@@ -0,0 +1,35 @@
+import {z} from 'zod';
+import { MD5_KEY } from '@common/constants';
+import { md5_hex } from '@common/utils/md5';
+import { useApi } from '@common/api/useApi.ts';
+
+// 获取短信验证码
+const useSMSVerifyCode = (
+ phone: string, // 手机号
+ type: 'register' | 'login' | 'reset' | 'change', // 类型:注册/登录/重置密码/未登录更改手机
+ username?: string, // 用户姓名(更改手机号类型传)
+ idCardNumber?: string, // 身份证号(更改手机号类型传)
+) => {
+ const time = new Date().getTime();
+ const typeCode =
+ type === 'login' ? 1 : type === 'reset' ? 2 : type === 'register' ? 3 : 5; // 1:登录;2:重置密码;3:注册 5:未登录更改手机号
+ const signOrigin = `phoneNum=${phone}&time=${time}&type=${typeCode}#${MD5_KEY}`;
+ const sign = md5_hex(signOrigin);
+
+ return useApi(
+ '/am/v3/userCenter/account/getSMSVerifyCode',
+ 'POSTJSON',
+ {
+ phoneNum: phone,
+ userName: username,
+ userIdCardNum: idCardNumber,
+ type: typeCode,
+ time: time,
+ sign: sign,
+ },
+ z.unknown(),
+ {},
+ );
+};
+
+export {useSMSVerifyCode};
diff --git a/src/app/screens/login/changePhone/ChangePhoneScreen.tsx b/src/app/screens/login/changePhone/ChangePhoneScreen.tsx
new file mode 100644
index 0000000..ec256cf
--- /dev/null
+++ b/src/app/screens/login/changePhone/ChangePhoneScreen.tsx
@@ -0,0 +1,245 @@
+import React, { useLayoutEffect, useState } from 'react';
+import {
+ Keyboard,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import LoginTextInputItem from '../components/LoginTextInputItem';
+import SubmitButton from '@common/components/SubmitButton';
+import {
+ COUNTDOWN_CHANGE_PHONE_KEY,
+ ID_CARD_PATTERN,
+ PHONE_PATTERN,
+ THEME_COLOR,
+} from '@common/constants';
+import { useCountdown } from '@szyx-mobile/hooks';
+import Spinner from '@common/components/Spinner';
+import { useSMSVerifyCode } from '../api';
+import { useChangePhone } from './api';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+
+const BUTTON_ACTIVE_COLOR = THEME_COLOR;
+const BUTTON_UNACTIVE_COLOR = '#9B9B9B';
+const SMS_CODE_LENGTH = 6; // 验证码长度
+
+type Props = StackScreenProps;
+
+export default function ChangePhoneScreen(props: Props) {
+ const { navigation } = props;
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '更换手机号',
+ });
+ }, [navigation]);
+
+ const { count, start } = useCountdown(59, COUNTDOWN_CHANGE_PHONE_KEY);
+
+ // 【状态】
+
+ const [username, setUsername] = useState(''); // 用户姓名
+ const [idCardNumber, setIdCardNumber] = useState(''); // 身份证号
+ const [phone, setPhone] = useState(''); // 手机号
+ const [smsCode, setSmsCode] = useState(''); // 验证码
+
+ // 【接口】
+
+ const { loading: verifyCodeLoading, fetchAsync: verifyCodeFetch } =
+ useSMSVerifyCode(phone, 'change', username, idCardNumber); // 获取验证码
+
+ const { loading: changePhoneLoading, fetchAsync: changePhoneFetch } =
+ useChangePhone(username, idCardNumber, phone, smsCode); // 更改手机号
+
+ return (
+ <>
+
+
+ 温馨提示:更换成功后,您只能用新手机号登录APP
+
+
+
+
+
+ {
+ setUsername(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setIdCardNumber(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setPhone(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setSmsCode(value);
+ }}
+ clearButton={true}
+ rightButton={{
+ title:
+ count === null
+ ? '获取验证码'
+ : count === 0
+ ? '重新获取'
+ : `重新获取(${count})`,
+ color:
+ count === null || count === 0
+ ? BUTTON_ACTIVE_COLOR
+ : BUTTON_UNACTIVE_COLOR,
+ onPress: () => {
+ if (count !== null && count > 0) {
+ return;
+ }
+ if (username.length === 0) {
+ showErrorMessage('请输入您的姓名');
+ return;
+ }
+
+ if (idCardNumber.length === 0) {
+ showErrorMessage('请输入您的身份证号');
+ return;
+ }
+
+ if (ID_CARD_PATTERN.test(idCardNumber) === false) {
+ showErrorMessage('身份证号格式不正确');
+ return;
+ }
+
+ if (phone.length === 0) {
+ showErrorMessage('请输入更换的手机号码');
+ return;
+ }
+
+ if (PHONE_PATTERN.test(phone) === false) {
+ showErrorMessage('手机号格式不正确');
+ return;
+ }
+ verifyCodeFetch()
+ .then(() => {
+ showMessage('验证码已发送');
+ start();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ },
+ }}
+ />
+
+ {
+ if (username.length === 0) {
+ showErrorMessage('请输入您的姓名');
+ return;
+ }
+
+ if (idCardNumber.length === 0) {
+ showErrorMessage('请输入您的身份证号');
+ return;
+ }
+
+ if (ID_CARD_PATTERN.test(idCardNumber) === false) {
+ showErrorMessage('身份证号格式不正确');
+ return;
+ }
+
+ if (phone.length === 0) {
+ showErrorMessage('请输入更换的手机号码');
+ return;
+ }
+
+ if (PHONE_PATTERN.test(phone) === false) {
+ showErrorMessage('手机号格式不正确');
+ return;
+ }
+
+ if (smsCode.length === 0) {
+ showErrorMessage('请输入验证码');
+ return;
+ }
+
+ if (smsCode.length !== SMS_CODE_LENGTH) {
+ showErrorMessage('验证码格式不正确');
+ return;
+ }
+
+ Keyboard.dismiss();
+ changePhoneFetch()
+ .then(() => {
+ showMessage('更换成功');
+ navigation.pop();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ />
+
+
+ {(verifyCodeLoading || changePhoneLoading) && }
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ header: {
+ backgroundColor: '#FFF5E5',
+ padding: 12,
+ },
+ headerText: {
+ color: '#FF9A08',
+ },
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ middle: {
+ marginTop: 20,
+ },
+ submit: {
+ marginHorizontal: 25,
+ marginBottom: 15,
+ marginTop: 20,
+ },
+});
diff --git a/src/app/screens/login/changePhone/api/index.ts b/src/app/screens/login/changePhone/api/index.ts
new file mode 100644
index 0000000..23bc860
--- /dev/null
+++ b/src/app/screens/login/changePhone/api/index.ts
@@ -0,0 +1 @@
+export * from './useChangePhone';
diff --git a/src/app/screens/login/changePhone/api/useChangePhone.ts b/src/app/screens/login/changePhone/api/useChangePhone.ts
new file mode 100644
index 0000000..64fd839
--- /dev/null
+++ b/src/app/screens/login/changePhone/api/useChangePhone.ts
@@ -0,0 +1,25 @@
+import {useApi} from '@common/api/useApi';
+import {z} from 'zod';
+
+// 修改手机号
+const useChangePhone = (
+ username: string,
+ idCardNumber: string,
+ phone: string,
+ smsCode: string,
+) => {
+ return useApi(
+ '/am/v3/userCenter/account/changeAccountByUnlogin',
+ 'POSTJSON',
+ {
+ userName: username,
+ userIdCardNum: idCardNumber,
+ phoneNum: phone,
+ verifyCode: smsCode,
+ },
+ z.unknown(),
+ {},
+ );
+};
+
+export {useChangePhone};
diff --git a/src/app/screens/login/components/AgreementChecking.tsx b/src/app/screens/login/components/AgreementChecking.tsx
new file mode 100644
index 0000000..1d9f472
--- /dev/null
+++ b/src/app/screens/login/components/AgreementChecking.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import {
+ Image,
+ Keyboard,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import {NavigationProp, useNavigation} from '@react-navigation/native';
+import {LoginParamList} from '@app/routes/login/LoginParamList.ts';
+
+type Props = {
+ checked: boolean; // 当前勾选状态
+ onPress?: () => void; // 点击勾选/取消勾选按钮
+};
+
+/**
+ * @description: 登录或注册界面中的隐私声明或注册协议勾选区域
+ */
+export default function AgreementChecking(props: Props) {
+ const navigation = useNavigation>();
+ return (
+
+ {
+ props.onPress && props.onPress();
+ }}>
+
+
+
+ {'我已阅读并同意 '}
+ {
+ Keyboard.dismiss();
+ navigation.navigate('WebView', {
+ url: 'https://www.51trust.com/privacy.html',
+ title: '用户隐私政策',
+ });
+ }}>
+ 隐私声明
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ paddingRight: 25,
+ paddingLeft: 15,
+ },
+ button: {
+ paddingLeft: 10,
+ paddingRight: 8,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ buttonImage: {
+ width: 14,
+ height: 14,
+ },
+ text: {
+ marginVertical: 10,
+ flex: 1,
+ color: '#11102C',
+ fontSize: 14,
+ lineHeight: 18,
+ },
+ buttonText: {
+ color: '#0E84FA',
+ textDecorationLine: 'underline',
+ },
+});
diff --git a/src/app/screens/login/components/LoginTextInputItem.tsx b/src/app/screens/login/components/LoginTextInputItem.tsx
new file mode 100644
index 0000000..01415a6
--- /dev/null
+++ b/src/app/screens/login/components/LoginTextInputItem.tsx
@@ -0,0 +1,173 @@
+import React, {useMemo, useRef, useState} from 'react';
+import {
+ Image,
+ ImageSourcePropType,
+ KeyboardTypeOptions,
+ StyleSheet,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+
+type Props = {
+ required?: boolean; // 是否必填
+ icon?: ImageSourcePropType; // 图标
+ placeholder?: string; // 占位文字
+ keyboardType?: KeyboardTypeOptions; // 输入框键盘类型
+ secureTextEntry?: boolean; // 是否是密码
+ maxLength?: number; // 输入框最大长度
+ value?: string; // 输入框值
+ onChangeText?: (text: string) => void; // 输入框文字改变回调
+ autoFocus?: boolean; // 是否自动弹出
+ clearButton?: boolean; // 输入框右侧是否展示清除按钮
+ rightButton?: {
+ title: string; // 按钮文字
+ color: string; // 按钮文字颜色
+ onPress?: () => void; // 点击回调
+ }; // 输入框右侧是否需要按钮
+};
+
+/**
+ * @description: 登录、注册等界面中的输入框条目
+ */
+export default function LoginTextInputItem(props: Props) {
+ const textInputRef = useRef(null);
+ const [focus, setFocus] = useState(false);
+
+ const showCleanButton = useMemo(() => {
+ return props.value && props.value.length > 0 && focus && props.clearButton;
+ }, [focus, props.clearButton, props.value]);
+
+ return (
+
+ {props.required === true && (
+
+ *
+
+ )}
+
+ {props.icon && }
+ {
+ props.onChangeText && props.onChangeText(value);
+ }}
+ autoFocus={props.autoFocus}
+ onFocus={() => {
+ setFocus(true);
+ }}
+ onBlur={() => {
+ setFocus(false);
+ }}
+ />
+ {showCleanButton && (
+ {
+ textInputRef.current?.clear();
+ props.onChangeText && props.onChangeText('');
+ }}>
+
+
+ )}
+ {props.rightButton && (
+
+
+ {
+ props.rightButton?.onPress && props.rightButton?.onPress();
+ }}>
+
+ {props.rightButton.title}
+
+
+
+ )}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ required: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ width: 25,
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingLeft: 12,
+ },
+ requiredText: {
+ fontSize: 14,
+ color: '#EB4900',
+ },
+ item: {
+ backgroundColor: '#F6F8FB',
+ height: 45,
+ borderRadius: 22.5,
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginVertical: 10,
+ marginHorizontal: 25,
+ paddingHorizontal: 15,
+ },
+ icon: {
+ width: 18,
+ height: 18,
+ },
+ textInput: {
+ flex: 1,
+ marginLeft: 8,
+ fontSize: 14,
+ color: '#17171A',
+ fontWeight: '500',
+ },
+ clearButton: {
+ height: '100%',
+ right: -15,
+ paddingVertical: 4,
+ paddingHorizontal: 15,
+ justifyContent: 'center',
+ },
+ clearButtonImage: {
+ width: 12,
+ height: 12,
+ },
+ rightView: {
+ marginLeft: 8,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ separator: {
+ backgroundColor: '#D8D8D8',
+ height: 14,
+ width: 1,
+ },
+ button: {
+ height: 32,
+ justifyContent: 'center',
+ marginLeft: 15,
+ },
+ buttonText: {
+ fontSize: 14,
+ },
+});
diff --git a/src/app/screens/login/environment/EnvironmentScreen.tsx b/src/app/screens/login/environment/EnvironmentScreen.tsx
new file mode 100644
index 0000000..31ab1f7
--- /dev/null
+++ b/src/app/screens/login/environment/EnvironmentScreen.tsx
@@ -0,0 +1,138 @@
+import React, { useLayoutEffect, useState } from 'react';
+import {
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+} from 'react-native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import { useCommon } from '@common/contexts/useCommon';
+import { useEnv } from '@common/env/useEnv';
+import ENV from '@common/env/env';
+import { THEME_COLOR } from '@common/constants';
+import { useQuickClicks } from '@szyx-mobile/hooks';
+import Alert from '@common/components/Alert.tsx';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+
+const ITEM_COLOR_SELECTED = THEME_COLOR;
+const ITEM_COLOR_UNSELECTED = '#FFFFFF';
+const TEXT_COLOR_SELECTED = '#FFFFFF';
+const TEXT_COLOR_UNSELECTED = '#11102C';
+
+const DEFAULT_DISPLAY_NUMBER = 2; // 默认展示的环境的数量
+
+type Props = StackScreenProps;
+
+export default function EnvironmentScreen(props: Props) {
+ const {navigation} = props;
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '环境切换',
+ });
+ }, [navigation]);
+
+ const {
+ actions: {save},
+ } = useCommon();
+
+ const {env} = useEnv();
+
+ const [expanded, setExpanded] = useState(
+ ENV.findIndex(item => item.env === env) >= DEFAULT_DISPLAY_NUMBER,
+ ); // 是否展示所有环境(如果当前环境不在默认展示的环境中,则直接展示所有环境)
+ const {click} = useQuickClicks(5, () => {
+ setExpanded(true);
+ });
+
+ return (
+
+
+ {
+ click();
+ }}>
+ 警告!此页面为环境切换页面,操作将会变更您的应用环境。若您无意进入此页面,请及时关闭,切勿因为误操作导致无法正常使用应用!
+
+ {(expanded ? ENV : ENV.slice(0, DEFAULT_DISPLAY_NUMBER)).map(
+ (envItem, index) => {
+ return (
+ {
+ Alert.show(
+ `确定要切换到${envItem.title}吗?`,
+ undefined,
+ {
+ action: () => {
+ save('env', envItem.env)
+ .then(() => {
+ showMessage(`已切换到${envItem.title}`);
+ navigation.pop();
+ })
+ .catch(e => {
+ showErrorMessage('切换环境失败');
+ console.error(`环境切换失败:${e.message}`);
+ });
+ },
+ },
+ {},
+ );
+ }}>
+
+ {envItem.title}
+
+ {/* 其余展示内容 */}
+
+ );
+ },
+ )}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ scroll: {
+ paddingVertical: 8,
+ },
+ headerText: {
+ color: '#DD2222',
+ marginVertical: 8,
+ marginHorizontal: 16,
+ },
+ item: {
+ backgroundColor: '#FFFFFF',
+ marginVertical: 8,
+ marginHorizontal: 16,
+ padding: 8,
+ borderRadius: 4,
+ },
+ itemText: {
+ marginVertical: 4,
+ },
+});
diff --git a/src/app/screens/login/forgotPassword/ForgotPasswordScreen.tsx b/src/app/screens/login/forgotPassword/ForgotPasswordScreen.tsx
new file mode 100644
index 0000000..89fb002
--- /dev/null
+++ b/src/app/screens/login/forgotPassword/ForgotPasswordScreen.tsx
@@ -0,0 +1,238 @@
+import React, { useEffect, useLayoutEffect, useState } from 'react';
+import {
+ Keyboard,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { useHeaderHeight } from '@react-navigation/elements';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import SubmitButton from '@common/components/SubmitButton';
+import Spinner from '@common/components/Spinner';
+import { PASSWORD_PATTERN, THEME_COLOR } from '@common/constants';
+import LoginTextInputItem from '../components/LoginTextInputItem';
+import { useResetPassword } from './api';
+import LinearGradient from 'react-native-linear-gradient';
+import { useSMSVerifyCode } from '../api';
+import { useCountdown } from '@szyx-mobile/hooks';
+import Toast from 'react-native-toast-message';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+
+const BUTTON_ACTIVE_COLOR = THEME_COLOR;
+const BUTTON_UNACTIVE_COLOR = '#9B9B9B';
+const SMS_CODE_LENGTH = 6; // 验证码长度
+
+type Props = StackScreenProps;
+
+export default function ForgotPasswordScreen(props: Props) {
+ const {navigation, route} = props;
+ const headerHeight = useHeaderHeight();
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '',
+ headerTransparent: true,
+ });
+ }, [navigation]);
+
+ const {count, start} = useCountdown(59);
+
+ // 【状态】
+
+ const [smsCode, setSmsCode] = useState(''); // 验证码
+ const [password, setPassword] = useState(''); // 密码
+ const [passwordAgain, setPasswordAgain] = useState(''); // 再次输入密码
+
+ // 【接口】
+
+ const {loading: verifyCodeLoading, fetchAsync: verifyCodeFetch} =
+ useSMSVerifyCode(route.params.phoneNumber, 'reset'); // 获取验证码
+ const {loading: resetPasswordLoading, fetchAsync: resetPasswordFetch} =
+ useResetPassword(route.params.phoneNumber, smsCode, password);
+
+ // 首次进入的时候一定是已经获取成功验证码了,直接开始倒计时
+ useEffect(() => {
+ start();
+ }, [start]);
+
+ return (
+
+
+
+
+ 密码重置
+ {`验证码已发送至 ${route.params.phoneNumber.replace(
+ /(\d{3})\d{4}(\d{4})/,
+ '$1****$2',
+ )}`}
+
+
+
+ {
+ setSmsCode(value);
+ }}
+ clearButton={true}
+ rightButton={{
+ title:
+ count === null
+ ? '获取验证码'
+ : count === 0
+ ? '重新获取'
+ : `重新获取(${count})`,
+ color:
+ count === null || count === 0
+ ? BUTTON_ACTIVE_COLOR
+ : BUTTON_UNACTIVE_COLOR,
+ onPress: () => {
+ if (count !== null && count > 0) {
+ return;
+ }
+ verifyCodeFetch()
+ .then(() => {
+ showMessage('验证码已发送');
+ start();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ },
+ }}
+ />
+ {
+ setPassword(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setPasswordAgain(value);
+ }}
+ clearButton={true}
+ />
+
+
+ 密码长度8-12位,支持英文字母、数字与字符,密码必须包含英文加数字
+
+ {
+ if (PASSWORD_PATTERN.test(password) === false) {
+ showErrorMessage(
+ '密码长度8-12位,支持英文字母、数字与字符,密码必须包含英文加数字',
+ );
+ return;
+ }
+
+ if (passwordAgain.length <= 0) {
+ showErrorMessage('请再次确认登录密码');
+ return;
+ }
+
+ if (passwordAgain !== password) {
+ showErrorMessage('两次输入的密码不一致');
+ return;
+ }
+
+ Keyboard.dismiss();
+
+ resetPasswordFetch()
+ .then(() => {
+ showErrorMessage('密码重置成功,请重新登录');
+ navigation.pop();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ />
+
+
+ {(verifyCodeLoading || resetPasswordLoading) && }
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ background: {
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ right: 0,
+ height: 168,
+ },
+ scroll: {
+ flex: 1,
+ },
+ header: {
+ paddingHorizontal: 25,
+ },
+ title: {
+ fontSize: 24,
+ color: '#17171A',
+ fontWeight: '600',
+ },
+ headerText: {
+ fontSize: 14,
+ color: '#17171A',
+ fontWeight: '500',
+ marginVertical: 14,
+ },
+ middleText: {
+ color: '#ED9900',
+ fontSize: 12,
+ marginHorizontal: 25,
+ marginVertical: 8,
+ },
+ submit: {
+ marginHorizontal: 25,
+ marginBottom: 20,
+ marginTop: 20,
+ },
+ bottom: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 64,
+ },
+ bottomText: {
+ fontSize: 12,
+ },
+});
diff --git a/src/app/screens/login/forgotPassword/api/index.ts b/src/app/screens/login/forgotPassword/api/index.ts
new file mode 100644
index 0000000..1b34d24
--- /dev/null
+++ b/src/app/screens/login/forgotPassword/api/index.ts
@@ -0,0 +1 @@
+export * from './useResetPassword';
diff --git a/src/app/screens/login/forgotPassword/api/useResetPassword.ts b/src/app/screens/login/forgotPassword/api/useResetPassword.ts
new file mode 100644
index 0000000..31df813
--- /dev/null
+++ b/src/app/screens/login/forgotPassword/api/useResetPassword.ts
@@ -0,0 +1,24 @@
+import {useApi} from '@common/api/useApi';
+import {z} from 'zod';
+
+// 重置密码
+const useResetPassword = (
+ phone: string, // 手机号
+ verifyCode: string, // 验证码
+ password: string, // 密码
+) => {
+ return useApi(
+ '/am/v3/userCenter/account/resetPwd',
+ 'POSTJSON',
+ {
+ phoneNum: phone,
+ newPwd: password,
+ confirmPwd: password,
+ verifyCode: verifyCode,
+ },
+ z.unknown(),
+ {},
+ );
+};
+
+export {useResetPassword};
diff --git a/src/app/screens/login/login/LoginScreen.tsx b/src/app/screens/login/login/LoginScreen.tsx
new file mode 100644
index 0000000..94199ab
--- /dev/null
+++ b/src/app/screens/login/login/LoginScreen.tsx
@@ -0,0 +1,418 @@
+import React, {
+ useCallback,
+ useEffect,
+ useLayoutEffect,
+ useMemo,
+ useState,
+} from 'react';
+import {
+ Image,
+ Keyboard,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { useHeaderHeight } from '@react-navigation/elements';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import SubmitButton from '@common/components/SubmitButton';
+import { useQuickClicks } from '@szyx-mobile/hooks';
+import LoginTextInputItem from '../components/LoginTextInputItem';
+import AgreementChecking from '../components/AgreementChecking';
+import { useSMSVerifyCode } from '../api';
+import Spinner from '@common/components/Spinner';
+import { PHONE_PATTERN } from '@common/constants';
+import { useLoginWithPassword } from './api';
+import { useCommon } from '@common/contexts/useCommon';
+import LinearGradient from 'react-native-linear-gradient';
+import { ValidationError } from '@szyx-mobile/use-axios';
+import { RequestError } from '@szyx-mobile/use-request';
+import { useLogin } from '@app/hooks/useLogin';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+import Alert from '@common/components/Alert.tsx';
+
+const USER_NOT_FOUND = '017x003';
+const USER_NOT_REGISTER = '017x036';
+
+type Props = StackScreenProps;
+
+export default function LoginScreen(props: Props) {
+ const {navigation} = props;
+ const headerHeight = useHeaderHeight();
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '',
+ headerTransparent: true,
+ });
+ }, [navigation]);
+
+ const {click} = useQuickClicks(10, () => {
+ navigation.navigate('Environment');
+ });
+
+ const login = useLogin();
+
+ const {
+ state: {info},
+ } = useCommon();
+
+ // 如果用户协议没有同意,则展示用户协议同意页
+ useEffect(() => {
+ if (info.policyAccepted !== true) {
+ navigation.navigate('Agreement');
+ }
+ }, [info.policyAccepted, navigation]);
+
+ // 【工具函数】
+
+ // 将手机号格式化成中间有空格的样式
+ const formatPhoneNumber = (text: string) => {
+ // 移除所有非数字字符
+ const cleaned = ('' + text).replace(/\D/g, '');
+
+ // 分段格式化
+ let formatted = '';
+ if (cleaned.length > 0) {
+ formatted = cleaned.slice(0, 3);
+ }
+ if (cleaned.length >= 4) {
+ formatted += ' ' + cleaned.slice(3, 7);
+ }
+ if (cleaned.length >= 8) {
+ formatted += ' ' + cleaned.slice(7, 11);
+ }
+
+ return formatted;
+ };
+
+ const handleChange = (text: string) => {
+ const formatted = formatPhoneNumber(text);
+ setPhone(formatted);
+ };
+
+ // 【状态】
+
+ const [loginType, setLoginType] = useState<'sms' | 'password'>('sms'); // 登录类型
+ const [phone, setPhone] = useState(
+ info.lastLoginPhoneNumber
+ ? formatPhoneNumber(info.lastLoginPhoneNumber)
+ : '',
+ ); // 手机号(用作展示,包含空格)
+ const [password, setPassword] = useState(''); // 密码
+ const [agreementChecked, setAgreementChecked] = useState(false); // 是否同意隐私声明
+
+ const phoneNumber = useMemo(() => {
+ return phone.replace(/\s+/g, '');
+ }, [phone]); // 去掉空格后的手机号
+
+ // 【接口】
+ const {loading: verifyCodeLoading, fetchAsync: verifyCodeFetch} =
+ useSMSVerifyCode(phoneNumber, loginType === 'sms' ? 'login' : 'reset'); // 注意:这里的获取验证码的类型根据登录类型确定。如果是验证码登录,则这里是登录类型;如果是密码登录,则只可能是点击忘记密码获取的验证码,因此是重置密码类型
+ const {loading: loginLoading, fetchAsync: loginFetch} = useLoginWithPassword(
+ phoneNumber,
+ password,
+ ); // 账号密码登录
+
+ // 处理需要注册的登录错误 - 用不不存在
+ const handleLoginErrorNotFound = useCallback(() => {
+ Alert.show(
+ '温馨提示',
+
+ 用户不存在,是否立即注册?
+
+ 电子签名用户请联系管理员注册,请勿自行注册
+
+ ,
+ {
+ title: '立即注册',
+ action: () => {
+ navigation.navigate('Register');
+ },
+ },
+ {
+ title: '重新输入',
+ },
+ );
+ return;
+ }, [navigation]);
+
+ // 处理需要注册的登录错误 - 用户手机号非注册手机号为关联手机号
+ const handleLoginErrorNotRegister = useCallback(
+ (message?: string) => {
+ Alert.show(
+ '温馨提示',
+
+ {message ?? ''}
+ ,
+ {
+ title: '更换手机号',
+ action: () => {
+ navigation.navigate('ChangePhone');
+ },
+ },
+ {
+ title: '我知道了',
+ },
+ );
+ return;
+ },
+ [navigation],
+ );
+
+ return (
+
+
+
+ {
+ click();
+ }}>
+
+ 欢迎使用医网信
+
+
+
+ {
+ handleChange(value);
+ }}
+ clearButton={true}
+ />
+ {loginType === 'password' && (
+ {
+ setPassword(value);
+ }}
+ clearButton={true}
+ rightButton={{
+ title: '忘记密码',
+ color: '#302F57',
+ onPress: () => {
+ if (PHONE_PATTERN.test(phoneNumber) === false) {
+ showErrorMessage('请输入正确的手机号码');
+ return;
+ }
+ Keyboard.dismiss();
+ verifyCodeFetch()
+ .then(() => {
+ navigation.navigate('ForgotPassword', {
+ phoneNumber: phoneNumber,
+ });
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ },
+ }}
+ />
+ )}
+
+ {
+ setAgreementChecked(checked => !checked);
+ }}
+ />
+ {
+ if (PHONE_PATTERN.test(phoneNumber) === false) {
+ showErrorMessage('请输入正确的手机号码');
+ return;
+ }
+
+ if (loginType === 'password') {
+ // 前端登录时候不校验密码规则,只在注册和重置密码的时候校验
+ Keyboard.dismiss();
+ // 登录逻辑
+ loginFetch()
+ .then(r => {
+ showMessage('登录成功');
+ login(r.sessionId, {
+ userId: r.userId,
+ userType: r.userType,
+ moduleList: [],
+ isFirstLogin: true,
+ currentUserType:
+ r.userType === 'general' ? 'general' : 'guanxin',
+ realNameStatus: r.realNameStatus,
+ enableCert: r.enableCert,
+ phone: phoneNumber,
+ gxLeader: r.gxLeader,
+ hasBindFirm: r.hasBindFirm,
+ hasFaceDetect: r.hasFaceDetect,
+ nickname: undefined,
+ picUrl: undefined,
+ faceDetectFirms: undefined,
+ userIdCardNum: undefined,
+ });
+ })
+ .catch(e => {
+ if (
+ e instanceof RequestError &&
+ e.type === 'ValidationError' &&
+ e.error instanceof ValidationError
+ ) {
+ if (e.error.response.data?.status === USER_NOT_FOUND) {
+ handleLoginErrorNotFound();
+ return;
+ }
+ if (e.error.response.data?.status === USER_NOT_REGISTER) {
+ handleLoginErrorNotRegister(
+ e.error.response.data?.message,
+ );
+ return;
+ }
+ }
+ showErrorMessage(e.message);
+ });
+ } else {
+ Keyboard.dismiss();
+ verifyCodeFetch()
+ .then(() => {
+ navigation.navigate('LoginWithSMS', {
+ phoneNumber: phoneNumber,
+ });
+ })
+ .catch(e => {
+ if (
+ e instanceof RequestError &&
+ e.type === 'ValidationError' &&
+ e.error instanceof ValidationError
+ ) {
+ if (e.error.response.data?.status === '017x003') {
+ handleLoginErrorNotFound();
+ return;
+ }
+ if (e.error.response.data?.status === USER_NOT_REGISTER) {
+ handleLoginErrorNotRegister(
+ e.error.response.data?.message,
+ );
+ return;
+ }
+ }
+ showErrorMessage(e.message);
+ });
+ }
+ }}
+ />
+ {/* 切换登录和快速注册 */}
+
+ {
+ Keyboard.dismiss();
+ setLoginType(type => {
+ if (type === 'sms') {
+ return 'password';
+ }
+ return 'sms';
+ });
+ setPassword('');
+ }}>
+
+ {loginType === 'sms' ? '密码登录' : '验证码登录'}
+
+
+ {' | '}
+ {
+ Keyboard.dismiss();
+ navigation.navigate('Register');
+ }}>
+ 快速注册
+
+
+
+
+ {(verifyCodeLoading || loginLoading) && }
+
+ );
+}
+
+const styles = StyleSheet.create({
+ alertText: {
+ textAlign: 'center',
+ color: '#333333',
+ fontSize: 14,
+ },
+ alertSubText: {
+ textAlign: 'center',
+ color: '#E51C23',
+ fontSize: 10,
+ marginTop: 4,
+ },
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ background: {
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ right: 0,
+ height: 168,
+ },
+ scroll: {
+ flex: 1,
+ },
+ header: {
+ paddingHorizontal: 25,
+ marginBottom: 35,
+ alignItems: 'center',
+ },
+ logo: {
+ width: 75,
+ height: 75,
+ marginBottom: 10,
+ },
+ title: {
+ fontSize: 24,
+ color: '#17171A',
+ fontWeight: '600',
+ },
+ loginButton: {
+ marginHorizontal: 25,
+ marginBottom: 20,
+ marginTop: 20,
+ },
+ bottom: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 64,
+ },
+ bottomText: {
+ color: '#666666',
+ fontSize: 14,
+ },
+});
diff --git a/src/app/screens/login/login/api/index.ts b/src/app/screens/login/login/api/index.ts
new file mode 100644
index 0000000..50f4567
--- /dev/null
+++ b/src/app/screens/login/login/api/index.ts
@@ -0,0 +1 @@
+export * from './useLoginWithPassword';
diff --git a/src/app/screens/login/login/api/useLoginWithPassword.ts b/src/app/screens/login/login/api/useLoginWithPassword.ts
new file mode 100644
index 0000000..17a1da0
--- /dev/null
+++ b/src/app/screens/login/login/api/useLoginWithPassword.ts
@@ -0,0 +1,22 @@
+import {Platform} from 'react-native';
+import {useApi} from '@common/api/useApi';
+import {loginSchema} from '../../api/loginSchema';
+
+// 使用账号密码登录
+// {"changeDevice":false,"enableCert":false,"gxLeader":false,"hasBindFirm":false,"hasFaceDetect":false,"hasPwd":true,"realNameStatus":0,"sessionId":"71979180-62c1-425e-a041-b3158944217c","userId":"99b00468e337456cb0005ad10d6d88da","userType":2}
+
+const useLoginWithPassword = (phone: string, password: string) => {
+ return useApi(
+ '/am/v3/userCenter/account/pwdlogin',
+ 'POSTJSON',
+ {
+ phoneNum: phone,
+ pwd: password,
+ deviceType: Platform.select({android: '0', ios: '1'}),
+ },
+ loginSchema,
+ {},
+ );
+};
+
+export {useLoginWithPassword};
diff --git a/src/app/screens/login/loginWithSMS/LoginWithSMSScreen.tsx b/src/app/screens/login/loginWithSMS/LoginWithSMSScreen.tsx
new file mode 100644
index 0000000..6b36078
--- /dev/null
+++ b/src/app/screens/login/loginWithSMS/LoginWithSMSScreen.tsx
@@ -0,0 +1,217 @@
+import React, { useEffect, useLayoutEffect, useState } from 'react';
+import {
+ Keyboard,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ TouchableOpacity,
+ View,
+} from 'react-native';
+import { StackScreenProps } from '@react-navigation/stack';
+import { useHeaderHeight } from '@react-navigation/elements';
+import { useCountdown } from '@szyx-mobile/hooks';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import SubmitButton from '@common/components/SubmitButton';
+import LoginTextInputItem from '../components/LoginTextInputItem';
+import { useLoginWithVerifyCode } from './api';
+import Spinner from '@common/components/Spinner';
+import { useLogin } from '@app/hooks/useLogin';
+import { useSMSVerifyCode } from '../api';
+import LinearGradient from 'react-native-linear-gradient';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+
+const BUTTON_ACTIVE_COLOR = '#1D91FF';
+const BUTTON_UNACTIVE_COLOR = '#9B9B9B';
+const SMS_CODE_LENGTH = 6; // 验证码长度
+
+type Props = StackScreenProps;
+
+export default function LoginWithSMSScreen(props: Props) {
+ const { navigation, route } = props;
+ const headerHeight = useHeaderHeight();
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '',
+ headerTransparent: true,
+ });
+ }, [navigation]);
+
+ const login = useLogin();
+
+ const { count, start } = useCountdown(59);
+
+ // 【状态】
+
+ const [smsCode, setSmsCode] = useState(''); // 验证码
+
+ // 【接口】
+
+ const { loading: verifyCodeLoading, fetchAsync: verifyCodeFetch } =
+ useSMSVerifyCode(route.params.phoneNumber, 'login'); // 获取验证码
+ const { loading: loginLoading, fetchAsync: loginFetch } =
+ useLoginWithVerifyCode(route.params.phoneNumber, smsCode);
+
+ // 首次进入的时候一定是已经获取成功验证码了,直接开始倒计时
+ useEffect(() => {
+ start();
+ }, [start]);
+
+ return (
+
+
+
+
+ 短信验证
+
+ {/* 手机号需要隐藏中间四位 */}
+ {count === null
+ ? '验证码正在发送'
+ : `验证码已发送至 ${route.params.phoneNumber.replace(
+ /(\d{3})\d{4}(\d{4})/,
+ '$1****$2',
+ )}`}
+
+
+
+
+ {
+ setSmsCode(value);
+ }}
+ clearButton={true}
+ />
+
+ {
+ Keyboard.dismiss();
+
+ loginFetch()
+ .then(r => {
+ showMessage('登录成功');
+ login(r.sessionId, {
+ userId: r.userId,
+ userType: r.userType,
+ moduleList: [],
+ isFirstLogin: true,
+ currentUserType:
+ r.userType === 'general' ? 'general' : 'guanxin',
+ realNameStatus: r.realNameStatus,
+ enableCert: r.enableCert,
+ phone: route.params.phoneNumber,
+ gxLeader: r.gxLeader,
+ hasBindFirm: r.hasBindFirm,
+ hasFaceDetect: r.hasFaceDetect,
+ nickname: undefined,
+ picUrl: undefined,
+ faceDetectFirms: undefined,
+ userIdCardNum: undefined,
+ });
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ />
+ {/* 重新获取验证码 */}
+
+ 0}
+ onPress={() => {
+ verifyCodeFetch()
+ .then(() => {
+ showMessage('验证码已发送');
+ start();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ >
+ {`${
+ count === null
+ ? ''
+ : count === 0
+ ? '重发验证码'
+ : `重新获取(${count})`
+ }`}
+
+
+
+
+ {(verifyCodeLoading || loginLoading) && }
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ background: {
+ position: 'absolute',
+ left: 0,
+ top: 0,
+ right: 0,
+ height: 168,
+ },
+ scroll: {
+ flex: 1,
+ },
+ header: {
+ paddingHorizontal: 25,
+ marginBottom: 65,
+ },
+ title: {
+ fontSize: 24,
+ color: '#17171A',
+ fontWeight: '600',
+ },
+ headerText: {
+ fontSize: 14,
+ color: '#17171A',
+ fontWeight: '500',
+ marginVertical: 14,
+ },
+ loginButton: {
+ marginHorizontal: 25,
+ marginBottom: 20,
+ marginTop: 20,
+ },
+ bottom: {
+ flexDirection: 'row',
+ justifyContent: 'center',
+ marginBottom: 64,
+ },
+ bottomText: {
+ fontSize: 14,
+ },
+});
diff --git a/src/app/screens/login/loginWithSMS/api/index.ts b/src/app/screens/login/loginWithSMS/api/index.ts
new file mode 100644
index 0000000..ae809f4
--- /dev/null
+++ b/src/app/screens/login/loginWithSMS/api/index.ts
@@ -0,0 +1 @@
+export * from './useLoginWithVerifyCode';
diff --git a/src/app/screens/login/loginWithSMS/api/useLoginWithVerifyCode.ts b/src/app/screens/login/loginWithSMS/api/useLoginWithVerifyCode.ts
new file mode 100644
index 0000000..597ddfa
--- /dev/null
+++ b/src/app/screens/login/loginWithSMS/api/useLoginWithVerifyCode.ts
@@ -0,0 +1,22 @@
+import {Platform} from 'react-native';
+import {useApi} from '@common/api/useApi';
+import {loginSchema} from '../../api/loginSchema';
+
+// 使用验证码登录
+// {"changeDevice":false,"enableCert":false,"gxLeader":false,"hasBindFirm":false,"hasFaceDetect":false,"hasPwd":true,"realNameStatus":0,"sessionId":"71979180-62c1-425e-a041-b3158944217c","userId":"99b00468e337456cb0005ad10d6d88da","userType":2}
+
+const useLoginWithVerifyCode = (phone: string, smsCode: string) => {
+ return useApi(
+ '/am/v3/userCenter/account/login',
+ 'POSTJSON',
+ {
+ phoneNum: phone,
+ verifyCode: smsCode,
+ deviceType: Platform.select({android: '0', ios: '1'}),
+ },
+ loginSchema,
+ {},
+ );
+};
+
+export {useLoginWithVerifyCode};
diff --git a/src/app/screens/login/register/RegisterScreen.tsx b/src/app/screens/login/register/RegisterScreen.tsx
new file mode 100644
index 0000000..9ef136d
--- /dev/null
+++ b/src/app/screens/login/register/RegisterScreen.tsx
@@ -0,0 +1,266 @@
+import React, { useLayoutEffect, useState } from 'react';
+import {
+ Keyboard,
+ SafeAreaView,
+ ScrollView,
+ StyleSheet,
+ Text,
+ View,
+} from 'react-native';
+import { LoginParamList } from '@app/routes/login/LoginParamList.ts';
+import { StackScreenProps } from '@react-navigation/stack';
+import LoginTextInputItem from '../components/LoginTextInputItem';
+import SubmitButton from '@common/components/SubmitButton';
+import { useCountdown } from '@szyx-mobile/hooks';
+import {
+ COUNTDOWN_REGISTER_KEY,
+ PASSWORD_PATTERN,
+ PHONE_PATTERN,
+ THEME_COLOR,
+} from '@common/constants';
+import Spinner from '@common/components/Spinner';
+import { useSMSVerifyCode } from '../api';
+import { useRegister } from './api';
+import { useLogin } from '@app/hooks/useLogin';
+import { showErrorMessage, showMessage } from '@common/ToastHelper.ts';
+
+const BUTTON_ACTIVE_COLOR = THEME_COLOR;
+const BUTTON_UNACTIVE_COLOR = '#9B9B9B';
+const SMS_CODE_LENGTH = 6; // 验证码长度
+
+type Props = StackScreenProps;
+
+export default function RegisterScreen(props: Props) {
+ const { navigation } = props;
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: '用户注册',
+ });
+ }, [navigation]);
+
+ const { count, start } = useCountdown(59, COUNTDOWN_REGISTER_KEY);
+
+ const login = useLogin();
+
+ // 【状态】
+
+ const [phone, setPhone] = useState(''); // 手机号
+ const [smsCode, setSmsCode] = useState(''); // 验证码
+ const [nickname, setNickname] = useState(''); // 用户姓名
+ const [invitationCode, setInvitationCode] = useState(''); // 团队邀请码
+ const [password, setPassword] = useState(''); // 密码
+ const [passwordAgain, setPasswordAgain] = useState(''); // 再次输入密码
+
+ // 【接口】
+
+ const { loading: verifyCodeLoading, fetchAsync: verifyCodeFetch } =
+ useSMSVerifyCode(phone, 'register'); // 获取验证码
+ const { loading: registerLoading, fetchAsync: registerFetch } = useRegister(
+ phone,
+ smsCode,
+ nickname,
+ invitationCode,
+ password,
+ ); // 注册
+
+ return (
+ <>
+
+
+
+ {
+ setPhone(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setSmsCode(value);
+ }}
+ clearButton={true}
+ rightButton={{
+ title:
+ count === null
+ ? '获取验证码'
+ : count === 0
+ ? '重新获取'
+ : `重新获取(${count})`,
+ color:
+ count === null || count === 0
+ ? BUTTON_ACTIVE_COLOR
+ : BUTTON_UNACTIVE_COLOR,
+ onPress: () => {
+ if (count !== null && count > 0) {
+ return;
+ }
+ if (PHONE_PATTERN.test(phone) === false) {
+ showErrorMessage('请输入正确的手机号码');
+ return;
+ }
+ verifyCodeFetch()
+ .then(() => {
+ showMessage('验证码已发送');
+ start();
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ },
+ }}
+ />
+ {
+ setNickname(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setPassword(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setPasswordAgain(value);
+ }}
+ clearButton={true}
+ />
+ {
+ setInvitationCode(value);
+ }}
+ clearButton={true}
+ />
+
+ {
+ if (PHONE_PATTERN.test(phone) === false) {
+ showErrorMessage('请输入正确的手机号码');
+ return;
+ }
+ if (
+ password.length > 0 &&
+ PASSWORD_PATTERN.test(password) === false
+ ) {
+ showErrorMessage(
+ '密码长度8-12位,支持英文字母、数字与字符,密码必须包含英文加数字',
+ );
+ return;
+ }
+ if (passwordAgain !== password) {
+ showErrorMessage('两次输入的密码不一致');
+ return;
+ }
+ if (invitationCode.length > 0 && invitationCode.length !== 6) {
+ showErrorMessage('团队邀请码应为6位');
+ return;
+ }
+
+ Keyboard.dismiss();
+
+ registerFetch()
+ .then(r => {
+ showMessage('注册成功');
+ login(r.sessionId, {
+ userId: r.userId,
+ userType: r.userType,
+ moduleList: [],
+ isFirstLogin: true,
+ currentUserType:
+ r.userType === 'general' ? 'general' : 'guanxin',
+ enableCert: r.enableCert,
+ realNameStatus: r.realNameStatus,
+ phone: phone,
+ gxLeader: r.gxLeader,
+ hasBindFirm: r.hasBindFirm,
+ hasFaceDetect: r.hasFaceDetect,
+ nickname: undefined,
+ picUrl: undefined,
+ faceDetectFirms: undefined,
+ userIdCardNum: undefined,
+ });
+ })
+ .catch(e => {
+ showErrorMessage(e.message);
+ });
+ }}
+ />
+
+ 电子签名用户请联系管理员注册,请勿自行注册
+
+
+
+ {(verifyCodeLoading || registerLoading) && }
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#FFFFFF',
+ },
+ submit: {
+ marginHorizontal: 25,
+ marginBottom: 15,
+ marginTop: 20,
+ },
+ bottomText: {
+ color: '#EB4900',
+ textAlign: 'center',
+ marginHorizontal: 25,
+ fontSize: 12,
+ marginBottom: 20,
+ },
+});
diff --git a/src/app/screens/login/register/api/index.ts b/src/app/screens/login/register/api/index.ts
new file mode 100644
index 0000000..c73092b
--- /dev/null
+++ b/src/app/screens/login/register/api/index.ts
@@ -0,0 +1 @@
+export * from './useRegister';
diff --git a/src/app/screens/login/register/api/useRegister.ts b/src/app/screens/login/register/api/useRegister.ts
new file mode 100644
index 0000000..190c6f4
--- /dev/null
+++ b/src/app/screens/login/register/api/useRegister.ts
@@ -0,0 +1,31 @@
+import {Platform} from 'react-native';
+import {loginSchema} from '../../api/loginSchema';
+import { useApi } from '@common/api/useApi.ts';
+
+// 用户注册
+// {"changeDevice":false,"enableCert":false,"gxLeader":false,"hasBindFirm":false,"hasFaceDetect":false,"hasPwd":false,"realNameStatus":0,"sessionId":"c701897a-3c49-4d82-b5db-d4c37f91a3eb","userId":"f964953d16f64e6ba64567dda76c3467","userType":2}
+
+const useRegister = (
+ phone: string,
+ smsCode: string,
+ nickname: string,
+ invitationCode: string,
+ password: string,
+) => {
+ return useApi(
+ '/am/v4/userCenter/register',
+ 'POSTJSON',
+ {
+ phoneNum: phone,
+ verifyCode: smsCode,
+ nickname: nickname,
+ khcrmTeamCode: invitationCode,
+ pwd: password,
+ deviceType: Platform.select({android: '0', ios: '1'}),
+ },
+ loginSchema,
+ {},
+ );
+};
+
+export {useRegister};
diff --git a/src/common/StorageHelper.ts b/src/common/StorageHelper.ts
index 43f9280..e7ea95f 100644
--- a/src/common/StorageHelper.ts
+++ b/src/common/StorageHelper.ts
@@ -4,7 +4,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
export const storageApp = new Storage({
size: 1000,
storageBackend: AsyncStorage,
- // defaultExpires: 1000 * 3600 * 24 * 60,
+ defaultExpires: null, // 不过期
enableCache: true,
});
/**
@@ -19,8 +19,15 @@ export const saveItem = (key: string, value: any, expires?: number | null) =>
* 获取一个键值对
* @param key
*/
-export const getItem = (key: string) =>
- storageApp.load({ key, autoSync: true, syncInBackground: true });
+export const getItem = (key: string) => {
+ return new Promise(resolve => {
+ storageApp
+ .load({ key, autoSync: true, syncInBackground: true })
+ .then(data => resolve(data))
+ .catch(() => resolve(undefined));
+ });
+};
+
/**
* 删除一个键值对
* @param key
@@ -30,8 +37,14 @@ export const removeItem = (key: string) => storageApp.remove({ key });
* 获取批量键值对
* @param params
*/
-export const getAllItems = (params: LoadParams[]) =>
- storageApp.getBatchData(params);
+export const getAllItems = (params: LoadParams[]) =>{
+ return new Promise(resolve => {
+ storageApp.getBatchData(params)
+ .then(data => resolve(data))
+ .catch(() => resolve([]));
+ });
+};
+
export const saveAllItems = (
params: {
@@ -41,5 +54,10 @@ export const saveAllItems = (
expires?: number | null;
}[],
) => Promise.all([params.map(item => storageApp.save(item))]);
+
export const removeAllItems = (params: string[]) =>
Promise.all([params.map(item => storageApp.remove({ key: item }))]);
+
+export const getAllKeys = () => {
+ return AsyncStorage.getAllKeys();
+};
diff --git a/src/common/ToastHelper.ts b/src/common/ToastHelper.ts
index 1958a91..dc11a64 100644
--- a/src/common/ToastHelper.ts
+++ b/src/common/ToastHelper.ts
@@ -1,9 +1,20 @@
import Toast from 'react-native-toast-message';
import { ToastType } from 'react-native-toast-message/lib/src/types';
-export const showMessage = (message: string, type?: ToastType, message2?: string) => {
+export const showMessage = (
+ message: string,
+ type?: ToastType,
+ message2?: string,
+) => {
Toast.show({
- type: type??'success',
+ type: type ?? 'success',
+ text1: message,
+ text2: message2,
+ });
+};
+export const showErrorMessage = (message: string, message2?: string) => {
+ Toast.show({
+ type: 'error',
text1: message,
text2: message2,
});
diff --git a/src/common/api/useApi.ts b/src/common/api/useApi.ts
new file mode 100644
index 0000000..18ccf16
--- /dev/null
+++ b/src/common/api/useApi.ts
@@ -0,0 +1,268 @@
+import { useCallback, useEffect, useMemo } from 'react';
+import {
+ AxiosInterceptorManager,
+ AxiosRequestConfig,
+ AxiosResponse,
+ isAxiosError,
+ isCancel,
+ ValidationError,
+} from '@szyx-mobile/use-axios';
+import {
+ RequestError,
+ RequestOptions,
+ useRequest,
+} from '@szyx-mobile/use-request';
+import { z } from 'zod';
+import { Platform } from 'react-native';
+import { useAuth } from '@common/contexts/useAuth.ts';
+import { useCommon } from '@common/contexts/useCommon.ts';
+import Toast from 'react-native-toast-message';
+import { getEnvUrl } from '@common/env/envUtils.ts';
+import { md5_hex } from '@common/utils/md5';
+import { CLIENT_ID, MD5_KEY } from '@common/constants';
+
+const SUCCESS_STATUS = /^0$/; // 请求成功的状态码
+
+type Response = {
+ status: string;
+ message: string;
+ data: T;
+};
+
+const useApi = <
+ S extends z.ZodTypeAny,
+ T extends z.infer = unknown,
+ D = unknown,
+>(
+ url: string,
+ method: 'GET' | 'POSTJSON' | 'POSTFORM' | 'PUT' | 'UPLOAD',
+ params?: D,
+ validationSchema?: S,
+ options?: RequestOptions,
+ config?: AxiosRequestConfig,
+): {
+ response?: T;
+ error?: RequestError;
+ loading: boolean;
+ fetch: (config?: AxiosRequestConfig) => void;
+ fetchAsync: (config?: AxiosRequestConfig) => Promise;
+ cancel: () => void;
+ requestInterceptors: AxiosInterceptorManager>;
+ responseInterceptors: AxiosInterceptorManager<
+ AxiosResponse, unknown>
+ >;
+} => {
+ const {
+ state: { token, userInfo },
+ actions: { logout },
+ } = useAuth();
+
+ const {
+ state: { info },
+ } = useCommon();
+
+ // 【这里增加公共参数】
+ const commonParams = useMemo(() => {
+ return {};
+ }, []);
+
+ // 【这里添加额外的 headers】
+ const commonHeaders = useMemo(() => {
+ try {
+ const deviceInfo = JSON.parse(info.deviceInfo ?? '{}') as Record<
+ string,
+ string
+ >;
+
+ return {
+ clientId: CLIENT_ID, // 厂商唯一标识,这个值是为医网信app分配的
+ deviceType: Platform.select({ android: '0', ios: '1' }),
+ version: deviceInfo.versionName,
+ deviceId: deviceInfo.deviceId,
+ phoneModel: deviceInfo.phoneModel,
+ phoneBrand: deviceInfo.phoneBrand,
+ phoneVersion: deviceInfo.phoneVersion,
+
+ userId: userInfo?.userId ?? '',
+ sessionId: token ?? '',
+ currentClientId: '',
+ appType: '0', // 医网信是 0,冠新是 1
+ };
+ } catch {
+ return {};
+ }
+ }, [info.deviceInfo, token, userInfo?.userId]);
+
+ // 【这里定义 Response 的 schema】
+ const responseSchema = useMemo(() => {
+ // 这里 code 和 message 字段为项目接口中相应的字段名和字段类型
+ return z.object({
+ status: z.string().regex(SUCCESS_STATUS),
+ message: z.string(),
+ data: validationSchema ?? z.unknown(),
+ });
+ }, [validationSchema]);
+
+ const {
+ response,
+ error,
+ loading,
+ fetch,
+ fetchAsync,
+ cancel,
+ requestInterceptors,
+ responseInterceptors,
+ } = useRequest, unknown>(
+ url,
+ method,
+ { ...params, ...commonParams },
+ responseSchema,
+ options,
+ {
+ // 【这里配置 baseURL】
+ baseURL: getEnvUrl(info.env ?? 'production').url,
+ ...config,
+ timeout: 30000,
+ headers: {
+ ...commonHeaders,
+ ...config?.headers,
+ },
+ },
+ );
+
+ useEffect(() => {
+ const requestInterceptor = requestInterceptors.use(c => {
+ // 这几个header的参数因为是实时的,所以单独拿出来
+ const timeStamp = Date.now();
+ const signOrigin = `timeStamp=${timeStamp}#${MD5_KEY}`;
+ const sign = md5_hex(signOrigin);
+ c.headers = {
+ ...c.headers,
+ timeStamp: timeStamp + '',
+ sign: sign,
+ };
+ return c;
+ });
+ return () => {
+ requestInterceptors.eject(requestInterceptor);
+ };
+ }, [requestInterceptors]);
+
+ useEffect(() => {
+ const responseInterceptor = responseInterceptors.use(
+ r => {
+ if (options?.log) {
+ console.debug(JSON.stringify(r, null, 2));
+ }
+ return r;
+ },
+ e => {
+ if (isCancel(e)) {
+ return Promise.reject(
+ new RequestError('网络请求已取消', 'Cancel', e),
+ );
+ }
+
+ if (e instanceof ValidationError) {
+ console.error(
+ JSON.stringify(
+ {
+ name: e.name,
+ status: e.response.status,
+ data: e.response.data,
+ config: e.response.config,
+ headers: e.response.headers,
+ issues: e.issues,
+ url: `${e.response.config.baseURL}${e.response.config.url}`,
+ },
+ null,
+ 2,
+ ),
+ );
+ // 退出登录逻辑
+ if (e.response.data?.status === '017x025') {
+ Toast.show(
+ e.response.data?.message ??
+ '该账号已在其他设备登录,请重新登录!',
+ );
+ logout();
+
+ return Promise.reject(
+ new RequestError(
+ e.response.data?.message ??
+ '该账号已在其他设备登录,请重新登录!',
+ 'ValidationError',
+ e,
+ ),
+ );
+ }
+
+ if (e.issues.length <= 0) {
+ return Promise.reject(
+ new RequestError('数据格式校验错误', 'ValidationError', e),
+ );
+ }
+ // 这里 code 为项目接口中相应的字段名
+ if (e.issues[0].path.length > 0 && e.issues[0].path[0] === 'status') {
+ return Promise.reject(
+ new RequestError(
+ e.response.data?.message ?? '', // 这里 message 为项目接口中相应的字段名
+ 'ValidationError',
+ e,
+ ),
+ );
+ }
+ return Promise.reject(
+ new RequestError(e.issues[0].message, 'ValidationError', e),
+ );
+ }
+
+ if (isAxiosError(e)) {
+ console.error(
+ JSON.stringify(e, null, 2),
+ `${e.config?.baseURL}${e.config?.url}`,
+ );
+ return Promise.reject(
+ new RequestError('网络请求失败', 'AxiosError', e),
+ );
+ }
+
+ console.error(
+ JSON.stringify(e, null, 2),
+ `${e.config?.baseURL}${e.config?.url}`,
+ );
+ return Promise.reject(
+ new RequestError('网络请求失败', 'OtherError', e),
+ );
+ },
+ );
+ return () => {
+ responseInterceptors.eject(responseInterceptor);
+ };
+ }, [logout, options?.log, options?.tag, responseInterceptors]);
+
+ const transformedFetchAsync = useCallback(
+ async (c?: AxiosRequestConfig) => {
+ try {
+ const r = await fetchAsync(c);
+ return r.data.data;
+ } catch (e) {
+ return Promise.reject(e);
+ }
+ },
+ [fetchAsync],
+ );
+
+ return {
+ response: response?.data.data,
+ error: error as RequestError,
+ loading,
+ fetch,
+ fetchAsync: transformedFetchAsync,
+ cancel,
+ requestInterceptors,
+ responseInterceptors,
+ };
+};
+
+export { useApi };
diff --git a/src/common/api/usePageApi.ts b/src/common/api/usePageApi.ts
new file mode 100644
index 0000000..20e6b4a
--- /dev/null
+++ b/src/common/api/usePageApi.ts
@@ -0,0 +1,133 @@
+import {useEffect, useState} from 'react';
+import {AxiosRequestConfig, ValidationOptions} from '@szyx-mobile/use-axios';
+import {RequestError} from '@szyx-mobile/use-request';
+import z from 'zod';
+import {useApi} from './useApi';
+
+type List = {
+ records: T[];
+};
+
+type Root = T[];
+
+interface PageOptions extends ValidationOptions {
+ pageSize?: number; // 默认为 10
+ path?: 'list' | 'root'; // 默认为 'list'
+ pagination?: 'cursor' | 'offset'; // 默认为 'cursor' offset 代表传入 current 和 size 进行分页,cursor 代表传入 startNum 和 endNum 进行分页
+}
+
+const usePageApi = <
+ S extends z.ZodTypeAny,
+ T extends z.infer = unknown,
+ P extends List | Root = List,
+ D = Record,
+>(
+ url: string,
+ method: 'GET' | 'POSTJSON' | 'POSTFORM',
+ params?: D,
+ validationSchema?: S,
+ options?: PageOptions,
+ config?: AxiosRequestConfig,
+): {
+ response?: T[];
+ error?: RequestError;
+ loading: boolean;
+ fetch: React.Dispatch>;
+ cancel: () => void;
+} => {
+ const [data, setData] = useState(undefined);
+ const [status, setStatus] = useState<'loaded' | 'reload' | 'loadmore'>(
+ 'loaded',
+ );
+
+ const listSchema: z.ZodSchema> = z.object({
+ records: z.array(validationSchema ?? z.any()),
+ });
+
+ const rootSchema: z.ZodSchema> = z.array(validationSchema ?? z.any());
+
+ const {error, loading, fetchAsync, cancel} = useApi<
+ typeof listSchema | typeof rootSchema,
+ P
+ >(
+ url,
+ method,
+ options?.pagination === 'offset'
+ ? {
+ ...params,
+ current:
+ status === 'loadmore'
+ ? Math.ceil((data ?? []).length / (options?.pageSize ?? 10)) + 1
+ : 1,
+ size: options?.pageSize ?? 10,
+ }
+ : {
+ ...params,
+ startNum: status === 'loadmore' ? (data ?? []).length : 0,
+ endNum:
+ status === 'loadmore'
+ ? (data ?? []).length + (options?.pageSize ?? 10) - 1
+ : (options?.pageSize ?? 10) - 1,
+ },
+ options?.path === 'root' ? rootSchema : listSchema,
+ options,
+ config,
+ );
+
+ useEffect(() => {
+ if (
+ status === 'loaded' ||
+ (status === 'loadmore' &&
+ ((data ?? []).length === 0 ||
+ (data ?? []).length % (options?.pageSize ?? 10) !== 0))
+ ) {
+ return;
+ }
+ fetchAsync()
+ .then(r => {
+ if (status === 'reload') {
+ switch (options?.path) {
+ case 'root':
+ const rr = r as Root;
+ setData(rr);
+ setStatus('loaded');
+ break;
+ default:
+ const lr = r as List;
+ setData(lr.records);
+ setStatus('loaded');
+ break;
+ }
+ } else {
+ switch (options?.path) {
+ case 'root':
+ const rr = r as Root;
+ setData(d => {
+ return [...(d ?? []), ...rr];
+ });
+ setStatus('loaded');
+ break;
+ default:
+ const lr = r as List;
+ setData(d => {
+ return [...(d ?? []), ...lr.records];
+ });
+ setStatus('loaded');
+ break;
+ }
+ }
+ })
+ .catch(() => {
+ setStatus('loaded');
+ });
+ }, [data, fetchAsync, options?.pageSize, options?.path, status]);
+
+ const typedFetch = setStatus as React.Dispatch<
+ React.SetStateAction<'reload' | 'loadmore'>
+ >;
+
+ return {response: data, error, loading, fetch: typedFetch, cancel};
+};
+
+export type {AxiosRequestConfig};
+export {usePageApi};
diff --git a/src/common/assets/images/common_appicon.png b/src/common/assets/images/common_appicon.png
new file mode 100644
index 0000000..3d8d2a7
Binary files /dev/null and b/src/common/assets/images/common_appicon.png differ
diff --git a/src/common/components/Spinner.tsx b/src/common/components/Spinner.tsx
index 3ffa852..b0487f7 100644
--- a/src/common/components/Spinner.tsx
+++ b/src/common/components/Spinner.tsx
@@ -1,7 +1,7 @@
import React, { JSX } from 'react';
import { Platform, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
import Spin from 'react-native-spinkit';
-import { THEME_COLOR } from '@app/constants';
+import { THEME_COLOR } from '@common/constants';
function Spinner(props: {
containerStyle?: StyleProp;
diff --git a/src/common/components/SubmitButton.tsx b/src/common/components/SubmitButton.tsx
new file mode 100644
index 0000000..ddf5327
--- /dev/null
+++ b/src/common/components/SubmitButton.tsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import {
+ Image,
+ ImageSourcePropType,
+ ImageStyle,
+ StyleProp,
+ StyleSheet,
+ Text,
+ TextStyle,
+ TouchableOpacity,
+ ViewStyle,
+} from 'react-native';
+import {THEME_COLOR, THEME_COLOR_DISABLED} from '@common/constants';
+
+const THEME_COLOR_LIGHT = '#FFFFFF';
+const BACK_COLOR_LIGHT = 'rgba(0 , 0, 0, 0)';
+
+type Props = {
+ title: string; // 按钮文字
+ disabled?: boolean; // 是否禁用
+ icon?: ImageSourcePropType; // 图标
+ theme: 'light' | 'dark'; // 按钮主题,默认深色
+ onPress?: () => void; // 点击按钮回调
+ style?: StyleProp; // 样式
+ textStyle?: StyleProp; // 文字样式
+ iconStyle?: StyleProp; // 图标样式
+};
+
+/**
+ * @description: 项目中常见的用于提交的按钮,长条圆角,主题色。
+ */
+export default function SubmitButton(props: Props) {
+ return (
+ {
+ props.onPress && props.onPress();
+ }}>
+ {props.icon && (
+
+ )}
+
+ {props.title}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ borderWidth: 1,
+ borderRadius: 50,
+ paddingVertical: 13,
+ paddingHorizontal: 8,
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ },
+ text: {
+ fontSize: 16,
+ fontWeight: 'bold',
+ },
+ icon: {
+ width: 17,
+ height: 17,
+ marginRight: 5,
+ },
+});
diff --git a/src/app/constants/index.ts b/src/common/constants/index.ts
similarity index 100%
rename from src/app/constants/index.ts
rename to src/common/constants/index.ts
diff --git a/src/app/constants/types.ts b/src/common/constants/types.ts
similarity index 90%
rename from src/app/constants/types.ts
rename to src/common/constants/types.ts
index 6476138..c96facd 100644
--- a/src/app/constants/types.ts
+++ b/src/common/constants/types.ts
@@ -35,6 +35,11 @@ export type UserInfo = {
// 7、消息-列表
// 8、消息-详情
// 9、热点资讯-图片菜单
+ // 10、自动签名页面
+ // 11、授权签名-我的授权码页面
+ // 12、我的授权码-授权成功页面
+ // 13、授权签名-指定授权页面
+ // 14、指定授权-授权签名后页面
moduleList: number[] | undefined; // 是否允许显示广告
isFirstLogin: boolean; // 是否第一次登录(第一次登录不允许显示广告)
// ...
diff --git a/src/app/constants/values.ts b/src/common/constants/values.ts
similarity index 98%
rename from src/app/constants/values.ts
rename to src/common/constants/values.ts
index 4994407..71c28d7 100644
--- a/src/app/constants/values.ts
+++ b/src/common/constants/values.ts
@@ -44,7 +44,7 @@ export const TAB_BAR_INACTIVE_TINT_COLOR = '#333333'; // 标签栏未选中颜
export const TAB_BAR_ICON_SIZE = 20; // 标签栏图标宽高值,最大为25
// 配置
-export const MD5_KEY = 'YwqMain!@#'; // 医网信进行md5运算的key
+export const MD5_KEY = 'YWQ!@#'; // 医网信进行md5运算的key
export const CLIENT_ID = '2000111111110002'; // 厂商唯一标识,这个值是为医网信app分配的
export const GUIDE_PAGES_VERSION = '7.0'; // 引导页版本号 - 只有当设计了全新的引导页时,才修改这里
export const APP_TOUR_VERSION = '7.0'; // 版本引导版本号 - 只有当设计了全新的版本引导时,才修改这里
diff --git a/src/app/contexts/AuthContext.tsx b/src/common/contexts/AuthContext.tsx
similarity index 95%
rename from src/app/contexts/AuthContext.tsx
rename to src/common/contexts/AuthContext.tsx
index d79d440..1680eb5 100644
--- a/src/app/contexts/AuthContext.tsx
+++ b/src/common/contexts/AuthContext.tsx
@@ -1,6 +1,6 @@
import React, { createContext, ReactNode, useEffect, useReducer } from 'react';
-import { TOKEN_KEY, UserInfo, USERINFO_KEY } from '@app/constants';
-import { getAllItems, storageApp } from '@common/StorageHelper.ts';
+import { getAllItems } from '@common/StorageHelper.ts';
+import { TOKEN_KEY, UserInfo, USERINFO_KEY } from '@common/constants';
type RequiredUserInfo = Required;
diff --git a/src/app/contexts/CommonContext.tsx b/src/common/contexts/CommonContext.tsx
similarity index 96%
rename from src/app/contexts/CommonContext.tsx
rename to src/common/contexts/CommonContext.tsx
index c6c9a79..20d306b 100644
--- a/src/app/contexts/CommonContext.tsx
+++ b/src/common/contexts/CommonContext.tsx
@@ -1,6 +1,6 @@
import React, { createContext, ReactNode, useEffect, useReducer } from 'react';
-import { CommonInfo, COMMONINFO_KEY } from '@app/constants';
import { getItem } from '@common/StorageHelper.ts';
+import { CommonInfo, COMMONINFO_KEY } from '@common/constants';
type PartialCommonInfo = Partial;
type RequiredCommonInfo = Required;
diff --git a/src/app/contexts/useAuth.ts b/src/common/contexts/useAuth.ts
similarity index 93%
rename from src/app/contexts/useAuth.ts
rename to src/common/contexts/useAuth.ts
index f3cfa4e..bcb7b49 100644
--- a/src/app/contexts/useAuth.ts
+++ b/src/common/contexts/useAuth.ts
@@ -1,15 +1,16 @@
import { useContext, useEffect, useMemo, useRef } from 'react';
-import {
- AuthContext,
- AuthState,
- RequiredUserInfo,
-} from '@app/contexts/AuthContext';
-import { TOKEN_KEY, USERINFO_KEY } from '@app/constants';
+
import {
removeAllItems,
saveAllItems,
saveItem,
} from '@common/StorageHelper.ts';
+import { TOKEN_KEY, USERINFO_KEY } from '@common/constants';
+import {
+ AuthContext,
+ AuthState,
+ RequiredUserInfo,
+} from '@common/contexts/AuthContext.tsx';
const useAuth = (): {
state: AuthState;
diff --git a/src/app/contexts/useCommon.ts b/src/common/contexts/useCommon.ts
similarity index 93%
rename from src/app/contexts/useCommon.ts
rename to src/common/contexts/useCommon.ts
index ff8aa0a..bf87d44 100644
--- a/src/app/contexts/useCommon.ts
+++ b/src/common/contexts/useCommon.ts
@@ -1,11 +1,12 @@
import {useContext, useEffect, useMemo, useRef} from 'react';
+
+import { getItem, saveItem } from '@common/StorageHelper.ts';
+import { COMMONINFO_KEY } from '@common/constants';
import {
CommonContext,
CommonState,
PartialCommonInfo,
-} from '@app/contexts/CommonContext';
-import {COMMONINFO_KEY} from '@app/constants';
-import { getItem, saveItem } from '@common/StorageHelper.ts';
+} from '@common/contexts/CommonContext.tsx';
const useCommon = (): {
state: CommonState;
diff --git a/src/common/env/env.ts b/src/common/env/env.ts
new file mode 100644
index 0000000..3e469d5
--- /dev/null
+++ b/src/common/env/env.ts
@@ -0,0 +1,32 @@
+import {RequiredCommonInfo} from '@app/contexts/CommonContext';
+
+type Env = {
+ env: RequiredCommonInfo['env'];
+ title: string;
+ url: string;
+};
+
+const ENV: Env[] = [
+ {
+ env: 'production',
+ title: '生产环境',
+ url: 'https://www.51trust.com',
+ },
+ {
+ env: 'acceptance',
+ title: '集成环境',
+ url: 'https://test.51trust.com',
+ },
+ {
+ env: 'testing',
+ title: '测试环境',
+ url: 'https://beta.51trust.com',
+ },
+ {
+ env: 'development',
+ title: '开发环境',
+ url: 'https://dev.51trust.com',
+ },
+];
+
+export default ENV;
diff --git a/src/common/env/envUtils.ts b/src/common/env/envUtils.ts
new file mode 100644
index 0000000..f04f327
--- /dev/null
+++ b/src/common/env/envUtils.ts
@@ -0,0 +1,21 @@
+import {RequiredCommonInfo} from '@app/contexts/CommonContext';
+import ENV from '@common/env/env.ts';
+
+/**
+ * @description: 直接根据环境类型获得环境的配置
+ * @param env 环境类型
+ * @return 具体某个环境的配置对象
+ */
+function getEnvUrl(env: RequiredCommonInfo['env']) {
+ if (ENV.filter(e => e.env === 'production').length === 0) {
+ throw new Error('"production" environment must be specified!');
+ }
+
+ const envs = ENV.filter(e => e.env === env);
+ if (envs.length > 0) {
+ return envs[0];
+ }
+ return ENV.filter(e => e.env === 'production')[0];
+}
+
+export {getEnvUrl};
diff --git a/src/common/env/useEnv.ts b/src/common/env/useEnv.ts
new file mode 100644
index 0000000..8b3de9d
--- /dev/null
+++ b/src/common/env/useEnv.ts
@@ -0,0 +1,25 @@
+import {useMemo} from 'react';
+import { useCommon } from '@common/contexts/useCommon.ts';
+import ENV from '@common/env/env.ts';
+
+function useEnv() {
+ const {
+ state: {info},
+ } = useCommon();
+
+ const env = useMemo(() => {
+ if (ENV.filter(e => e.env === 'production').length === 0) {
+ throw new Error('"production" environment must be specified!');
+ }
+
+ const envs = ENV.filter(e => e.env === info.env);
+ if (envs.length > 0) {
+ return envs[0];
+ }
+ return ENV.filter(e => e.env === 'production')[0];
+ }, [info.env]);
+
+ return env;
+}
+
+export {useEnv};
diff --git a/src/common/router/CommonParamList.ts b/src/common/router/CommonParamList.ts
new file mode 100644
index 0000000..d7cce7d
--- /dev/null
+++ b/src/common/router/CommonParamList.ts
@@ -0,0 +1,6 @@
+export type CommonParamList = {
+ WebView: {
+ url: string; // 地址
+ title?: string; // 标题
+ }; // 通用的 webview 页面
+};
diff --git a/src/common/screens/webview/WebViewScreen.tsx b/src/common/screens/webview/WebViewScreen.tsx
new file mode 100644
index 0000000..db800af
--- /dev/null
+++ b/src/common/screens/webview/WebViewScreen.tsx
@@ -0,0 +1,58 @@
+import React, {useLayoutEffect} from 'react';
+import {ActivityIndicator, StyleSheet, View} from 'react-native';
+import {StackScreenProps} from '@react-navigation/stack';
+import WebView from 'react-native-webview';
+import {THEME_COLOR} from '@common/constants';
+import { CommonParamList } from '@common/router/CommonParamList.ts';
+
+type Props = StackScreenProps;
+
+export default function WebViewScreen(props: Props) {
+ const {navigation, route} = props;
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ title: route.params.title ?? '',
+ });
+ }, [navigation, route.params.title]);
+
+ return (
+
+ {
+ return (
+
+ );
+ }}
+ />
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ webview: {
+ flex: 1,
+ },
+ activityIndicator: {
+ position: 'absolute',
+ alignSelf: 'center',
+ top: 100,
+ },
+});
diff --git a/src/common/utils/commonUtils.ts b/src/common/utils/commonUtils.ts
new file mode 100644
index 0000000..b02fda0
--- /dev/null
+++ b/src/common/utils/commonUtils.ts
@@ -0,0 +1,74 @@
+// 这里放置一些公共的工具函数
+
+import {md5_hex} from './md5';
+
+/**
+ * @description: 比较两个版本号的大小。比如 6.0 和 7.3.1
+ * @param v1
+ * @param v2
+ * @return 如果 v1 更大,返回 1;如果 v2 更大,返回 -1;否则返回 0
+ */
+const compareVersions = (v1: string, v2: string): number => {
+ const v1Parts = v1.split('.').map(Number);
+ const v2Parts = v2.split('.').map(Number);
+
+ for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
+ const v1Part = v1Parts[i] || 0;
+ const v2Part = v2Parts[i] || 0;
+
+ if (v1Part > v2Part) {
+ return 1;
+ }
+ if (v1Part < v2Part) {
+ return -1;
+ }
+ }
+
+ return 0;
+};
+
+// 暂时使用该防抖函数,之后要优化
+const functionReqMap: Map<
+ string,
+ {
+ startTime: number;
+ wait: number;
+ }
+> = new Map();
+// eslint-disable-next-line @typescript-eslint/ban-types
+const debounce = (fun: Function, wait = 1500): void => {
+ const funcValue1 = fun.toString();
+ const hash = md5_hex(funcValue1);
+ const startTime = Date.now();
+ if (functionReqMap.get(hash)) {
+ const funcValue = functionReqMap.get(hash);
+ // 防止因为特殊原因没有移除map中该函数的记录,导致一直无法执行函数的问题
+ if (funcValue && funcValue.startTime + funcValue.wait <= startTime) {
+ functionReqMap.delete(hash);
+ } else {
+ return;
+ }
+ }
+ functionReqMap.set(hash, {
+ startTime: startTime,
+ wait: wait,
+ });
+ // 执行函数调用
+ fun();
+ // 拦截在wait期间的函数再次调用,在超时后,将限制解除
+ setTimeout(() => {
+ const needRemoveKeys: string[] = [];
+ functionReqMap.forEach((value, key, map) => {
+ const curTime = Date.now();
+ const funcValue = map.get(key);
+ if (funcValue && funcValue.startTime + funcValue.wait <= curTime) {
+ needRemoveKeys.push(key);
+ }
+ });
+ needRemoveKeys.map(value => {
+ functionReqMap.delete(value);
+ });
+ }, wait);
+};
+
+export {compareVersions, debounce};
diff --git a/src/common/utils/md5.js b/src/common/utils/md5.js
new file mode 100644
index 0000000..f96e3b3
--- /dev/null
+++ b/src/common/utils/md5.js
@@ -0,0 +1,254 @@
+/* eslint-disable no-array-constructor */
+/* eslint-disable no-bitwise */
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+var b64pad = ''; /* base-64 pad character. "=" for strict RFC compliance */
+var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+//32位小写
+export function md5_hex(s) {
+ return binl2hex(core_md5(str2binl(s), s.length * chrsz));
+}
+export function md5_b64(s) {
+ return binl2b64(core_md5(str2binl(s), s.length * chrsz));
+}
+export function md5_str(s) {
+ return binl2str(core_md5(str2binl(s), s.length * chrsz));
+}
+export function md5_hex_hmac(key, data) {
+ return binl2hex(core_hmac_md5(key, data));
+}
+export function md5_b64_hmac(key, data) {
+ return binl2b64(core_hmac_md5(key, data));
+}
+export function md5_str_hmac(key, data) {
+ return binl2str(core_hmac_md5(key, data));
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len) {
+ /* append padding */
+ x[len >> 5] |= 0x80 << len % 32;
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for (var i = 0; i < x.length; i += 16) {
+ var olda = a;
+ var oldb = b;
+ var oldc = c;
+ var oldd = d;
+
+ a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
+ d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+ c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+ b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+ a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+ d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+ c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+ b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+ a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+ d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+ c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+ b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+ a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+ d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+ c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+ b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+ a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+ d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+ c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+ b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
+ a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+ d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+ c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+ b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+ a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+ d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+ c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+ b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+ a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+ d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+ c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+ b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+ a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+ d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+ c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+ b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+ a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+ d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+ c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+ b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+ a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+ d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
+ c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+ b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+ a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+ d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+ c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+ b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+ a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
+ d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+ c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+ b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+ a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+ d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+ c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+ b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+ a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+ d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+ c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+ b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+ a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+ d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+ c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+ b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ }
+ return Array(a, b, c, d);
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t) {
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
+}
+function md5_ff(a, b, c, d, x, s, t) {
+ return md5_cmn((b & c) | (~b & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t) {
+ return md5_cmn((b & d) | (c & ~d), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t) {
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t) {
+ return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data) {
+ var bkey = str2binl(key);
+ if (bkey.length > 16) {
+ bkey = core_md5(bkey, key.length * chrsz);
+ }
+
+ var ipad = Array(16),
+ opad = Array(16);
+ for (var i = 0; i < 16; i++) {
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5c5c5c5c;
+ }
+
+ var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+ return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y) {
+ var lsw = (x & 0xffff) + (y & 0xffff);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xffff);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt) {
+ return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str) {
+ var bin = Array();
+ var mask = (1 << chrsz) - 1;
+ for (var i = 0; i < str.length * chrsz; i += chrsz) {
+ bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << i % 32;
+ }
+ return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin) {
+ var str = '';
+ var mask = (1 << chrsz) - 1;
+ for (var i = 0; i < bin.length * 32; i += chrsz) {
+ str += String.fromCharCode((bin[i >> 5] >>> i % 32) & mask);
+ }
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray) {
+ var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef';
+ var str = '';
+ for (var i = 0; i < binarray.length * 4; i++) {
+ str +=
+ hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xf) +
+ hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xf);
+ }
+ return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray) {
+ var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ var str = '';
+ for (var i = 0; i < binarray.length * 4; i += 3) {
+ var triplet =
+ (((binarray[i >> 2] >> (8 * (i % 4))) & 0xff) << 16) |
+ (((binarray[(i + 1) >> 2] >> (8 * ((i + 1) % 4))) & 0xff) << 8) |
+ ((binarray[(i + 2) >> 2] >> (8 * ((i + 2) % 4))) & 0xff);
+ for (var j = 0; j < 4; j++) {
+ if (i * 8 + j * 6 > binarray.length * 32) {
+ str += b64pad;
+ } else {
+ str += tab.charAt((triplet >> (6 * (3 - j))) & 0x3f);
+ }
+ }
+ }
+ return str;
+}
diff --git a/yarn.lock b/yarn.lock
index adf9044..d1ab307 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10,6 +10,13 @@
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
+"@anatine/zod-mock@^3.12.0":
+ version "3.14.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@anatine/zod-mock/-/zod-mock-3.14.0.tgz#7bd9399a2031e12161fe643ab43ccf73123cc9d6"
+ integrity sha512-pTYyn9dlszUl5gMGWtE7DBRvx1EcqpBJ1iJ73rlJYCZr9lgIEdZ6PCZQqoXIdlC8DYIwuT1M3/N8arkJa7A10Q==
+ dependencies:
+ randexp "^0.5.3"
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7", "@babel/code-frame@^7.27.1":
version "7.27.1"
resolved "https://nexus-inner.51trust.com/repository/npm/@babel/code-frame/-/code-frame-7.27.1.tgz"
@@ -1279,6 +1286,11 @@
resolved "https://nexus-inner.51trust.com/repository/npm/@eslint/js/-/js-8.57.1.tgz"
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
+"@faker-js/faker@^8.0.0":
+ version "8.4.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@faker-js/faker/-/faker-8.4.1.tgz#5d5e8aee8fce48f5e189bf730ebd1f758f491451"
+ integrity sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==
+
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
version "9.3.0"
resolved "https://nexus-inner.51trust.com/repository/npm/@hapi/hoek/-/hoek-9.3.0.tgz"
@@ -1784,6 +1796,11 @@
prompts "^2.4.2"
semver "^7.5.2"
+"@react-native-community/hooks@^100.1.0":
+ version "100.1.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@react-native-community/hooks/-/hooks-100.1.0.tgz#34cfa9e986b84a8babfe667dc3ec497954aef8f1"
+ integrity sha512-aXESGr6WcnwyeIl+SiCLNFpbApzTmupyYzm2OkGjMuwHr42+a3G65xxTxN/xHuNdTVWO0dXbOwyLgEkQ0i/qZg==
+
"@react-native/assets-registry@0.80.1":
version "0.80.1"
resolved "https://nexus-inner.51trust.com/repository/npm/@react-native/assets-registry/-/assets-registry-0.80.1.tgz"
@@ -2055,6 +2072,28 @@
dependencies:
"@sinonjs/commons" "^3.0.0"
+"@szyx-mobile/hooks@^1.2.0":
+ version "1.2.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@szyx-mobile/hooks/-/hooks-1.2.0.tgz#0bbfcbd947872d4e34e172bd4829b3ff100a6dcc"
+ integrity sha512-VHHOUylD5ZURPQloayh7ycUNkcWbLTNKYYqoXmDGW2T52jCX3v8WKoZvcI7SeRLLJA7ikE2Cki+O3noFrRRehQ==
+
+"@szyx-mobile/use-axios@^1.2.3":
+ version "1.2.3"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@szyx-mobile/use-axios/-/use-axios-1.2.3.tgz#42a05809cf727a14cef742d500a7ebdce36d7344"
+ integrity sha512-3Hfn42MnYFWg145I29bkHYCVAc+Bbhc8uEzvIqBxkByROAcmxc9FL2iOkNfzwaiDEvvsz+zhrwcO0+XJA3egDg==
+ dependencies:
+ "@anatine/zod-mock" "^3.12.0"
+ "@faker-js/faker" "^8.0.0"
+ axios "^1.3.0"
+ zod "^3.21.0"
+
+"@szyx-mobile/use-request@^1.2.3":
+ version "1.2.3"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@szyx-mobile/use-request/-/use-request-1.2.3.tgz#d33e34fb179c390dc39de1b1e836257ec10eb05b"
+ integrity sha512-tSb+cvm+vSr4wIf31tLVW7YMJsK7ZrgQmaU90y2Z04dl9amwtGegP3f6Wv4MySSVRm2KMTXOF531IYz+O8n1Hg==
+ dependencies:
+ "@szyx-mobile/use-axios" "^1.2.3"
+
"@types/babel__core@^7.1.14":
version "7.20.5"
resolved "https://nexus-inner.51trust.com/repository/npm/@types/babel__core/-/babel__core-7.20.5.tgz"
@@ -2326,6 +2365,11 @@
resolved "https://nexus-inner.51trust.com/repository/npm/@vscode/sudo-prompt/-/sudo-prompt-9.3.1.tgz"
integrity sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA==
+"@yarnpkg/lockfile@^1.1.0":
+ version "1.1.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
+ integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
+
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/abort-controller/-/abort-controller-3.0.0.tgz"
@@ -2569,6 +2613,16 @@ async-limiter@~1.0.0:
resolved "https://nexus-inner.51trust.com/repository/npm/async-limiter/-/async-limiter-1.0.1.tgz"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+at-least-node@^1.0.0:
+ version "1.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+ integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+
available-typed-arrays@^1.0.7:
version "1.0.7"
resolved "https://nexus-inner.51trust.com/repository/npm/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz"
@@ -2576,6 +2630,15 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
+axios@^1.3.0:
+ version "1.11.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/axios/-/axios-1.11.0.tgz#c2ec219e35e414c025b2095e8b8280278478fdb6"
+ integrity sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==
+ dependencies:
+ follow-redirects "^1.15.6"
+ form-data "^4.0.4"
+ proxy-from-env "^1.1.0"
+
babel-jest@^29.7.0:
version "29.7.0"
resolved "https://nexus-inner.51trust.com/repository/npm/babel-jest/-/babel-jest-29.7.0.tgz"
@@ -3049,6 +3112,13 @@ colorette@^1.0.7:
resolved "https://nexus-inner.51trust.com/repository/npm/colorette/-/colorette-1.4.0.tgz"
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
+combined-stream@^1.0.8:
+ version "1.0.8"
+ resolved "https://nexus-inner.51trust.com/repository/npm/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
command-exists@^1.2.8:
version "1.2.9"
resolved "https://nexus-inner.51trust.com/repository/npm/command-exists/-/command-exists-1.2.9.tgz"
@@ -3274,6 +3344,11 @@ define-properties@^1.1.3, define-properties@^1.2.1:
has-property-descriptors "^1.0.0"
object-keys "^1.1.1"
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
depd@2.0.0:
version "2.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/depd/-/depd-2.0.0.tgz"
@@ -3325,6 +3400,11 @@ dotenv@^8.1.0:
resolved "https://nexus-inner.51trust.com/repository/npm/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
+drange@^1.0.2:
+ version "1.1.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/drange/-/drange-1.1.1.tgz#b2aecec2aab82fcef11dbbd7b9e32b83f8f6c0b8"
+ integrity sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==
+
dunder-proto@^1.0.0, dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://nexus-inner.51trust.com/repository/npm/dunder-proto/-/dunder-proto-1.0.1.tgz"
@@ -3928,6 +4008,13 @@ find-up@^5.0.0:
locate-path "^6.0.0"
path-exists "^4.0.0"
+find-yarn-workspace-root@^2.0.0:
+ version "2.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
+ integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==
+ dependencies:
+ micromatch "^4.0.2"
+
flat-cache@^3.0.4:
version "3.2.0"
resolved "https://nexus-inner.51trust.com/repository/npm/flat-cache/-/flat-cache-3.2.0.tgz"
@@ -3947,6 +4034,11 @@ flow-enums-runtime@^0.0.6:
resolved "https://nexus-inner.51trust.com/repository/npm/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz"
integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==
+follow-redirects@^1.15.6:
+ version "1.15.11"
+ resolved "https://nexus-inner.51trust.com/repository/npm/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
+ integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
+
for-each@^0.3.3, for-each@^0.3.5:
version "0.3.5"
resolved "https://nexus-inner.51trust.com/repository/npm/for-each/-/for-each-0.3.5.tgz"
@@ -3954,6 +4046,17 @@ for-each@^0.3.3, for-each@^0.3.5:
dependencies:
is-callable "^1.2.7"
+form-data@^4.0.4:
+ version "4.0.4"
+ resolved "https://nexus-inner.51trust.com/repository/npm/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
+ integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ es-set-tostringtag "^2.1.0"
+ hasown "^2.0.2"
+ mime-types "^2.1.12"
+
fresh@0.5.2:
version "0.5.2"
resolved "https://nexus-inner.51trust.com/repository/npm/fresh/-/fresh-0.5.2.tgz"
@@ -3977,6 +4080,16 @@ fs-extra@^8.1.0:
jsonfile "^4.0.0"
universalify "^0.1.0"
+fs-extra@^9.0.0:
+ version "9.1.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
+ integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
+ dependencies:
+ at-least-node "^1.0.0"
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/fs.realpath/-/fs.realpath-1.0.0.tgz"
@@ -4130,7 +4243,7 @@ gopd@^1.0.1, gopd@^1.2.0:
resolved "https://nexus-inner.51trust.com/repository/npm/gopd/-/gopd-1.2.0.tgz"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
-graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://nexus-inner.51trust.com/repository/npm/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -4356,7 +4469,7 @@ internal-slot@^1.1.0:
hasown "^2.0.2"
side-channel "^1.1.0"
-invariant@^2.2.4:
+invariant@2.2.4, invariant@^2.2.4:
version "2.2.4"
resolved "https://nexus-inner.51trust.com/repository/npm/invariant/-/invariant-2.2.4.tgz"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@@ -5138,6 +5251,17 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://nexus-inner.51trust.com/repository/npm/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+json-stable-stringify@^1.0.2:
+ version "1.3.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz#8903cfac42ea1a0f97f35d63a4ce0518f0cc6a70"
+ integrity sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==
+ dependencies:
+ call-bind "^1.0.8"
+ call-bound "^1.0.4"
+ isarray "^2.0.5"
+ jsonify "^0.0.1"
+ object-keys "^1.1.1"
+
json5@^2.2.3:
version "2.2.3"
resolved "https://nexus-inner.51trust.com/repository/npm/json5/-/json5-2.2.3.tgz"
@@ -5150,6 +5274,20 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
+jsonfile@^6.0.1:
+ version "6.2.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/jsonfile/-/jsonfile-6.2.0.tgz#7c265bd1b65de6977478300087c99f1c84383f62"
+ integrity sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsonify@^0.0.1:
+ version "0.0.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978"
+ integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==
+
"jsx-ast-utils@^2.4.1 || ^3.0.0":
version "3.3.5"
resolved "https://nexus-inner.51trust.com/repository/npm/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz"
@@ -5167,6 +5305,13 @@ keyv@^4.5.3:
dependencies:
json-buffer "3.0.1"
+klaw-sync@^6.0.0:
+ version "6.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c"
+ integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==
+ dependencies:
+ graceful-fs "^4.1.11"
+
kleur@^3.0.3:
version "3.0.3"
resolved "https://nexus-inner.51trust.com/repository/npm/kleur/-/kleur-3.0.3.tgz"
@@ -5533,7 +5678,7 @@ metro@0.82.5, metro@^0.82.2:
ws "^7.5.10"
yargs "^17.6.2"
-micromatch@^4.0.4, micromatch@^4.0.8:
+micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.8:
version "4.0.8"
resolved "https://nexus-inner.51trust.com/repository/npm/micromatch/-/micromatch-4.0.8.tgz"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@@ -5551,7 +5696,7 @@ mime-db@1.52.0:
resolved "https://nexus-inner.51trust.com/repository/npm/mime-db/-/mime-db-1.54.0.tgz"
integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==
-mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://nexus-inner.51trust.com/repository/npm/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -5604,6 +5749,11 @@ minimist@1.2.0:
resolved "https://nexus-inner.51trust.com/repository/npm/minimist/-/minimist-1.2.0.tgz"
integrity sha512-7Wl+Jz+IGWuSdgsQEJ4JunV0si/iMhg42MnQQG6h1R6TNeVenp4U9x5CC5v/gYqz/fENLQITAWXidNtVL0NNbw==
+minimist@^1.2.6:
+ version "1.2.8"
+ resolved "https://nexus-inner.51trust.com/repository/npm/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+ integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
minipass@^4.2.4:
version "4.2.8"
resolved "https://nexus-inner.51trust.com/repository/npm/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a"
@@ -5827,7 +5977,7 @@ open@^6.2.0:
dependencies:
is-wsl "^1.1.0"
-open@^7.0.3:
+open@^7.0.3, open@^7.4.2:
version "7.4.2"
resolved "https://nexus-inner.51trust.com/repository/npm/open/-/open-7.4.2.tgz"
integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
@@ -5995,6 +6145,27 @@ parseurl@~1.3.3:
resolved "https://nexus-inner.51trust.com/repository/npm/parseurl/-/parseurl-1.3.3.tgz"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+patch-package@^8.0.0:
+ version "8.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61"
+ integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==
+ dependencies:
+ "@yarnpkg/lockfile" "^1.1.0"
+ chalk "^4.1.2"
+ ci-info "^3.7.0"
+ cross-spawn "^7.0.3"
+ find-yarn-workspace-root "^2.0.0"
+ fs-extra "^9.0.0"
+ json-stable-stringify "^1.0.2"
+ klaw-sync "^6.0.0"
+ minimist "^1.2.6"
+ open "^7.4.2"
+ rimraf "^2.6.3"
+ semver "^7.5.3"
+ slash "^2.0.0"
+ tmp "^0.0.33"
+ yaml "^2.2.2"
+
path-exists@^3.0.0:
version "3.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -6137,6 +6308,11 @@ prop-types@^15.5.8, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
+proxy-from-env@^1.1.0:
+ version "1.1.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+ integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
punycode@^2.1.0:
version "2.3.1"
resolved "https://nexus-inner.51trust.com/repository/npm/punycode/-/punycode-2.3.1.tgz"
@@ -6181,6 +6357,14 @@ queue@6.0.2:
dependencies:
inherits "~2.0.3"
+randexp@^0.5.3:
+ version "0.5.3"
+ resolved "https://nexus-inner.51trust.com/repository/npm/randexp/-/randexp-0.5.3.tgz#f31c2de3148b30bdeb84b7c3f59b0ebb9fec3738"
+ integrity sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==
+ dependencies:
+ drange "^1.0.2"
+ ret "^0.2.0"
+
range-parser@~1.2.1:
version "1.2.1"
resolved "https://nexus-inner.51trust.com/repository/npm/range-parser/-/range-parser-1.2.1.tgz"
@@ -6243,6 +6427,11 @@ react-native-device-info@^14.0.4:
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-device-info/-/react-native-device-info-14.0.4.tgz#56b24ace9ff29a66bdfc667209086421ed6cfdce"
integrity sha512-NX0wMAknSDBeFnEnSFQ8kkAcQrFHrG4Cl0mVjoD+0++iaKrOupiGpBXqs8xR0SeJyPC5zpdPl4h/SaBGly6UxA==
+react-native-exit-app@^2.0.0:
+ version "2.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/react-native-exit-app/-/react-native-exit-app-2.0.0.tgz#21d523580f0c1bf6793bc4e6d5771ba9347cd8e4"
+ integrity sha512-vr9e/8jgPcKCBw6qo0QLxfeMiTwExydghbYDqpLZYAGWR+6cbgnhvOxwdYj/JWR7ZtOALrRA4GMGSvU/ayxM7w==
+
react-native-fs@^2.20.0:
version "2.20.0"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-fs/-/react-native-fs-2.20.0.tgz#05a9362b473bfc0910772c0acbb73a78dbc810f6"
@@ -6260,6 +6449,11 @@ react-native-gesture-handler@^2.27.2:
hoist-non-react-statics "^3.3.0"
invariant "^2.2.4"
+react-native-linear-gradient@^2.8.3:
+ version "2.8.3"
+ resolved "https://nexus-inner.51trust.com/repository/npm/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f"
+ integrity sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA==
+
react-native-root-siblings@^5.0.1:
version "5.0.1"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-root-siblings/-/react-native-root-siblings-5.0.1.tgz#97e050e5155228f65810fb1c466ff8e769c5272c"
@@ -6290,6 +6484,14 @@ react-native-toast-message@^2.3.3:
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-toast-message/-/react-native-toast-message-2.3.3.tgz#e301508d386a9902ff6b4559ecc6674f8cfdf97a"
integrity sha512-4IIUHwUPvKHu4gjD0Vj2aGQzqPATiblL1ey8tOqsxOWRPGGu52iIbL8M/mCz4uyqecvPdIcMY38AfwRuUADfQQ==
+react-native-webview@^13.16.0:
+ version "13.16.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/react-native-webview/-/react-native-webview-13.16.0.tgz#c995148f944a7eaf12389f0e6d5c6f5e6a775686"
+ integrity sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==
+ dependencies:
+ escape-string-regexp "^4.0.0"
+ invariant "2.2.4"
+
react-native-zip-archive@^7.0.2:
version "7.0.2"
resolved "https://nexus-inner.51trust.com/repository/npm/react-native-zip-archive/-/react-native-zip-archive-7.0.2.tgz#f8c488b4f5ab1605bff1f366ac101fe0dce6fd1d"
@@ -6526,11 +6728,23 @@ restore-cursor@^3.1.0:
onetime "^5.1.0"
signal-exit "^3.0.2"
+ret@^0.2.0:
+ version "0.2.2"
+ resolved "https://nexus-inner.51trust.com/repository/npm/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c"
+ integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==
+
reusify@^1.0.4:
version "1.1.0"
resolved "https://nexus-inner.51trust.com/repository/npm/reusify/-/reusify-1.1.0.tgz"
integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==
+rimraf@^2.6.3:
+ version "2.7.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
rimraf@^3.0.2:
version "3.0.2"
resolved "https://nexus-inner.51trust.com/repository/npm/rimraf/-/rimraf-3.0.2.tgz"
@@ -6762,6 +6976,11 @@ sisteransi@^1.0.5:
resolved "https://nexus-inner.51trust.com/repository/npm/sisteransi/-/sisteransi-1.0.5.tgz"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+slash@^2.0.0:
+ version "2.0.0"
+ resolved "https://nexus-inner.51trust.com/repository/npm/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+ integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
slash@^3.0.0:
version "3.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/slash/-/slash-3.0.0.tgz"
@@ -7248,6 +7467,11 @@ universalify@^0.1.0:
resolved "https://nexus-inner.51trust.com/repository/npm/universalify/-/universalify-0.1.2.tgz"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+universalify@^2.0.0:
+ version "2.0.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
+ integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://nexus-inner.51trust.com/repository/npm/unpipe/-/unpipe-1.0.0.tgz"
@@ -7477,6 +7701,11 @@ yaml@^2.2.1:
resolved "https://nexus-inner.51trust.com/repository/npm/yaml/-/yaml-2.8.0.tgz"
integrity sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==
+yaml@^2.2.2:
+ version "2.8.1"
+ resolved "https://nexus-inner.51trust.com/repository/npm/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79"
+ integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==
+
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://nexus-inner.51trust.com/repository/npm/yargs-parser/-/yargs-parser-18.1.3.tgz"
@@ -7524,3 +7753,8 @@ yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://nexus-inner.51trust.com/repository/npm/yocto-queue/-/yocto-queue-0.1.0.tgz"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zod@^3.21.0:
+ version "3.25.76"
+ resolved "https://nexus-inner.51trust.com/repository/npm/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34"
+ integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==