From 02f1c9fd2c594323f772f8e8f0f2187a285c1749 Mon Sep 17 00:00:00 2001 From: admin <weikou2014> Date: 星期四, 31 三月 2022 15:08:26 +0800 Subject: [PATCH] service服务bug修复 --- src/main/resources/code/flutter_module/lib/utils/app_util.dart | 70 src/main/resources/code/flutter_module/lib/ui/widget/sos_ui.dart | 105 src/main/resources/code/flutter_module/assets/imgs/login/ic_login_logo.png | 0 src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterRecommendFragment.java | 37 src/main/resources/code/flutter_module/lib/utils/ad_util.dart | 92 src/main/resources/code/android/library-flutter/src/main/res/layout/activity_common_flutter.xml | 14 src/main/resources/code/flutter_module/lib/ui/mine/about_us.dart | 166 src/main/resources/code/flutter_module/lib/utils/des/des.dart | 415 + src/main/resources/code/flutter_module/lib/api/config_api.dart | 17 src/main/resources/code/flutter_module/assets/imgs/login/icon_email.png | 0 src/main/resources/code/flutter_module/assets/images/flutter-mark-square-64.png | 0 src/main/resources/code/flutter_module/lib/model/video/video_model.dart | 381 + src/main/resources/code/flutter_module/lib/ui/widget/refresh_listview.dart | 244 + src/main/resources/code/flutter_module/lib/ui/widget/base_ui.dart | 2 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_share.png | 0 src/main/resources/code/flutter_module/assets/imgs/login/icon_code.png | 0 src/main/resources/code/flutter_module/lib/model/video/video_play_url_model.dart | 53 src/main/resources/code/flutter_module/assets/imgs/icon.png | 0 src/main/resources/code/flutter_module/lib/ui/common/browser.dart | 132 src/main/resources/code/flutter_module/lib/home.dart | 418 + src/main/resources/code/flutter_module/lib/utils/des/number_utils.dart | 95 src/main/resources/code/flutter_module/lib/utils/global.dart | 28 src/main/resources/code/flutter_module/lib/utils/jump_page.dart | 40 src/main/resources/code/flutter_module/lib/utils/pageutils.dart | 95 src/main/resources/code/flutter_module/lib/api/user_api.dart | 196 src/main/resources/code/flutter_module/lib/ui/widget/dialog.dart | 415 + src/main/resources/code/flutter_module/assets/imgs/ic_portrait_default.png | 0 src/main/resources/code/flutter_module/lib/ui/video/video_list.dart | 166 src/main/resources/code/flutter_module/assets/imgs/video/ic_play_back.png | 0 src/main/resources/code/flutter_module/lib/mine.dart | 632 ++ src/main/resources/code/flutter_module/assets/imgs/home/icon_home_category_novel.png | 0 src/main/resources/code/flutter_module/lib/ui/search/search_result.dart | 286 + src/main/resources/code/flutter_module/lib/utils/des/crypto_util.dart | 34 src/main/resources/code/flutter_module/assets/imgs/common/ic_network_error.png | 0 src/main/resources/code/flutter_module/lib/utils/video/search_record_util.dart | 38 src/main/resources/code/flutter_module/assets/imgs/video/ic_pause.png | 0 src/main/resources/code/flutter_module/lib/model/video/home_type_model.dart | 221 src/main/resources/code/flutter_module/lib/ui/mine/settings.dart | 363 + src/main/resources/code/flutter_module/lib/utils/video/video_util.dart | 19 src/main/resources/code/flutter_module/lib/utils/cache_util.dart | 57 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_input.png | 0 src/main/resources/code/flutter_module/lib/utils/share_preference.dart | 86 src/main/resources/code/flutter_module/lib/model/video/watch_record_model.dart | 69 src/main/resources/code/flutter_module/lib/ui/demo_page.dart | 81 src/main/resources/code/flutter_module/lib/ui/mine/email_register.dart | 421 + src/main/resources/code/flutter_module/lib/ui/video/video_attention_list.dart | 228 src/main/resources/code/flutter_module/lib/model/video/video_detail_model.dart | 0 src/main/resources/code/flutter_module/lib/ui/mine/advice.dart | 153 src/main/resources/code/flutter_module/lib/ui/video/video_collected_list.dart | 296 + src/main/resources/code/flutter_module/lib/ui/widget/common_ui.dart | 35 src/main/resources/code/flutter_module/lib/ui/mine/email_pwd_find.dart | 346 + src/main/resources/code/flutter_module/assets/imgs/video/icon_share.png | 0 src/main/resources/code/flutter_module/lib/ui/widget/nav.dart | 129 src/main/resources/code/flutter_module/lib/utils/setting_util.dart | 46 src/main/resources/code/flutter_module/lib/utils/user_util.dart | 114 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_privacy.png | 0 src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_link.png | 0 src/main/resources/code/flutter_module/lib/api/video_api.dart | 309 + src/main/resources/code/flutter_module/lib/ui/main/home.dart | 539 ++ src/main/resources/code/flutter_module/lib/model/common/adinfo_model.dart | 29 src/main/resources/code/flutter_module/assets/imgs/common/ic_empty.png | 0 src/main/resources/code/flutter_module/lib/model/user/user_info.dart | 73 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_feedback.png | 0 src/main/resources/code/flutter_module/lib/utils/config_util.dart | 56 src/main/resources/code/flutter_module/lib/ui/video/video_download_list.dart | 302 + src/main/resources/code/android/library-ad/src/main/res/xml/gdt_file_path.xml | 9 src/main/resources/code/flutter_module/lib/api/feed_back_api.dart | 34 src/main/resources/code/flutter_module/lib/utils/ui_constant.dart | 40 src/main/resources/code/flutter_module/lib/ui/main.dart | 16 src/main/resources/code/flutter_module/lib/utils/event_bus_util.dart | 10 src/main/resources/code/flutter_module/assets/imgs/icon_search_home.png | 0 src/main/resources/code/flutter_module/lib/utils/jsinterface.dart | 250 + src/main/resources/code/flutter_module/lib/utils/sqlite_utils.dart | 69 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_settings.png | 0 src/main/resources/code/flutter_module/assets/imgs/video/icon_check_false.png | 0 src/main/resources/code/flutter_module/lib/utils/push_util.dart | 39 src/main/resources/code/android/library-flutter/src/main/res/xml/filepaths.xml | 6 src/main/resources/code/flutter_module/assets/imgs/icon_person_info_input.png | 0 src/main/resources/code/flutter_module/assets/imgs/video/ic_play.png | 0 src/main/resources/code/flutter_module/lib/ui/video/video_detail.dart | 935 +++ src/main/resources/code/android/library-ad/src/main/res/xml/file_paths.xml | 8 src/main/resources/code/flutter_module/assets/imgs/video/icon_check_true.png | 0 src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_out.png | 0 src/main/resources/code/flutter_module/lib/splash.dart | 10 src/main/resources/code/flutter_module/assets/imgs/common/icon_array_right.png | 0 src/main/resources/code/flutter_module/lib/utils/permission_util.dart | 150 src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_msg.png | 0 src/main/resources/code/flutter_module/lib/ui/widget/search_widget.dart | 313 + src/main/resources/code/flutter_module/lib/model/video/video_attention_model.dart | 49 src/main/resources/code/flutter_module/lib/ui/mine/email_login.dart | 381 + src/main/resources/code/flutter_module/assets/imgs/video/icon_collected.png | 0 src/main/resources/code/flutter_module/lib/ui/test.dart | 45 src/main/resources/code/flutter_module/lib/ui/mine/person_info.dart | 536 ++ src/main/resources/code/flutter_module/lib/ui/video/video_player_browser.dart | 321 + src/main/resources/code/flutter_module/lib/utils/string_util.dart | 19 src/main/resources/code/flutter_module/lib/ui/widget/button.dart | 138 src/main/resources/code/flutter_module/lib/ui/video/video_scan_record_list.dart | 321 + src/main/resources/code/flutter_module/lib/ui/widget/video_item.dart | 755 +++ src/main/resources/code/flutter_module/assets/imgs/login/icon_pwd.png | 0 src/main/resources/code/flutter_module/lib/utils/db_manager.dart | 99 src/main/resources/code/flutter_module/lib/utils/share_utils.dart | 40 src/main/resources/code/flutter_module/lib/model/video/search_special_model.dart | 36 src/main/resources/code/flutter_module/lib/model/common/http_model.dart | 16 src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_like.png | 0 src/main/resources/code/flutter_module/assets/imgs/video/icon_download.png | 0 src/main/resources/code/flutter_module/assets/imgs/common/icon_check_false.png | 0 src/main/resources/code/flutter_module/lib/ui/widget/capture.dart | 47 src/main/resources/code/flutter_module/lib/api/http.dart | 184 src/main/resources/code/flutter_module/assets/imgs/video/icon_uncollected.png | 0 src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterMineFragment.java | 35 src/main/resources/code/flutter_module/lib/model/video/home_ad_model.dart | 74 src/main/resources/code/flutter_module/lib/ui/mine/advice_submit.dart | 198 src/main/resources/code/flutter_module/lib/utils/ui_utils.dart | 107 src/main/resources/code/flutter_module/lib/utils/image_util.dart | 36 src/main/resources/code/flutter_module/lib/utils/des/padding.dart | 19 src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_about_us.png | 0 src/main/resources/code/flutter_module/lib/ui/search/search.dart | 369 + src/main/resources/code/flutter_module/lib/ui/widget/ad_express.dart | 149 src/main/resources/code/flutter_module/lib/utils/encrypt_util.dart | 10 src/main/resources/code/flutter_module/assets/imgs/common/icon_check_true.png | 0 src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_refresh.png | 0 src/main/resources/code/flutter_module/lib/ui/mine/login.dart | 496 ++ src/main/resources/code/flutter_module/assets/imgs/advice/ic_advice_top.png | 0 123 files changed, 14,512 insertions(+), 0 deletions(-) diff --git a/src/main/resources/code/android/library-ad/src/main/res/xml/file_paths.xml b/src/main/resources/code/android/library-ad/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..d40ca4b --- /dev/null +++ b/src/main/resources/code/android/library-ad/src/main/res/xml/file_paths.xml @@ -0,0 +1,8 @@ +<paths xmlns:android="http://schemas.android.com/apk/res/android"> + <!--涓轰簡閫傞厤鎵�鏈夎矾寰勫彲浠ヨ缃� path = "." --> + <external-path name="tt_external_root" path="." /> + <external-path name="tt_external_download" path="Download" /> + <external-files-path name="tt_external_files_download" path="Download" /> + <files-path name="tt_internal_file_download" path="Download" /> + <cache-path name="tt_internal_cache_download" path="Download" /> +</paths> \ No newline at end of file diff --git a/src/main/resources/code/android/library-ad/src/main/res/xml/gdt_file_path.xml b/src/main/resources/code/android/library-ad/src/main/res/xml/gdt_file_path.xml new file mode 100644 index 0000000..925d70e --- /dev/null +++ b/src/main/resources/code/android/library-ad/src/main/res/xml/gdt_file_path.xml @@ -0,0 +1,9 @@ +<paths> + <!-- 杩欎釜涓嬭浇璺緞涔熶笉鍙互淇敼锛屽繀椤讳负GDTDOWNLOAD --> + <external-cache-path + name="gdt_sdk_download_path1" + path="com_qq_e_download" /> + <cache-path + name="gdt_sdk_download_path2" + path="com_qq_e_download" /> +</paths> \ No newline at end of file diff --git a/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterMineFragment.java b/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterMineFragment.java new file mode 100644 index 0000000..4a919c7 --- /dev/null +++ b/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterMineFragment.java @@ -0,0 +1,35 @@ +package com.demo.library_flutter; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.idlefish.flutterboost.containers.FlutterBoostFragment; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class FlutterMineFragment extends FlutterBoostFragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return super.onCreateView( inflater, container, savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + } +} diff --git a/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterRecommendFragment.java b/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterRecommendFragment.java new file mode 100644 index 0000000..534a049 --- /dev/null +++ b/src/main/resources/code/android/library-flutter/src/main/java/com/demo/library_flutter/FlutterRecommendFragment.java @@ -0,0 +1,37 @@ +package com.demo.library_flutter; + +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.idlefish.flutterboost.containers.FlutterBoostFragment; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +public class FlutterRecommendFragment extends FlutterBoostFragment { + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return super.onCreateView( inflater, container, savedInstanceState); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + } +} diff --git a/src/main/resources/code/android/library-flutter/src/main/res/layout/activity_common_flutter.xml b/src/main/resources/code/android/library-flutter/src/main/res/layout/activity_common_flutter.xml new file mode 100644 index 0000000..0db6c0d --- /dev/null +++ b/src/main/resources/code/android/library-flutter/src/main/res/layout/activity_common_flutter.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".FlutterCommonActivity"> + + <FrameLayout + android:id="@+id/fl_container" + android:layout_width="match_parent" + android:layout_height="match_parent"></FrameLayout> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/src/main/resources/code/android/library-flutter/src/main/res/xml/filepaths.xml b/src/main/resources/code/android/library-flutter/src/main/res/xml/filepaths.xml new file mode 100644 index 0000000..37df1fa --- /dev/null +++ b/src/main/resources/code/android/library-flutter/src/main/res/xml/filepaths.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<paths> + <cache-path + name="files_path" + path="/"></cache-path> +</paths> \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/assets/images/flutter-mark-square-64.png b/src/main/resources/code/flutter_module/assets/images/flutter-mark-square-64.png new file mode 100644 index 0000000..56f22d5 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/images/flutter-mark-square-64.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/advice/ic_advice_top.png b/src/main/resources/code/flutter_module/assets/imgs/advice/ic_advice_top.png new file mode 100644 index 0000000..6aa23e3 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/advice/ic_advice_top.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_like.png b/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_like.png new file mode 100644 index 0000000..8921e40 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_like.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_msg.png b/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_msg.png new file mode 100644 index 0000000..1201efa --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/advice/icon_advice_msg.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/ic_empty.png b/src/main/resources/code/flutter_module/assets/imgs/common/ic_empty.png new file mode 100644 index 0000000..f9407c6 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/ic_empty.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/ic_network_error.png b/src/main/resources/code/flutter_module/assets/imgs/common/ic_network_error.png new file mode 100644 index 0000000..65d420e --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/ic_network_error.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_array_right.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_array_right.png new file mode 100644 index 0000000..69df631 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_array_right.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_link.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_link.png new file mode 100644 index 0000000..09f7059 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_link.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_out.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_out.png new file mode 100644 index 0000000..4374080 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_out.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_refresh.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_refresh.png new file mode 100644 index 0000000..796ca89 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_browser_refresh.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_false.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_false.png new file mode 100644 index 0000000..742e270 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_false.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_true.png b/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_true.png new file mode 100644 index 0000000..12a1d16 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/common/icon_check_true.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/home/icon_home_category_novel.png b/src/main/resources/code/flutter_module/assets/imgs/home/icon_home_category_novel.png new file mode 100644 index 0000000..08e2411 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/home/icon_home_category_novel.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/ic_portrait_default.png b/src/main/resources/code/flutter_module/assets/imgs/ic_portrait_default.png new file mode 100644 index 0000000..bb8162b --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/ic_portrait_default.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon.png b/src/main/resources/code/flutter_module/assets/imgs/icon.png new file mode 100644 index 0000000..3826234 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_about_us.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_about_us.png new file mode 100644 index 0000000..a8df430 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_about_us.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_feedback.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_feedback.png new file mode 100644 index 0000000..94f04b3 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_feedback.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_input.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_input.png new file mode 100644 index 0000000..d50439d --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_input.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_privacy.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_privacy.png new file mode 100644 index 0000000..8e7c3b7 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_privacy.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_settings.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_settings.png new file mode 100644 index 0000000..c1165e7 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_settings.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_share.png b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_share.png new file mode 100644 index 0000000..8c2161f --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_mine_fun_share.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_person_info_input.png b/src/main/resources/code/flutter_module/assets/imgs/icon_person_info_input.png new file mode 100644 index 0000000..69df631 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_person_info_input.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/icon_search_home.png b/src/main/resources/code/flutter_module/assets/imgs/icon_search_home.png new file mode 100644 index 0000000..ca629f4 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/icon_search_home.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/login/ic_login_logo.png b/src/main/resources/code/flutter_module/assets/imgs/login/ic_login_logo.png new file mode 100644 index 0000000..44e9fda --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/login/ic_login_logo.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/login/icon_code.png b/src/main/resources/code/flutter_module/assets/imgs/login/icon_code.png new file mode 100644 index 0000000..c5467d2 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/login/icon_code.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/login/icon_email.png b/src/main/resources/code/flutter_module/assets/imgs/login/icon_email.png new file mode 100644 index 0000000..147eec0 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/login/icon_email.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/login/icon_pwd.png b/src/main/resources/code/flutter_module/assets/imgs/login/icon_pwd.png new file mode 100644 index 0000000..7b1379e --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/login/icon_pwd.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/ic_pause.png b/src/main/resources/code/flutter_module/assets/imgs/video/ic_pause.png new file mode 100644 index 0000000..40b3c20 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/ic_pause.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/ic_play.png b/src/main/resources/code/flutter_module/assets/imgs/video/ic_play.png new file mode 100644 index 0000000..9ace965 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/ic_play.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/ic_play_back.png b/src/main/resources/code/flutter_module/assets/imgs/video/ic_play_back.png new file mode 100644 index 0000000..b08d530 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/ic_play_back.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_false.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_false.png new file mode 100644 index 0000000..fea9753 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_false.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_true.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_true.png new file mode 100644 index 0000000..e267bd1 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_check_true.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_collected.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_collected.png new file mode 100644 index 0000000..863aab1 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_collected.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_download.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_download.png new file mode 100644 index 0000000..2c93b41 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_download.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_share.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_share.png new file mode 100644 index 0000000..61a5389 --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_share.png Binary files differ diff --git a/src/main/resources/code/flutter_module/assets/imgs/video/icon_uncollected.png b/src/main/resources/code/flutter_module/assets/imgs/video/icon_uncollected.png new file mode 100644 index 0000000..9ebfa8f --- /dev/null +++ b/src/main/resources/code/flutter_module/assets/imgs/video/icon_uncollected.png Binary files differ diff --git a/src/main/resources/code/flutter_module/lib/api/config_api.dart b/src/main/resources/code/flutter_module/lib/api/config_api.dart new file mode 100644 index 0000000..ab71f80 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/api/config_api.dart @@ -0,0 +1,17 @@ + +import 'package:flutter/material.dart'; + +import 'http.dart'; + +class ConfigApiUtil { + ///鏌ラ槄鍚戞垜姹傛晳鐨凷OS + static Future<Map<String, dynamic>?> getConfig(BuildContext context) async { + var result = await HttpUtil.baseRequest( + context, "/api/config/getConfig", {}, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/api/feed_back_api.dart b/src/main/resources/code/flutter_module/lib/api/feed_back_api.dart new file mode 100644 index 0000000..865f264 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/api/feed_back_api.dart @@ -0,0 +1,34 @@ + +import 'package:flutter/material.dart'; +import '../../utils/user_util.dart'; + +import 'http.dart'; + +class FeedBackApiUtil { + ///鏌ラ槄鍚戞垜姹傛晳鐨凷OS + static Future<Map<String, dynamic>?> advice( + BuildContext context, String? type, String content) async { + var uid = await UserUtil.getUid(); + Map<String, dynamic> params = {}; + if (uid != null) { + params["uid"] = uid.toString(); + } + + if (type != null) { + params["type"] = type; + } + + params["content"] = content; + var result = await HttpUtil.baseRequest( + context, "/api/v2/help/advice", params, () { + showLoading(context); + }, notifyError: true); + + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } +} + diff --git a/src/main/resources/code/flutter_module/lib/api/http.dart b/src/main/resources/code/flutter_module/lib/api/http.dart new file mode 100644 index 0000000..9c48d14 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/api/http.dart @@ -0,0 +1,184 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:device_info/device_info.dart'; +import 'package:dio/adapter.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../utils/des/des.dart'; +import '../../utils/share_preference.dart'; + +import '../model/common/http_model.dart'; +import '../ui/widget/dialog.dart'; +import '../utils/app_util.dart'; +import '../utils/encrypt_util.dart'; +import '../utils/global.dart'; +import '../utils/ui_utils.dart'; + +typedef OnHttpRequestFinish = void Function(HttpRequestResult result); + +typedef OnHttpRequestStart = void Function(); + +showLoading(BuildContext context) { + //鍏堜涪澶辩劍鐐� + FocusScope.of(context).unfocus(); + //寮�鍚姞杞芥 + DialogUtil.showDialog(context, LoadingDialog("")); +} + +dismissDialog(BuildContext context) { + Navigator.pop(context); +} + +class HttpUtil { + static AndroidDeviceInfo? _androidInfo; + static IosDeviceInfo? _iosInfo; + + static _getSign(Map<String, dynamic> params) { + List list = []; + //绛惧悕 + params.forEach((key, value) { + list.add("$key=$value"); + }); + //鎺掑簭 + list.sort(); + String signStr = ""; + list.forEach((element) { + signStr += element + "&"; + }); + + if (signStr.endsWith("&")) { + signStr = signStr.substring(0, signStr.length - 1); + } + + signStr += "8888B&*@-uWan88/',@@^"; + + return EncryptUtil.MD5(signStr); + } + + static String _decode(String content) { + String key = utf8.decode(base64Decode("VW1nT3R2WVk=")); + String iv = utf8.decode(base64Decode("WXlIeEhsY0o=")); + + List<int> result = + DES().decryptWithCBC(base64Decode(content), key.codeUnits, iv: iv); + + return utf8.decode(result); + } + + static Future<Map<String, dynamic>> getBaseParams( + Map<String, dynamic>? params) async { + params ??= {}; + + if (Platform.isIOS) { + String finalParams = + await dataMethodChannel.invokeMethod("getBaseRequestParams", params); + return jsonDecode(finalParams); + } + if (Platform.isAndroid) { + if (_androidInfo == null) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + _androidInfo = await deviceInfo.androidInfo; + } + print("androidInfo锛�${_androidInfo!.version}"); + params["Version"] = (await AppUtil.getVersionCode()).toString(); + } else if (Platform.isIOS) { + if (_iosInfo == null) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + _iosInfo = await deviceInfo.iosInfo; + } + params["Version"] = "105"; + } + + //娣诲姞闄勫姞鍙傛暟 + params["Timestamp"] = DateTime.now().millisecondsSinceEpoch.toString(); + params["Platform"] = "Android"; // Platform.isAndroid ? "Android" : "IOS"; + params["Package"] = "com.hanju.video"; + params["System"] = "1"; + Global.utdId = "testtest"; + Global.channel = "QQ"; + if (Platform.isAndroid) { + if (Global.utdId != null) { + params["UtdId"] = Global.utdId; + params["Device"] = Global.utdId; + } + params["osVersion"] = _androidInfo!.version.release; + } else if (Platform.isIOS) { + params["Device"] = "test123123"; //_iosInfo!.identifierForVendor; + } + + if (Global.channel != null) { + params["Channel"] = Global.channel; + } + + //闈掑皯骞存ā寮� + params["YouthMode"] = false; + + params["Sign"] = _getSign(params); + + return params; + } + + static Future<HttpRequestResult> baseRequest(BuildContext context, String api, + Map<String, dynamic> params, OnHttpRequestStart? onStart, + {bool notifyError = false}) async { + // params ??= {}; + params = await getBaseParams(params); + + if (onStart != null) { + onStart(); + } + HttpRequestResult requestResult; + + try { + var dio = Dio() + ..options = BaseOptions( + baseUrl: "http://api.hanju.goxcw.com:8089/BuWan", + connectTimeout: 20000, + receiveTimeout: 1000 * 60, + contentType: "application/x-www-form-urlencoded"); + //璁剧疆浠g悊 + (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = + (HttpClient client) { + client.findProxy = (uri) { + return 'PROXY 192.168.3.122:8888'; + }; + client.badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + }; + + // FormData formData = FormData.fromMap(params); + var response = await dio.post( + api, + data: params, + onSendProgress: (int sent, int total) { + print('$sent $total'); + }, + ); + if (response.statusCode == HttpStatus.ok) { + String result = response.data.toString(); + result = _decode(result); + print("缃戠粶璇锋眰缁撴灉:$result"); + requestResult = HttpRequestResult(true, jsonDecode(result)); + } else { + requestResult = HttpRequestResult(false, null, msg: "缃戠粶璇锋眰澶辫触"); + } + } on DioError catch (_) { + if (_.type == DioErrorType.connectTimeout || + _.type == DioErrorType.receiveTimeout || + _.type == DioErrorType.sendTimeout) { + requestResult = HttpRequestResult(false, null, msg: "缃戠粶璇锋眰瓒呮椂"); + } else { + requestResult = HttpRequestResult(false, null, msg: "缃戠粶璇锋眰鍑洪敊"); + } + } catch (e) { + requestResult = HttpRequestResult(false, null, msg: "缃戠粶璇锋眰鍑洪敊"); + } + if (notifyError && !requestResult.success) { + ToastUtil.toast(requestResult.msg!, context); + } + return requestResult; + } +} diff --git a/src/main/resources/code/flutter_module/lib/api/user_api.dart b/src/main/resources/code/flutter_module/lib/api/user_api.dart new file mode 100644 index 0000000..5fb74c4 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/api/user_api.dart @@ -0,0 +1,196 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import '../../utils/string_util.dart'; +import '../../utils/user_util.dart'; + +import 'http.dart'; +import 'dart:io'; + +class UserApiUtil { + ///楠岃瘉鐮佸彂閫� + static Future<Map<String, dynamic>?> sendSMS( + BuildContext context, String phone) async { + var result = await HttpUtil.baseRequest( + context, "/api/v1/sms/sendSMS", {"phone": phone}, () { + showLoading(context); + }); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + ///鍙戦�侀偖绠遍獙璇佺爜 + static Future<Map<String, dynamic>?> sendEmailCode( + BuildContext context, String email) async { + var result = await HttpUtil.baseRequest( + context, "/api/user/sendVerifyCode", {"Email": email}, () { + showLoading(context); + }); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> uploadPushRegId( + BuildContext context, String regId) async { + var uid = await UserUtil.getUid(); + var params = {"regId": regId}; + if (uid != null) { + params["uid"] = uid.toString(); + } + + var result = await HttpUtil.baseRequest( + context, "/api/v1/user/uploadPushRegId", params, () {}); + if (result.success) { + return result.data; + } + return null; + } + + ///鐧诲綍 + static Future<Map<String, dynamic>?> loginByPhone( + BuildContext context, String phone, String vcode, String token) async { + Map<String, dynamic> params = {}; + params["phone"] = phone; + if (!StringUtil.isNullOrEmpty(vcode)) { + params["vcode"] = vcode; + } + + if (!StringUtil.isNullOrEmpty(token)) { + params["token"] = token; + } + + var result = await HttpUtil.baseRequest( + context, "/api/v1/user/loginPhone", params, () { + showLoading(context); + }, notifyError: true); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> loginByEmail( + BuildContext context, String email, String pwd) async { + Map<String, dynamic> params = {}; + params["Email"] = email; + params["Pwd"] = pwd; + + var result = + await HttpUtil.baseRequest(context, "/api/user/login", params, () { + showLoading(context); + }, notifyError: true); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> registerByEmail( + BuildContext context, String email, String pwd, String code) async { + Map<String, dynamic> params = {}; + params["Email"] = email; + params["Pwd"] = pwd; + params["VerifyCode"] = code; + var result = + await HttpUtil.baseRequest(context, "/api/user/register", params, () { + showLoading(context); + }, notifyError: true); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> setPwd( + BuildContext context, String email, String pwd, String code) async { + Map<String, dynamic> params = {}; + params["Email"] = email; + params["Pwd"] = pwd; + params["VerifyCode"] = code; + var result = + await HttpUtil.baseRequest(context, "/api/user/setPwd", params, () { + showLoading(context); + }, notifyError: true); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + ///鑾峰彇鐢ㄦ埛淇℃伅 + static Future<Map<String, dynamic>?> getUserInfo( + BuildContext context, String uid) async { + Map<String, dynamic> params = {}; + params["LoginUid"] = uid.toString(); + var result = await HttpUtil.baseRequest( + context, "/api/user/getLoginUserInfo", params, () {}); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> updateUserInfo(BuildContext context, + {String? nickName, + String? portraitPath, + int? sex, + String? sign, + String? birthDay}) async { + Map<String, dynamic> params = {}; + params["LoginUid"] = await UserUtil.getUid(); + if (nickName != null) { + params["NickName"] = nickName; + } + + if (portraitPath != null) { + List<int> bytes =await File(portraitPath).readAsBytes(); + String bs64 = base64Encode(bytes); + params["Portrait"] = bs64; + } + + if (sex != null) { + params["Sex"] = sex.toString(); + } + + if (sign != null) { + params["PersonalSign"] = sign; + } + + if (birthDay != null) { + params["BirthDay"] = birthDay; + } + + var result = await HttpUtil.baseRequest( + context, "/api/user/updateLoginUserInfo", params, () { + showLoading(context); + }); + dismissDialog(context); + if (result.success) { + return result.data; + } + return null; + } + + ///閫�鍑虹櫥褰� + static Future<Map<String, dynamic>?> logout( + BuildContext context, String uid) async { + Map<String, dynamic> params = {}; + params["uid"] = uid.toString(); + var result = + await HttpUtil.baseRequest(context, "/api/user/logout", params, () {}); + if (result.success) { + return result.data; + } + return null; + } +} diff --git a/src/main/resources/code/flutter_module/lib/api/video_api.dart b/src/main/resources/code/flutter_module/lib/api/video_api.dart new file mode 100644 index 0000000..c10a19c --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/api/video_api.dart @@ -0,0 +1,309 @@ +import 'package:flutter/material.dart'; +import '../../utils/user_util.dart'; + +import 'http.dart'; + +class HomeApiUtil { + static Future<Map<String, dynamic>?> getHomeAd( + BuildContext context, String dataKey, String vtid) async { + Map<String, dynamic> params = {}; + params["DataKey"] = dataKey; + params["Vtid"] = vtid; + params["Method"] = "getHomeAd"; + + var result = await HttpUtil.baseRequest( + context, "/api/v2/recommend", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> getHomeTypes( + BuildContext context, String dataKey, String vtid, int page) async { + Map<String, dynamic> params = {}; + params["DataKey"] = dataKey; + params["Vtid"] = vtid; + params["Method"] = "getHomeTypeNew"; + params["Page"] = "$page"; + params["PageSize"] = "5"; + + var result = await HttpUtil.baseRequest( + context, "/api/v2/recommend", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> getRecommendSearchSpecial( + BuildContext context) async { + Map<String, dynamic> params = {}; + params["Method"] = "getRecommendSearchSpecial"; + + var result = await HttpUtil.baseRequest( + context, "/api/v2/recommend", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } +} + +class SearchApiUtil { + static Future<Map<String, dynamic>?> getHotSearch( + BuildContext context) async { + Map<String, dynamic> params = {}; + var result = await HttpUtil.baseRequest( + context, "/api/user/getHotSearch", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> getSuggestSearch( + BuildContext context, String key) async { + Map<String, dynamic> params = {}; + params["Key"] = key; + var result = await HttpUtil.baseRequest( + context, "/api/user/suggestSearch", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> search( + BuildContext context, String key, int page, int? type) async { + Map<String, dynamic> params = {}; + params["Key"] = key; + params["Page"] = "$page"; + type ??= 0; + params["Type"] = "$type"; + + var result = await HttpUtil.baseRequest( + context, "/api/user/searchNew", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } +} + +class VideoApiUtil { + //鑾峰彇鎼滅储涓撻瑙嗛鍒楄〃 + static Future<Map<String, dynamic>?> getSearchSpecialVideoList( + BuildContext context, String key, int page) async { + Map<String, dynamic> params = {}; + params["Method"] = "getSpecialVideo"; + params["key"] = key; + params["page"] = "$page"; + var result = await HttpUtil.baseRequest( + context, "/api/v2/search", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鑾峰彇璇︽儏 + static Future<Map<String, dynamic>?> getVideoDetail( + BuildContext context, String videoId, String? resourceId) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["Type"] = "0"; + params["Uid"] = "1"; + params["ResourceId"] = resourceId ?? ""; + + if (await UserUtil.isLogin()) { + params["LoginUid"] = await UserUtil.getUid(); + } + + var result = await HttpUtil.baseRequest( + context, "/api/recommend/getVideoDetailV2", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鑾峰彇鍓ч泦鍒楄〃 + static Future<Map<String, dynamic>?> getVideoEpisodeList( + BuildContext context, String videoId, String? resourceId, int page, + {int pageSize = 100}) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["PageSize"] = "$pageSize"; + params["Page"] = "$page"; + params["Method"] = "getVideoEpisodeList"; + params["ResourceId"] = resourceId ?? ""; + var result = await HttpUtil.baseRequest( + context, "/api/v2/recommend", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鑾峰彇鎾斁鍒楄〃 + static Future<Map<String, dynamic>?> getPlayUrl(BuildContext context, + String videoId, String id, String resourceId) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["Id"] = id; + params["ResourceId"] = resourceId; + var result = await HttpUtil.baseRequest( + context, "/api/recommend/getPlayUrl", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鐩稿叧鎺ㄨ崘 + static Future<Map<String, dynamic>?> getRelativeVideos( + BuildContext context, String videoId) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + var result = await HttpUtil.baseRequest( + context, "/api/user/getRelativeVideos", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鏄惁鏀惰棌 + static Future<Map<String, dynamic>?> isCollelctedVideo( + BuildContext context, String videoId) async { + Map<String, dynamic> params = {}; + params["Id"] = videoId; + params["LoginUid"] = await UserUtil.getUid(); + params["ThirdType"] = "0"; + var result = await HttpUtil.baseRequest( + context, "/api/recommend/isCollect", params, () {}, + notifyError: true); + + if (result.success) { + return result.data; + } + return null; + } + + //鏀惰棌/鍙栨秷鏀惰棌 瑙嗛 + static Future<Map<String, dynamic>?> collelctVideo( + BuildContext context, String videoId, bool collected) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["LoginUid"] = await UserUtil.getUid(); + + var result; + if (collected) { + result = await HttpUtil.baseRequest( + context, "/api/uservideo/collectVideo", params, () { + showLoading(context); + }, notifyError: true); + } else { + result = await HttpUtil.baseRequest( + context, "/api/uservideo/cancelCollectVideo", params, () { + showLoading(context); + }, notifyError: true); + } + dismissDialog(context); + + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> getCollelctedVideos( + BuildContext context, int page) async { + Map<String, dynamic> params = {}; + params["Page"] = "$page"; + params["LoginUid"] = await UserUtil.getUid(); + + var result = await HttpUtil.baseRequest( + context, "/api/uservideo/getCollectVideoList", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + static Future<Map<String, dynamic>?> getUserVideoCount( + BuildContext context) async { + Map<String, dynamic> params = {}; + params["LoginUid"] = await UserUtil.getUid(); + params["Method"] = "getUserVideoDataCount"; + + var result = await HttpUtil.baseRequest( + context, "/api/v2/userVideo", params, () {}, + notifyError: true); + if (result.success) { + return result.data; + } + return null; + } + + //鍏虫敞 + static Future<Map<String, dynamic>?> addAttentionVideo( + BuildContext context, String videoId) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["LoginUid"] = await UserUtil.getUid(); + var result = await HttpUtil.baseRequest( + context, "/api/attention/addAttention", params, () {}, + notifyError: true); + + if (result.success) { + return result.data; + } + return null; + } + + //鍙栨秷鍏虫敞 + static Future<Map<String, dynamic>?> cancelAttentionVideo( + BuildContext context, String videoId) async { + Map<String, dynamic> params = {}; + params["VideoId"] = videoId; + params["LoginUid"] = await UserUtil.getUid(); + var result = await HttpUtil.baseRequest( + context, "/api/attention/cancelAttention", params, () {}, + notifyError: true); + + if (result.success) { + return result.data; + } + return null; + } + + //鑾峰彇鍏虫敞鍒楄〃 + static Future<Map<String, dynamic>?> getAttentionVideoList( + BuildContext context, int page) async { + Map<String, dynamic> params = {}; + params["Page"] = page; + params["LoginUid"] = await UserUtil.getUid(); + var result = await HttpUtil.baseRequest( + context, "/api/attention/getAttentionList", params, () {}, + notifyError: true); + + if (result.success) { + return result.data; + } + return null; + } +} diff --git a/src/main/resources/code/flutter_module/lib/home.dart b/src/main/resources/code/flutter_module/lib/home.dart new file mode 100644 index 0000000..69feb7e --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/home.dart @@ -0,0 +1,418 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_module/utils/ui_utils.dart'; +import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; +import '../../ui/widget/ad_express.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../utils/ad_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/ui_constant.dart'; + +import '../../model/video/home_type_model.dart'; +import '../../model/video/search_special_model.dart'; +import 'api/video_api.dart'; +import 'model/video/home_ad_model.dart'; +import 'ui/widget/video_item.dart'; +import 'utils/jump_page.dart'; + +class HomePage extends StatefulWidget { + HomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State<HomePage> + with SingleTickerProviderStateMixin { + final MyRefreshController _refreshController = + MyRefreshController(initialRefresh: false); + List<HomeAdModel> bannerList = []; + List homeTypeList = []; + int page = 1; + int totalCount = 0; + List<SearchSpecialModel> categoryList = []; + bool hasDYHot = true; + String searchKw = ""; + + //淇℃伅娴佸箍鍛� + Widget? expressAd; + AdType? expressAdType; + String? expressPid; + + ExpressAdController _expressAdController = ExpressAdController(); + + @override + void initState() { + super.initState(); + init(); + AdUtil.getAdType(AdPosition.other).then((value) { + setState(() { + expressAdType = value; + }); + if (expressAdType == AdType.csj) { + expressPid = CSJADConstant.PID_RECOMMEND_BIG_PICTURE; + } else if (expressAdType == AdType.gdt) { + expressPid = GDTADConstant.PID_RECOMMEND_BIG_PICTURE; + } + }); + + // Future.delayed(Duration(seconds: 5),(){ + uiMethodChannel.invokeMethod("setStatusBarLight"); + // }); + } + + void init() { + getHomeAd(); + getHomeType(page); + getSearchSpecial(); + getHotSearch(); + //鍒锋柊 + _expressAdController.refresh; + } + + void jumpPage(String name, dynamic params) { + JumpPageUtil.jumpPage(name, context, + params: params, native: true, callback: (data) {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column(children: [ + Container( + color: ColorConstant.theme, + padding: const EdgeInsets.fromLTRB(10, 12, 10, 12), + child: InkWell( + onTap: () { + jumpPage("SearchPage", {"title": searchKw}); + }, + child: Container( + height: 32, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16)), + child: Row( + children: [ + Container( + width: 12, + ), + Image.asset( + "assets/imgs/icon_search_home.png", + height: 17, + ), + Container( + width: 10, + ), + Text( + searchKw, + style: TextStyle( + color: const Color(0xFF787878), fontSize: 14), + ) + ], + ), + )), + ), + Expanded( + child: RefreshListView( + refreshController: _refreshController, + content: CustomScrollView(slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (content, index) { + if (index == 0) { + return bannerList.isEmpty ? Container() : getBannerView(); + } else if (index == 1) { + return categoryList.isEmpty + ? Container() + : getCategoryView(); + } else if (index == 2) { + return getAdView(); + } else { + return getHomeTypeView(index - 3, context); + } + }, + childCount: homeTypeList.length + 3, + )) + ]), + refresh: () { + page = 1; + init(); + }, + loadMore: () { + getHomeType(page + 1); + }, + )) + ])); + } + + void getHomeAd() async { + Map<String, dynamic>? result = + await HomeApiUtil.getHomeAd(context, "recommend", "1628826741158"); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + List<HomeAdModel> adList = []; + for (var element in list) { + adList.add(HomeAdModel.fromJson(element)); + } + setState(() { + bannerList = adList; + }); + } + } + + void getHomeType(int _page) async { + page = _page; + Map<String, dynamic>? result = await HomeApiUtil.getHomeTypes( + context, "recommend", "1628826741158", _page); + _refreshController.refreshCompleted(resetFooterState: true); + //璇锋眰澶辫触浜� + if (result == null) { + if (page > 1) { + page = page - 1; + } + if (homeTypeList.isEmpty) { + _refreshController.apiError!(); + } + + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + totalCount = int.parse(result["Data"]["count"]); + List<HomeTypeModel> tempHomeTypeList = []; + for (var element in list) { + tempHomeTypeList.add(HomeTypeModel.fromJson(element)); + } + setState(() { + if (_page == 1) { + homeTypeList = tempHomeTypeList; + } else { + if (tempHomeTypeList.isNotEmpty) { + homeTypeList.addAll(tempHomeTypeList); + } + } + }); + + if (totalCount <= homeTypeList.length) { + _refreshController.loadNoData(); + } else { + if (_page > 1) { + _refreshController.loadComplete(); + } + } + if (homeTypeList.isEmpty) { + _refreshController.dataEmpty!(); + } else { + //姝e父鐨勭姸鎬� + _refreshController.dataNormal!(); + } + } + } + + void getSearchSpecial() async { + Map<String, dynamic>? result = + await HomeApiUtil.getRecommendSearchSpecial(context); + _refreshController.refreshCompleted(resetFooterState: true); + //璇锋眰澶辫触浜� + if (result == null) { + if (page > 1) { + page = page - 1; + } + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]; + List<SearchSpecialModel> tempList = []; + for (var element in list) { + tempList.add(SearchSpecialModel.fromJson(element)); + } + // tempList.add(SearchSpecialModel( + // id: "novel", + // icon: "assets/imgs/home/icon_home_category_novel.png", + // name: "灏忚")); + setState(() { + categoryList = tempList; + }); + } + } + + void getHotSearch() async { + Map<String, dynamic>? result = await SearchApiUtil.getHotSearch(context); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + setState(() { + searchKw = list[Random().nextInt(list.length)]; + }); + } + } + + Widget getBannerView() { + double width = MediaQuery.of(context).size.width; + double itemWidth = width - 40; + double itemHeight = itemWidth * 0.4382; + CustomLayoutOption customLayoutOption = + CustomLayoutOption(startIndex: -1, stateCount: 3); + customLayoutOption.addTranslate([ + Offset(-(width - 65), 0), + const Offset(0.0, 0.0), + Offset(width - 65, 0) + ]); + customLayoutOption.addScale([0.8, 1, 0.8], Alignment.center); + + return KeepAliveWrapper( + child: bannerList.isNotEmpty + ? Container( + padding: EdgeInsets.only(top: 10), + child: SizedBox( + width: width, + height: itemHeight, + child: Swiper( + itemBuilder: (BuildContext context, int index) { + return ClipRRect( + child: VideoImage( + bannerList[index].picture, + ), + borderRadius: BorderRadius.circular(8), + ); + }, + layout: SwiperLayout.DEFAULT, + customLayoutOption: customLayoutOption, + indicatorLayout: PageIndicatorLayout.COLOR, + autoplay: true, + duration: 500, + itemCount: bannerList.length, + pagination: const SwiperPagination( + margin: EdgeInsets.all(10), + builder: DotSwiperPaginationBuilder( + size: 8, + activeColor: ColorConstant.theme, + color: Colors.grey)), + itemWidth: itemWidth, + outer: false, + scale: 0.86, + viewportFraction: 0.86, + // containerHeight: itemHeight-30, + // containerWidth:width, + itemHeight: itemHeight, + onTap: (index) { + print("banner鐐瑰嚮锛�$index"); + if (bannerList[index].linkType == 1) { + jumpPage("VideoDetailPage", { + "video": bannerList[index].video!.toJson(), + }); + } else if (bannerList[index].linkType == 2) { + dynamic json = jsonDecode(bannerList[index].params!); + String url = json["url"]; + jumpPage("BrowserPage", { + "url": url, + }); + } + }, + )), + ) + : Container()); + } + + Widget getCategoryView() { + return categoryList.isNotEmpty + ? Container( + padding: const EdgeInsets.fromLTRB(10, 20, 10, 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: categoryList + .map((e) => InkWell( + onTap: () { + jumpPage( + "VideoListPage", {"kw": e.id!, "title": e.name}); + }, + child: Column(children: [ + CommonImage( + e.icon!, + width: MediaQuery.of(context).size.width * 55 / 375, + ), + Text( + e.name!, + style: const TextStyle( + color: Color(0xFF3B3B3B), fontSize: 12), + ) + ]))) + .toList())) + : Container(); + } + + Widget getAdView() { + return expressAdType != null + ? Container( + padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // const Text( + // "浠婃棩鐑偣", + // style: TextStyle(color: Color(0xFF5F5F5F), fontSize: 16), + // ), + SizedBox( + height: (MediaQuery.of(context).size.width - 20) * 0.75, + width: MediaQuery.of(context).size.width, + child: _nativeView(), + ) + ], + ), + ) + : Container(); + } + + Widget _nativeView() { + return expressAdType == AdType.csj + ? CSJEXpressAd( + expressPid!, + MediaQuery.of(context).size.width - 20, + (MediaQuery.of(context).size.width - 20) * 0.8, + controller: _expressAdController, + close: () { + setState(() { + expressAdType = null; + }); + }, + loadFail: () { + setState(() { + expressAdType = null; + }); + }, + ) + : GDTEXpressAd( + expressPid!, + MediaQuery.of(context).size.width - 20, + (MediaQuery.of(context).size.width - 20) * 0.8, + controller: _expressAdController, + close: () { + setState(() { + expressAdType = null; + }); + }, + loadFail: () { + setState(() { + expressAdType = null; + }); + }, + ); + } + + Widget getHomeTypeView(index, BuildContext context) { + HomeTypeModel homeType = homeTypeList[index]; + double mx = MediaQuery.of(context).size.width; + return VideoListUIUtil.getHomeTypeItem(mx, homeType, context); + } +} diff --git a/src/main/resources/code/flutter_module/lib/mine.dart b/src/main/resources/code/flutter_module/lib/mine.dart new file mode 100644 index 0000000..9c826d9 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/mine.dart @@ -0,0 +1,632 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_boost/flutter_boost.dart'; +import '../../api/video_api.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/db_manager.dart'; +import '../../utils/jump_page.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/user_util.dart'; + +import 'model/user/user_info.dart'; +import 'model/video/watch_record_model.dart'; +import 'utils/share_preference.dart'; +import 'package:share_plus/share_plus.dart'; + +class MinePage extends StatefulWidget { + MinePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _MinePageState createState() => _MinePageState(); +} + +class _MinePageState extends State<MinePage> + with SingleTickerProviderStateMixin, PageVisibilityObserver { + int index = 1; + + UserInfo? _user; + List<WatchRecordModel> recordList = []; + + int collectedVideoCount = 0; + int favoriteVideoCount = 0; + String applink = ""; + + @override + void initState() { + super.initState(); + if (Platform.isIOS) { + MySharedPreferences.getInstance().getString("appLink").then((value) { + setState(() { + applink = value!; + }); + }); + } + _refreshData(); + } + + void _refreshData() { + updateUserInfo(); + loadWatchRecord(); + loadUserVideoCount(); + } + + void updateUserInfo() async { + if (!await UserUtil.isLogin()) { + setState(() { + _user = null; + }); + return; + } + + UserUtil.updateUserInfo(context).then((value) { + print("user:$value"); + setState(() { + _user = value; + }); + }); + } + + void loadWatchRecord() async { + List<WatchRecordModel> temp = await DBManager.listWatchRecord(1, 20); + setState(() { + recordList = temp; + }); + } + + void loadUserVideoCount() async { + if (!await UserUtil.isLogin()) { + return; + } + Map<String, dynamic>? result = + await VideoApiUtil.getUserVideoCount(context); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + setState(() { + collectedVideoCount = result["Data"]["collectionCount"]; + favoriteVideoCount = result["Data"]["attentionCount"]; + }); + } + } + + void jumpPage(String name, {Map<String, dynamic>? params}) { + JumpPageUtil.jumpPage(name, context, native: Platform.isIOS,params: params, + callback: (data) { + _refreshData(); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + ///娉ㄥ唽鐩戝惉鍣� + PageVisibilityBinding.instance.addObserver(this, ModalRoute.of(context)!); + } + + @override + void dispose() { + ///绉婚櫎鐩戝惉鍣� + PageVisibilityBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void onBackground() { + super.onBackground(); + print("LifecycleTestPage - onBackground"); + } + + @override + void onForeground() { + super.onForeground(); + print("LifecycleTestPage - onForeground"); + } + + @override + void onPageHide() { + super.onPageHide(); + print("LifecycleTestPage - onPageHide"); + } + + @override + void onPageShow() { + super.onPageShow(); + print("LifecycleTestPage - onPageShow"); + _refreshData(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + alignment: Alignment.center, + padding: EdgeInsets.only( + top: 15 + MediaQuery.of(context).viewPadding.top, bottom: 15), + color: ColorConstant.theme, + child: const Text("鎴戠殑", + style: TextStyle(fontSize: 20, color: Colors.white)), + ), + Expanded( + child: Container( + height: 20, + color: Colors.white, + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 51, + ), + + //涓汉淇℃伅 + InkWell( + child: Row( + children: [ + Container( + width: 18, + ), + portraitWidget(), + Container( + width: 10, + ), + _user != null + ? personInfoWidget() + : + //鐧诲綍妗� + loginBtnWidget(), + Container( + width: 18, + ), + ], + ), + onTap: () { + UserUtil.isLogin().then((value) { + if (value) { + jumpPage("PersonInfoPage"); + } + }); + }, + ), + Container( + height: 8, + ), + //绛惧悕 + signWidget(), + Container( + height: 23, + ), + //鏀惰棌锛岃拷鍓э紝缂撳瓨 + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + onTap: () { + UserUtil.isLogin().then((value) { + if (!value) { + jumpPage("EmailLoginPage"); + return; + } + jumpPage("VideoCollectedPage"); + }); + }, + child: Column( + children: [ + const Text( + "鏀惰棌", + style: TextStyle( + fontSize: 18, color: Color(0xFF888888)), + ), + Text( + "$collectedVideoCount", + style: const TextStyle( + fontSize: 12, color: Color(0xFF888888)), + ), + ], + )), + InkWell( + onTap: () { + UserUtil.isLogin().then((value) { + if (!value) { + jumpPage("EmailLoginPage"); + return; + } + jumpPage("VideoAttentionPage"); + }); + }, + child: Column( + children: [ + const Text( + "杩藉墽", + style: TextStyle( + fontSize: 18, color: Color(0xFF888888)), + ), + Text( + "$favoriteVideoCount", + style: const TextStyle( + fontSize: 12, color: Color(0xFF888888)), + ), + ], + ), + ), + InkWell( + onTap: () { + jumpPage("VideoDownloadPage"); + }, + child: Column( + children: const [ + Text( + "缂撳瓨", + style: TextStyle( + fontSize: 18, color: Color(0xFF888888)), + ), + Text( + "0", + style: TextStyle( + fontSize: 12, color: Color(0xFF888888)), + ), + ], + )), + ], + ), + //鍘嗗彶璁板綍 + Container( + height: 20, + ), + recordList.isNotEmpty + ? Column( + children: [ + //title + InkWell( + onTap: () { + jumpPage("VideoScanRecordPage"); + }, + child: Row( + children: [ + Container( + width: 16, + ), + const Text( + "鍘嗗彶璁板綍", + style: TextStyle( + color: Color(0xFFAAAAAA), + fontSize: 15), + ), + Expanded(child: Container()), + const Text( + "鍏ㄩ儴", + style: TextStyle( + color: Color(0xFFAAAAAA), + fontSize: 15), + ), + Container( + width: 6, + ), + Image.asset( + "assets/imgs/icon_mine_fun_input.png", + width: 6.5, + ), + Container( + width: 16, + ), + ], + )), + //鍐呭 + Container( + margin: const EdgeInsets.only(top: 15), + height: 270, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + constraints: BoxConstraints( + minWidth: MediaQuery.of(context) + .size + .width), + alignment: Alignment.centerLeft, + child: Wrap( + direction: Axis.vertical, + alignment: WrapAlignment.start, + spacing: 10, + runSpacing: 10, + children: getRecordListWidget()))), + ) + ], + ) + : Container(), + + //鍔熻兘 + Container( + margin: const EdgeInsets.fromLTRB(16, 23, 16, 16), + padding: const EdgeInsets.fromLTRB(24, 10, 24, 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.5), + border: Border.all( + color: const Color(0xFFDDDDDD), width: 1)), + child: Column( + children: [ + functionWidget( + "assets/imgs/icon_mine_fun_share.png", "鍒嗕韩APP", 16, + () { + Share.share("娴烽噺闊╁墽锛屽敖鍦ㄥ簲鐢細$applink"); + }), + functionWidget("assets/imgs/icon_mine_fun_privacy.png", + "闅愮鏀跨瓥", 13.5, () { + jumpPage("BrowserPage", params: {"url": Constant.PRIVACY_URL,"title":"闅愮鏀跨瓥"}); + }), + functionWidget("assets/imgs/icon_mine_fun_feedback.png", + "鍙嶉鍚愭Ы", 13.5, () { + jumpPage("AdvicePage"); + }), + functionWidget("assets/imgs/icon_mine_fun_about_us.png", + "鍏充簬鎴戜滑", 17, () { + MySharedPreferences.getInstance() + .getString("aboutUsLink") + .then((value) { + if (!StringUtil.isNullOrEmpty(value)) { + jumpPage("AboutUsPage"); + } + }); + }), + functionWidget("assets/imgs/icon_mine_fun_settings.png", + "璁剧疆", 17.5, () { + jumpPage("SettingPage"); + }), + ], + ), + ) + ], + )), + )) + ], + )); + } + + List<Widget> getRecordListWidget() { + List<Widget> list = recordList.map((e) { + //鏄惁涓轰富鍒嗙被 + List<int> mainTypes = [150, 151, 152, 153]; + if (mainTypes.contains(e.video!.videoType!.id!)) { + return InkWell( + child: getBigRecordItem(e), + onTap: () { + jumpVideoDetail(context, e.video, Platform.isIOS, + position: e.position!); + }, + ); + } else { + return InkWell( + child: getSmallRecordItem(e), + onTap: () { + jumpVideoDetail(context, e.video, Platform.isIOS, + position: e.position!); + }, + ); + } + }).toList(); + list.insert( + 0, + InkWell( + child: Container( + width: 5, + height: 270, + ))); + list.add(InkWell( + child: Container( + width: 5, + height: 270, + ))); + return list; + } + + Widget getSmallRecordItem(WatchRecordModel record) { + return Container( + height: 130, + child: Stack( + children: [ + Container( + margin: const EdgeInsets.only(top: 20), + height: 110, + width: 120 * 1.4, + decoration: BoxDecoration( + color: Colors.black38, + borderRadius: BorderRadius.circular(10)), + ), + Container( + alignment: Alignment.center, + width: 120 * 1.4, + padding: const EdgeInsets.only(left: 10, right: 10), + child: Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: SizedBox( + height: 100, + width: 120 * 1.4 - 20, + child: VideoImage(record.video!.picture), + ), + ), + Container( + height: 5, + ), + Text( + record.video!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(color: Colors.white), + ) + ], + )) + ], + )); + } + + Widget getBigRecordItem(WatchRecordModel record) { + return Stack( + children: [ + Container( + margin: const EdgeInsets.only(top: 20), + height: 250, + width: 150, + decoration: BoxDecoration( + color: Colors.black38, borderRadius: BorderRadius.circular(10)), + ), + Container( + alignment: Alignment.center, + width: 150, + padding: EdgeInsets.only(left: 10, right: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + height: 200, + child: VideoImage( + record.video!.vpicture ?? record.video!.picture), + ), + ), + Container( + height: 5, + ), + Text( + record.video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle(color: Colors.white), + ) + ], + )) + ], + ); + } + + Widget portraitWidget() { + return Container( + width: 80, + height: 80, + padding: const EdgeInsets.all(2), + alignment: Alignment.center, + decoration: BoxDecoration( + color: const Color(0xFFAAAAAA), + borderRadius: BorderRadius.circular(40)), + child: ClipRRect( + borderRadius: BorderRadius.circular(40), + child: CommonImage( + _user == null + ? "assets/imgs/ic_portrait_default.png" + : _user!.portrait!, + defaultWidget: + Image.asset("assets/imgs/ic_portrait_default.png")), + )); + } + + Widget loginBtnWidget() { + return MyFillButton( + "绔嬪嵆鐧诲綍", + 12.5, + width: 138, + fontSize: 14, + onClick: () { + jumpPage("EmailLoginPage"); + }, + ); + } + + Widget personInfoWidget() { + return Column( + children: [ + Text(_user!.nickname!, + maxLines: 1, + style: const TextStyle( + fontSize: 18, + color: ColorConstant.theme, + )), + Container( + height: 6, + ), + Text("ID:${_user!.id!}", + style: const TextStyle( + fontSize: 15, + color: Color(0xFFA9A9A9), + )), + ], + ); + } + + Widget signWidget() { + return Container( + padding: const EdgeInsets.only(top: 21.5, left: 12.5, right: 12.5), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + ColorConstant.theme.withAlpha(0), + ColorConstant.theme.withAlpha(108), + ], + ), + ), + child: Container( + height: 64, + padding: const EdgeInsets.only(left: 23, right: 23), + alignment: Alignment.centerLeft, + decoration: const BoxDecoration( + color: ColorConstant.theme, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), topRight: Radius.circular(15))), + child: Text( + "涓�х鍚嶏細${(_user == null || _user!.sign == null || _user!.sign!.isEmpty) ? "杩欎釜浜洪潪甯告湁瓒o紝浣嗘槸浠�涔堜篃娌″啓~" : _user!.sign}", + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12, color: Colors.white), + ), + ), + ); + } + + Widget functionWidget( + String icon, String text, double width, GestureTapCallback click) { + return InkWell( + onTap: () { + click(); + }, + child: Container( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Row( + children: [ + Container( + width: 20, + alignment: Alignment.center, + child: Image.asset( + icon, + width: width, + )), + Container( + width: 10, + ), + Text( + text, + style: const TextStyle(color: Color(0xFFAAAAAA), fontSize: 15), + ), + Expanded(child: Container()), + Image.asset( + "assets/imgs/icon_mine_fun_input.png", + width: 6.5, + ), + ], + )), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/common/adinfo_model.dart b/src/main/resources/code/flutter_module/lib/model/common/adinfo_model.dart new file mode 100644 index 0000000..fdb195f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/common/adinfo_model.dart @@ -0,0 +1,29 @@ +/// type : "csj" +/// pid : "123456" + +class AdinfoModel { + AdinfoModel({ + String? type, + String? pid,}){ + _type = type; + _pid = pid; +} + + AdinfoModel.fromJson(dynamic json) { + _type = json['type']; + _pid = json['pid']; + } + String? _type; + String? _pid; + + String? get type => _type; + String? get pid => _pid; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['type'] = _type; + map['pid'] = _pid; + return map; + } + +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/model/common/http_model.dart b/src/main/resources/code/flutter_module/lib/model/common/http_model.dart new file mode 100644 index 0000000..3f57498 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/common/http_model.dart @@ -0,0 +1,16 @@ +import '../../api/http.dart'; + +class HttpRequestResult { + final Map<String, dynamic>? data; + final bool success; + final String? msg; + + HttpRequestResult(this.success, this.data, {this.msg}); +} + +class HttpRequestListener { + OnHttpRequestFinish? onFinish; + OnHttpRequestStart? onStart; + + HttpRequestListener({this.onStart, this.onFinish}); +} diff --git a/src/main/resources/code/flutter_module/lib/model/user/user_info.dart b/src/main/resources/code/flutter_module/lib/model/user/user_info.dart new file mode 100644 index 0000000..01d7f1b --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/user/user_info.dart @@ -0,0 +1,73 @@ +/// Id : "1003703" +/// Email : "2780501319@qq.com" +/// Nickname : "娴嬭瘯" +/// Portrait : "http://buwan-1255749512.file.myqcloud.com/portrait_1640746276175.jpg" +/// Sex : "0" +/// Sign : "" + +class UserInfo { + UserInfo({ + String? id, + String? email, + String? nickname, + String? portrait, + String? sex, + String? sign, + String? birthday, + }) { + _id = id; + _email = email; + _nickname = nickname; + _portrait = portrait; + _sex = sex; + _sign = sign; + _birthday = birthday; + } + + UserInfo.fromJson(dynamic json) { + _id = json['Id']; + _email = json['Email']; + _nickname = json['Nickname']; + _portrait = json['Portrait']; + _sex = json['Sex']; + _sign = json['Sign']; + _birthday = json['Birthday']; + } + + String? _id; + String? _email; + String? _nickname; + String? _portrait; + String? _sex; + String? _sign; + int? _vipExpireTime; + String? _birthday; + + String? get id => _id; + + String? get email => _email; + + String? get nickname => _nickname; + + String? get portrait => _portrait; + + String? get sex => _sex; + + String? get sign => _sign; + + int? get vipExpireTime => _vipExpireTime; + + String? get birthday => _birthday; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Email'] = _email; + map['Nickname'] = _nickname; + map['Portrait'] = _portrait; + map['Sex'] = _sex; + map['Sign'] = _sign; + map['Birthday'] = _birthday; + return map; + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/video/home_ad_model.dart b/src/main/resources/code/flutter_module/lib/model/video/home_ad_model.dart new file mode 100644 index 0000000..d547331 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/home_ad_model.dart @@ -0,0 +1,74 @@ +import '../../model/video/video_model.dart'; + +/// Id : "728" +/// Picture : "https://hbimg.huabanimg.com/e96071279c6ee30f7e9ab54eac68738db2c0d77d1a123-DqVlxn_fw658/format/webp" +/// Createtime : "1629191385368" +/// Video : {"Id":"257727","Tag":"16闆嗗叏","Createtime":1440729119000,"Picture":"http://pic6.iqiyipic.com/image/20200609/ac/2b/a_100014741_m_601_m17.jpg","Name":"澶槼鐨勫悗瑁�","Introduction":"","Duration":"3006","MainActor":"","Year":"2016","Score":"9.3","Share":"0","WatchCount":"137468","NowNumber":"1","Finish":"1","Area":"闊╁浗","Vpicture":"http://pic6.iqiyipic.com/image/20200609/ac/2b/a_100014741_m_601_m17_260_360.jpg","Hpicture":"http://pic6.iqiyipic.com/image/20200609/ac/2b/a_100014741_m_601_m17_480_270.jpg","CanSave":false,"VideoType":{"Id":150,"Name":"鐢佃鍓�","Icon":"https://hbimg.huabanimg.com/40b7d467ac5bd3abdedb70aa8d29dbc144d27fc51876-hvReYJ_fw658/format/webp","Createtime":"0"},"CommentCount":0,"ThirdType":"0","ShowType":0,"Free":0,"Definition":0,"VideoDetailList":[]} +/// Introduction : "澶槼鐨勫悗瑁�" +/// LinkType : 1 + +class HomeAdModel { + HomeAdModel( + {String? id, + String? picture, + String? createtime, + VideoInfoModel? video, + String? introduction, + int? linkType, + String? params}) { + _id = id; + _picture = picture; + _createtime = createtime; + _video = video; + _introduction = introduction; + _linkType = linkType; + _params = params; + } + + HomeAdModel.fromJson(dynamic json) { + _id = json['Id']; + _picture = json['Picture']; + _createtime = json['Createtime']; + _video = + json['Video'] != null ? VideoInfoModel.fromJson(json['Video']) : null; + _introduction = json['Introduction']; + _linkType = json['LinkType']; + _params = json['Params']; + } + + String? _id; + String? _picture; + String? _createtime; + VideoInfoModel? _video; + String? _introduction; + int? _linkType; + String? _params; + + String? get id => _id; + + String? get picture => _picture; + + String? get createtime => _createtime; + + VideoInfoModel? get video => _video; + + String? get introduction => _introduction; + + int? get linkType => _linkType; + + String? get params => _params; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Picture'] = _picture; + map['Createtime'] = _createtime; + if (_video != null) { + map['Video'] = _video?.toJson(); + } + map['Introduction'] = _introduction; + map['LinkType'] = _linkType; + map['Params'] = _params; + return map; + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/video/home_type_model.dart b/src/main/resources/code/flutter_module/lib/model/video/home_type_model.dart new file mode 100644 index 0000000..9e394a7 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/home_type_model.dart @@ -0,0 +1,221 @@ +import '../../model/video/video_model.dart'; + +/// Id : "134" +/// Name : "澶у閮藉湪鐪�" +/// Createtime : "1640316645416" +/// HomeVideoList : [{"Id":"77684","Video":{"Id":""},"Picture":"","Tag":"","Orderby":90,"BigPicture":false,"VideoId":"38abe4cc5c733290101fa0699dbace2a","Createtime":"1640340085417"}] +/// Activity : "" +/// Params : "" +/// HasMore : false +/// NeedAd : false +/// IosControl : "" +/// Icon : "" +/// Columns : 3 +/// Number : 18 +/// RefreshPosition : 0 +/// MoreTag : "" +/// Count : 19 + +class HomeTypeModel { + HomeTypeModel({ + String? id, + String? name, + String? createtime, + List<HomeVideoModel>? homeVideoList, + String? activity, + String? params, + bool? hasMore, + bool? needAd, + String? iosControl, + String? icon, + int? columns, + int? number, + int? refreshPosition, + String? moreTag, + int? count, + }) { + _id = id; + _name = name; + _createtime = createtime; + _homeVideoList = homeVideoList; + _activity = activity; + _params = params; + _hasMore = hasMore; + _needAd = needAd; + _iosControl = iosControl; + _icon = icon; + _columns = columns; + _number = number; + _refreshPosition = refreshPosition; + _moreTag = moreTag; + _count = count; + } + + HomeTypeModel.fromJson(dynamic json) { + _id = json['Id']; + _name = json['Name']; + _createtime = json['Createtime']; + if (json['HomeVideoList'] != null) { + _homeVideoList = []; + json['HomeVideoList'].forEach((v) { + _homeVideoList?.add(HomeVideoModel.fromJson(v)); + }); + } + _activity = json['Activity']; + _params = json['Params']; + _hasMore = json['HasMore']; + _needAd = json['NeedAd']; + _iosControl = json['IosControl']; + _icon = json['Icon']; + _columns = json['Columns']; + _number = json['Number']; + _refreshPosition = json['RefreshPosition']; + _moreTag = json['MoreTag']; + _count = json['Count']; + } + + String? _id; + String? _name; + String? _createtime; + List<HomeVideoModel>? _homeVideoList; + String? _activity; + String? _params; + bool? _hasMore; + bool? _needAd; + String? _iosControl; + String? _icon; + int? _columns; + int? _number; + int? _refreshPosition; + String? _moreTag; + int? _count; + + String? get id => _id; + + String? get name => _name; + + String? get createtime => _createtime; + + List<HomeVideoModel>? get homeVideoList => _homeVideoList; + + String? get activity => _activity; + + String? get params => _params; + + bool? get hasMore => _hasMore; + + bool? get needAd => _needAd; + + String? get iosControl => _iosControl; + + String? get icon => _icon; + + int? get columns => _columns; + + int? get number => _number; + + int? get refreshPosition => _refreshPosition; + + String? get moreTag => _moreTag; + + int? get count => _count; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Name'] = _name; + map['Createtime'] = _createtime; + if (_homeVideoList != null) { + map['HomeVideoList'] = _homeVideoList?.map((v) => v.toJson()).toList(); + } + map['Activity'] = _activity; + map['Params'] = _params; + map['HasMore'] = _hasMore; + map['NeedAd'] = _needAd; + map['IosControl'] = _iosControl; + map['Icon'] = _icon; + map['Columns'] = _columns; + map['Number'] = _number; + map['RefreshPosition'] = _refreshPosition; + map['MoreTag'] = _moreTag; + map['Count'] = _count; + return map; + } +} + +/// Id : "77684" +/// Video : {"Id":""} +/// Picture : "" +/// Tag : "" +/// Orderby : 90 +/// BigPicture : false +/// VideoId : "38abe4cc5c733290101fa0699dbace2a" +/// Createtime : "1640340085417" + +class HomeVideoModel { + HomeVideoModel({ + String? id, + VideoInfoModel? video, + String? picture, + String? tag, + int? orderby, + bool? bigPicture, + String? videoId, + }) { + _id = id; + _video = video; + _picture = picture; + _tag = tag; + _orderby = orderby; + _bigPicture = bigPicture; + _videoId = videoId; + } + + HomeVideoModel.fromJson(dynamic json) { + _id = json['Id']; + _video = + json['Video'] != null ? VideoInfoModel.fromJson(json['Video']) : null; + _picture = json['Picture']; + _tag = json['Tag']; + _orderby = json['Orderby']; + _bigPicture = json['BigPicture']; + _videoId = json['VideoId']; + } + + String? _id; + VideoInfoModel? _video; + String? _picture; + String? _tag; + int? _orderby; + bool? _bigPicture; + String? _videoId; + + String? get id => _id; + + VideoInfoModel? get video => _video; + + String? get picture => _picture; + + String? get tag => _tag; + + int? get orderby => _orderby; + + bool? get bigPicture => _bigPicture; + + String? get videoId => _videoId; + + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + if (_video != null) { + map['Video'] = _video?.toJson(); + } + map['Picture'] = _picture; + map['Tag'] = _tag; + map['Orderby'] = _orderby; + map['BigPicture'] = _bigPicture; + map['VideoId'] = _videoId; + return map; + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/video/search_special_model.dart b/src/main/resources/code/flutter_module/lib/model/video/search_special_model.dart new file mode 100644 index 0000000..d6afa4f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/search_special_model.dart @@ -0,0 +1,36 @@ +/// icon : "https://buwan-1255749512.cos.ap-guangzhou.myqcloud.com/resource/hanju/icon_tv.png" +/// name : "鐢佃鍓�" +/// id : "2#hanju-tv" + +class SearchSpecialModel { + SearchSpecialModel({ + String? icon, + String? name, + String? id,}){ + _icon = icon; + _name = name; + _id = id; +} + + SearchSpecialModel.fromJson(dynamic json) { + _icon = json['icon']; + _name = json['name']; + _id = json['id']; + } + String? _icon; + String? _name; + String? _id; + + String? get icon => _icon; + String? get name => _name; + String? get id => _id; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['icon'] = _icon; + map['name'] = _name; + map['id'] = _id; + return map; + } + +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/model/video/video_attention_model.dart b/src/main/resources/code/flutter_module/lib/model/video/video_attention_model.dart new file mode 100644 index 0000000..b7e2dcc --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/video_attention_model.dart @@ -0,0 +1,49 @@ +/// Picture : "http://pic2.iqiyipic.com/image/20200902/65/f6/a_50181443_m_601_m3.jpg" +/// Name : "缁呭+鐨勫搧鏍�" +/// UpdateInfo : "鏇存柊:2骞村墠" +/// IsAttention : false + +class VideoAttentionModel { + VideoAttentionModel({ + String? picture, + String? name, + String? updateInfo, + bool? attention,}){ + _picture = picture; + _name = name; + _updateInfo = updateInfo; + _attention = attention; +} + + VideoAttentionModel.fromJson(dynamic json) { + _picture = json['Picture']; + _name = json['Name']; + _updateInfo = json['UpdateInfo']; + _attention = json['Attention']; + } + String? _picture; + String? _name; + String? _updateInfo; + bool? _attention; + + String? get picture => _picture; + String? get name => _name; + String? get updateInfo => _updateInfo; + bool? get attention => _attention; + + + set attention(value) { + _attention = value; + } + + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Picture'] = _picture; + map['Name'] = _name; + map['UpdateInfo'] = _updateInfo; + map['Attention'] = _attention; + return map; + } + +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/model/video/video_detail_model.dart b/src/main/resources/code/flutter_module/lib/model/video/video_detail_model.dart new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/video_detail_model.dart diff --git a/src/main/resources/code/flutter_module/lib/model/video/video_model.dart b/src/main/resources/code/flutter_module/lib/model/video/video_model.dart new file mode 100644 index 0000000..7ff472d --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/video_model.dart @@ -0,0 +1,381 @@ +class VideoInfoModel { + VideoInfoModel({ + String? id, + String? picture, + String? name, + String? introduction, + String? duration, + String? mainActor, + String? year, + VideoType? videoType, + int? thirdType, + String? createtime, + String? score, + String? watchCount, + String? share, + bool? canSave, + String? tag, + List<VideoResource>? resourceList, + String? playPicture, + int? showType, + String? vpicture, + String? hpicture, + int? commentCount, + String? updatetime, + String? area, + String? nowNumber, + String? finish, + List<VideoDetailInfo>? videoDetailList, + }) { + _id = id; + _picture = picture; + _name = name; + _introduction = introduction; + _duration = duration; + _mainActor = mainActor; + _year = year; + _videoType = videoType; + _score = score; + _watchCount = watchCount; + _share = share; + _canSave = canSave; + _tag = tag; + _resourceList = resourceList; + _playPicture = playPicture; + _showType = showType; + _vpicture = vpicture; + _hpicture = hpicture; + _commentCount = commentCount; + _updatetime = updatetime; + _area = area; + _nowNumber = nowNumber; + _finish = finish; + _videoDetailList = videoDetailList; + } + + VideoInfoModel.fromJson(dynamic json) { + _id = json['Id']; + _picture = json['Picture']; + _name = json['Name']; + _introduction = json['Introduction']; + _duration = json['Duration']; + _mainActor = json['MainActor']; + _year = json['Year']; + _videoType = json['VideoType'] != null + ? VideoType.fromJson(json['VideoType']) + : null; + _score = json['Score']; + _watchCount = json['WatchCount']; + _share = json['Share']; + _canSave = json['CanSave']; + _tag=json['Tag']; + if (json['ResourceList'] != null) { + _resourceList = []; + json['ResourceList'].forEach((v) { + _resourceList?.add(VideoResource.fromJson(v)); + }); + } + _playPicture = json['PlayPicture']; + _showType = json['ShowType']; + _vpicture = json['Vpicture']; + _hpicture = json['Hpicture']; + _commentCount = json['CommentCount']; + _updatetime = json['Updatetime']; + _area = json['Area']; + _nowNumber = json['NowNumber']; + _finish = json['Finish']; + if (json['VideoDetailList'] != null) { + _videoDetailList = []; + json['VideoDetailList'].forEach((v) { + _videoDetailList?.add(VideoDetailInfo.fromJson(v)); + }); + } + } + + String? _id; + String? _picture; + String? _name; + String? _introduction; + String? _duration; + String? _mainActor; + String? _year; + VideoType? _videoType; + String? _score; + String? _watchCount; + String? _share; + bool? _canSave; + String? _tag; + List<VideoResource>? _resourceList; + String? _playPicture; + int? _showType; + String? _vpicture; + String? _hpicture; + int? _commentCount; + String? _updatetime; + String? _area; + String? _nowNumber; + String? _finish; + List<VideoDetailInfo>? _videoDetailList; + + String? get id => _id; + + String? get picture => _picture; + + String? get name => _name; + + String? get introduction => _introduction; + + String? get duration => _duration; + + String? get mainActor => _mainActor; + + String? get year => _year; + + VideoType? get videoType => _videoType; + + + + String? get score => _score; + + String? get watchCount => _watchCount; + + String? get share => _share; + + bool? get canSave => _canSave; + + String? get tag => _tag; + + List<VideoResource>? get resourceList => _resourceList; + + String? get playPicture => _playPicture; + + int? get showType => _showType; + + String? get vpicture => _vpicture; + + String? get hpicture => _hpicture; + + int? get commentCount => _commentCount; + + String? get updatetime => _updatetime; + + String? get area => _area; + + String? get nowNumber => _nowNumber; + + String? get finish => _finish; + + List<VideoDetailInfo>? get videoDetailList => _videoDetailList; + + set videoDetailList(value) { + _videoDetailList = value; + } + + set tag(value) { + _tag = value; + } + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Picture'] = _picture; + map['Name'] = _name; + map['Introduction'] = _introduction; + map['Duration'] = _duration; + map['MainActor'] = _mainActor; + map['Year'] = _year; + if (_videoType != null) { + map['VideoType'] = _videoType?.toJson(); + } + map['Score'] = _score; + map['WatchCount'] = _watchCount; + map['Share'] = _share; + map['CanSave'] = _canSave; + map['Tag'] = _tag; + if (_resourceList != null) { + map['ResourceList'] = _resourceList?.map((v) => v.toJson()).toList(); + } + map['PlayPicture'] = _playPicture; + map['ShowType'] = _showType; + map['Vpicture'] = _vpicture; + map['Hpicture'] = _hpicture; + map['CommentCount'] = _commentCount; + map['Updatetime'] = _updatetime; + map['Area'] = _area; + map['NowNumber'] = _nowNumber; + map['Finish'] = _finish; + if (_videoDetailList != null) { + map['VideoDetailList'] = + _videoDetailList?.map((v) => v.toJson()).toList(); + } + return map; + } +} + +/// Introduction : "鍛婂瘑鑰呯埗濂宠荡绁為兘鍛婂瘑鍦ㄥ崡甯傞亣瀹筹紝鍐呭崼鏈堝崕鍚涙鎬濇湀鎶㈠厛涓�姝ュ甫璧板珜鐤戝嚩鐘紝澶х悊瀵哄嵖楂樺崌鍗翠互鍒哄鍒烘潃闀夸箰閮′富涓虹敱灏嗗叾浠庡唴鍗姠鍥炪�俓n楂樼鐑涙綔鍏ュぇ鐞嗗鐙卞啋鍏呮槬绉嬮亾閬撲紬濂楄瘽鍑剁姱锛屽緱鐭モ�滃憡瀵嗚�呯埗濂冲凡姝伙紝绁為亾澶т笟灏嗘垚鈥濄�俓n楂樼鐑涗粠鍛婂瘑鑰呭案韬笂鐪嬪埌鐔熸倝鐨勫崄瀛楄疮绌夸激锛屼笖鎵惧埌涓�寮犲叕楠岋紝琛ㄦ槑鍛婂瘑鑰呰嚜濂佸北鑰屾潵锛屾兂鍒板憡瀵嗚�呭湪鍗楀競涓庣櫨閲屽紭姣呰闈㈢殑涓�骞曪紝楂樼鐑涙��鐤戞浜嬪拰鐧鹃噷寮樻瘏鏈夊叧銆俓n鍐呭崼濂夊尽閮庢鏀稿喅鎺堟剰鎬濇湀涓庡ぇ鐞嗗鑱旀墜璋冩煡鍛婂瘑鑰呰鏉�涓�妗堬紝浜虹姱鍦ㄧ嫳涓噸浼よ�屼骸锛屽ぇ鐞嗗浜暱瑁磋皬娆插鍑哄叆杩囩洃鐗㈢殑浜轰竴涓�瀹¢棶锛屾鏃讹紝楂樼鐑涚珯鍑猴紝琛ㄧず浜虹姱鏄嚜宸辨墍鏉�銆俓n" +/// Name : "椋庤捣娲涢槼绗�1闆�" +/// Type : "album" +/// Id : "2152712324153900" +/// WatchCount : 0 +/// Tag : "1" + +class VideoDetailInfo { + VideoDetailInfo({ + String? introduction, + String? name, + String? type, + String? id, + int? watchCount, + String? tag, + }) { + _introduction = introduction; + _name = name; + _type = type; + _id = id; + _watchCount = watchCount; + _tag = tag; + } + + VideoDetailInfo.fromJson(dynamic json) { + _introduction = json['Introduction']; + _name = json['Name']; + _type = json['Type']; + _id = json['Id']; + _watchCount = json['WatchCount']; + _tag = json['Tag']; + } + + String? _introduction; + String? _name; + String? _type; + String? _id; + int? _watchCount; + String? _tag; + + String? get introduction => _introduction; + + String? get name => _name; + + String? get type => _type; + + String? get id => _id; + + int? get watchCount => _watchCount; + + String? get tag => _tag; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Introduction'] = _introduction; + map['Name'] = _name; + map['Type'] = _type; + map['Id'] = _id; + map['WatchCount'] = _watchCount; + map['Tag'] = _tag; + return map; + } +} + +/// Id : "22" +/// Name : "鐖卞鑹�2" +/// Createtime : "1464659723000" +/// Picture : "http://buwan-1255749512.cos.ap-guangzhou.myqcloud.com/resource/source/iqiyi.png" +/// Checked : true + +class VideoResource { + VideoResource({ + String? id, + String? name, + String? createtime, + String? picture, + bool? checked, + }) { + _id = id; + _name = name; + _createtime = createtime; + _picture = picture; + _checked = checked; + } + + VideoResource.fromJson(dynamic json) { + _id = json['Id']; + _name = json['Name']; + _createtime = json['Createtime']; + _picture = json['Picture']; + _checked = json['Checked']; + } + + String? _id; + String? _name; + String? _createtime; + String? _picture; + bool? _checked; + + String? get id => _id; + + String? get name => _name; + + String? get createtime => _createtime; + + String? get picture => _picture; + + bool? get checked => _checked; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Name'] = _name; + map['Createtime'] = _createtime; + map['Picture'] = _picture; + map['Checked'] = _checked; + return map; + } +} + +/// Id : 150 +/// Name : "鐢佃鍓�" +/// Icon : "https://hbimg.huabanimg.com/40b7d467ac5bd3abdedb70aa8d29dbc144d27fc51876-hvReYJ_fw658/format/webp" +/// Createtime : "0" + +class VideoType { + VideoType({ + int? id, + String? name, + String? icon, + String? createtime, + }) { + _id = id; + _name = name; + _icon = icon; + _createtime = createtime; + } + + VideoType.fromJson(dynamic json) { + _id = json['Id']; + _name = json['Name']; + _icon = json['Icon']; + _createtime = json['Createtime']; + } + + int? _id; + String? _name; + String? _icon; + String? _createtime; + + int? get id => _id; + + String? get name => _name; + + String? get icon => _icon; + + String? get createtime => _createtime; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['Id'] = _id; + map['Name'] = _name; + map['Icon'] = _icon; + map['Createtime'] = _createtime; + return map; + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/video/video_play_url_model.dart b/src/main/resources/code/flutter_module/lib/model/video/video_play_url_model.dart new file mode 100644 index 0000000..56439e3 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/video_play_url_model.dart @@ -0,0 +1,53 @@ +import '../../model/video/video_model.dart'; + +/// Resource : {"Id":"22","Name":"鐖卞鑹�2","Createtime":"1464659723000","Picture":"http://buwan-1255749512.cos.ap-guangzhou.myqcloud.com/resource/source/iqiyi.png","Checked":false} +/// Url : "http://m.iqiyi.com/v_19rrk83jpg.html?vfm=m_330_hjvap" +/// PlayType : 1 +/// Params : "" + +class VideoPlayUrlModel { + VideoPlayUrlModel({ + VideoResource? resource, + String? url, + int? playType, + String? params, + }) { + _resource = resource; + _url = url; + _playType = playType; + _params = params; + } + + VideoPlayUrlModel.fromJson(dynamic json) { + _resource = json['Resource'] != null + ? VideoResource.fromJson(json['Resource']) + : null; + _url = json['Url']; + _playType = json['PlayType']; + _params = json['Params']; + } + + VideoResource? _resource; + String? _url; + int? _playType; + String? _params; + + VideoResource? get resource => _resource; + + String? get url => _url; + + int? get playType => _playType; + + String? get params => _params; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + if (_resource != null) { + map['Resource'] = _resource?.toJson(); + } + map['Url'] = _url; + map['PlayType'] = _playType; + map['Params'] = _params; + return map; + } +} diff --git a/src/main/resources/code/flutter_module/lib/model/video/watch_record_model.dart b/src/main/resources/code/flutter_module/lib/model/video/watch_record_model.dart new file mode 100644 index 0000000..1ceca1b --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/model/video/watch_record_model.dart @@ -0,0 +1,69 @@ +import '../../model/video/video_model.dart'; + +/// id : 123 +/// videoId : "123123" +/// video : {"id":"123"} +/// videoDetail : {"id":"123"} +/// position : 1 +/// createTime : 123 +/// updateTime : 123 + +class WatchRecordModel { + WatchRecordModel({ + int? id, + String? videoId, + VideoInfoModel? video, + VideoDetailInfo? videoDetail, + int? position, + int? createTime, + int? updateTime,}){ + _id = id; + _videoId = videoId; + _video = video; + _videoDetail = videoDetail; + _position = position; + _createTime = createTime; + _updateTime = updateTime; +} + + WatchRecordModel.fromJson(dynamic json) { + _id = json['id']; + _videoId = json['videoId']; + _video = json['video'] != null ? VideoInfoModel.fromJson(json['video']) : null; + _videoDetail = json['videoDetail'] != null ? VideoDetailInfo.fromJson(json['videoDetail']) : null; + _position = json['position']; + _createTime = json['createTime']; + _updateTime = json['updateTime']; + } + int? _id; + String? _videoId; + VideoInfoModel? _video; + VideoDetailInfo? _videoDetail; + int? _position; + int? _createTime; + int? _updateTime; + + int? get id => _id; + String? get videoId => _videoId; + VideoInfoModel? get video => _video; + VideoDetailInfo? get videoDetail => _videoDetail; + int? get position => _position; + int? get createTime => _createTime; + int? get updateTime => _updateTime; + + Map<String, dynamic> toJson() { + final map = <String, dynamic>{}; + map['id'] = _id; + map['videoId'] = _videoId; + if (_video != null) { + map['video'] = _video?.toJson(); + } + if (_videoDetail != null) { + map['videoDetail'] = _videoDetail?.toJson(); + } + map['position'] = _position; + map['createTime'] = _createTime; + map['updateTime'] = _updateTime; + return map; + } +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/splash.dart b/src/main/resources/code/flutter_module/lib/splash.dart new file mode 100644 index 0000000..442c978 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/splash.dart @@ -0,0 +1,10 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'mine.dart'; + +void main(){ + runApp(MinePage(title: "")); +} + + + diff --git a/src/main/resources/code/flutter_module/lib/ui/common/browser.dart b/src/main/resources/code/flutter_module/lib/ui/common/browser.dart new file mode 100644 index 0000000..a047dea --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/common/browser.dart @@ -0,0 +1,132 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../utils/ui_constant.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +import '../../ui/widget/nav.dart'; +import '../../utils/jsinterface.dart'; + +class BrowserPage extends StatefulWidget { + BrowserPage({Key? key, required this.title, required this.url}) + : super(key: key); + final String title; + final String url; + + @override + _BrowserPageState createState() => _BrowserPageState(title, url); +} + +class _BrowserPageState extends State<BrowserPage> + with SingleTickerProviderStateMixin { + String title = ""; + String? url; + double progress = 0; + + _BrowserPageState(this.title, this.url); + + @override + void initState() { + super.initState(); + if (Platform.isAndroid) { + WebView.platform = SurfaceAndroidWebView(); + } + } + + WebViewController? _webViewController; + + _back() { + if (_webViewController == null) { + popPage(context); + } else { + _webViewController!.canGoBack().then((value) { + if (value) { + _webViewController!.goBack(); + } else { + popPage(context); + } + }); + } + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + _back(); + return false; + }, + child: Scaffold( + backgroundColor: Colors.white, + body: Flex( + direction: Axis.vertical, + children: [ + TopNavBar( + title: title, + back: () { + _back(); + }, + rightIcon: const Icon(Icons.refresh,size: 30,), + rightClick: (){ + if(_webViewController!=null){ + _webViewController!.reload(); + } + }, + ), + SizedBox( + height: 1, + child: LinearProgressIndicator( + backgroundColor: Colors.white, + valueColor: const AlwaysStoppedAnimation(ColorConstant.theme), + value: progress, + ), + ), + Expanded( + child: WebView( + //http://192.168.3.122:8848/test/JsTest.html + initialUrl:url, + onWebViewCreated: (WebViewController webViewController) { + _webViewController = webViewController; + }, + javascriptMode: JavascriptMode.unrestricted, + javascriptChannels: + JavascriptInterface(context, _webViewController) + .getInterfaces(), + navigationDelegate: (NavigationRequest request) { + print("閾炬帴锛�${request.url}"); + if (!request.url.startsWith("http")) { + launch(request.url); + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + onPageStarted: (url) { + print("process:onPageStarted-$url"); + }, + onPageFinished: (url) { + print("process:onPageFinished-$url"); + _webViewController!.getTitle().then((value) { + if (value != null) { + setState(() { + title = value; + }); + } + }); + }, + onProgress: (int process) { + print("process:$process"); + setState(() { + if (process == 100) { + progress = 0; + } else { + progress = process / 100.0; + } + }); + }, + )) + ], + ))); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/demo_page.dart b/src/main/resources/code/flutter_module/lib/ui/demo_page.dart new file mode 100644 index 0000000..89afd68 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/demo_page.dart @@ -0,0 +1,81 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../api/http.dart'; +import 'common/browser.dart'; +import 'widget/dialog.dart'; +import 'widget/nav.dart'; +import '../utils/cache_util.dart'; +import '../utils/config_util.dart'; +import '../utils/event_bus_util.dart'; +import '../utils/pageutils.dart'; +import '../utils/push_util.dart'; +import '../utils/setting_util.dart'; +import '../utils/string_util.dart'; +import '../utils/ui_constant.dart'; +import '../utils/ui_utils.dart'; +import '../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '瑙嗛鏀惰棌', + theme: ThemeData(primaryColor: const Color(0xFFF5F5F5)), + home: DemoPage(title: ''), + ); + } +} + +class DemoPage extends StatefulWidget { + DemoPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _DemoPageState createState() => _DemoPageState(); +} + +class _DemoPageState extends State<DemoPage> + with SingleTickerProviderStateMixin { + + @override + void initState() { + super.initState(); + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F5F5), + body: Flex( + direction: Axis.vertical, + children: [ + TopNavBar(title: "鎴戠殑鏀惰棌"), + InkWell(onTap: (){ + popPage(context); + }, child: Text("缁撴潫椤甸潰"),) + + ], + + + )); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/main.dart b/src/main/resources/code/flutter_module/lib/ui/main.dart new file mode 100644 index 0000000..5d86f3b --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/main.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'dart:ui'; + +import 'test.dart'; + +void main() => runApp(_widgetForRoute(window.defaultRouteName)); + +Widget _widgetForRoute(String route) { + print("flutter page: $route"); + switch (route) { + case "home": + return SplashPage(title: ""); + } + + return Container(); +} diff --git a/src/main/resources/code/flutter_module/lib/ui/main/home.dart b/src/main/resources/code/flutter_module/lib/ui/main/home.dart new file mode 100644 index 0000000..9cff1ad --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/main/home.dart @@ -0,0 +1,539 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/user_api.dart'; + +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/mine/advice.dart'; +import '../../ui/mine/login.dart'; +import '../../ui/mine/settings.dart'; +import '../../utils/config_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // Try running your application with "flutter run". You'll see the + // application has a blue toolbar. Then, without quitting the app, try + // changing the primarySwatch below to Colors.green and then invoke + // "hot reload" (press "r" in the console where you ran "flutter run", + // or simply save your changes to "hot reload" in a Flutter IDE). + // Notice that the counter didn't reset back to zero; the application + // is not restarted. + primaryColor: Color.fromARGB(255, 150, 150, 150)), + home: MinePage(title: ''), + ); + } +} + +class MinePage extends StatefulWidget { + MinePage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _MinePageState createState() => _MinePageState(); +} + +class _MinePageState extends State<MinePage> + with AutomaticKeepAliveClientMixin { + UserInfo? userInfo = null; + bool isLogin = false; + bool isVIP = false; + List<Widget> list = []; + Widget? adView; + bool adDeleted = false; + final List<Functions> data = [ + Functions("assets/imgs/mine/icon_mine_permission.png", "鏉冮檺璁剧疆", + "permission", false), + ]; + + void _onClick(Functions function) async { + if (function.needLogin) { + bool login = await UserUtil.isLogin(); + if (!login) { + NavigatorUtil.navigateToNextPage(context, LoginPage(title: ""), (data) { + _getUserInfo(); + }); + return; + } + } + + String? uid = await UserUtil.getUid(); + + switch (function.key) { + case "kefu": + ConfigUtil.getConfig(context, ConfigKey.kefu).then((value) { + if (!StringUtil.isNullOrEmpty(value)) { + NavigatorUtil.navigateToNextPage( + context, BrowserPage(title: "鍦ㄧ嚎瀹㈡湇", url: value!), (data) {}); + } + }); + break; + + case "advice": + NavigatorUtil.navigateToNextPage( + context, AdvicePage(title: ""), (data) {}); + break; + case "protocol": + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: "鐢ㄦ埛鍗忚", + url: Constant.PROTOCOL_URL, + ), + (data) {}); + break; + case "privacy": + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: "闅愮鏀跨瓥", + url: Constant.PRIVACY_URL, + ), + (data) {}); + break; + case "setting": + NavigatorUtil.navigateToNextPage(context, SettingPage(title: ""), + (data) { + _getUserInfo(); + }); + + break; + } + } + + @override + void initState() { + data.forEach((item) { + list.add(InkWell( + child: Column( + children: [ + Image.asset(item.icon, height: 31), + Container( + child: Text( + item.name, + style: new TextStyle(color: ColorConstant.theme), + ), + margin: const EdgeInsets.fromLTRB(0, 13, 0, 0)) + ], + ), + onTap: () { + _onClick(item); + }, + )); + }); + + //鑾峰彇鐢ㄦ埛淇℃伅 + _getUserInfo(); + + super.initState(); + } + + void _login() { + NavigatorUtil.navigateToNextPage(context, LoginPage(title: ""), (data) { + _getUserInfo(); + }); + } + + void _loadAd() async { + if (adView != null) { + return; + } + if (adDeleted) { + return; + } + + // Widget? ad = AdUtil.loadExpress( + // await AdUtil.getAdInfo(context, AdPosition.mineExpress), + // MediaQuery.of(context).size.width - 20, + // 200, (success, msg) { + // adDeleted = true; + // setState(() { + // adView = null; + // }); + // }); + // + // setState(() { + // adView = ad; + // }); + } + + void _getUserInfo() async { + var user = await UserUtil.getUserInfo(); + if (user == null) { + setState(() { + isLogin = false; + userInfo = null; + }); + return; + } + setState(() { + isLogin = true; + userInfo = user; + }); + + Map<String, dynamic>? result = + await UserApiUtil.getUserInfo(context, user.id!); + var code = result!["code"]; + if (code == 0) { + UserInfo user = UserInfo.fromJson(result["data"]); + //淇濆瓨鐢ㄦ埛淇℃伅 + UserUtil.setUserInfo(user); + setState(() { + userInfo = user; + if (userInfo != null && + userInfo!.vipExpireTime != null && + userInfo!.vipExpireTime! > DateTime.now().millisecondsSinceEpoch) { + isVIP = true; + } else { + isVIP = false; + } + }); + } else if (code == 80001) { + //璐﹀彿琚皝绂� + setState(() { + isLogin = false; + }); + ToastUtil.toast("璐﹀彿宸茶灏佺",context); + await UserUtil.logout(); + } else if (code == 80002) { + //璐﹀彿琚垹闄� + setState(() { + isLogin = false; + }); + ToastUtil.toast("璐﹀彿宸茶鍒犻櫎",context); + await UserUtil.logout(); + } + } + + Widget getLoginContentView() { + if (isLogin) { + return getUserInfoWidget(); + } else { + return getLoginBtnWidget(); + } + } + + Widget getLoginBtnWidget() { + return Expanded( + child: InkWell( + onTap: () { + _login(); + }, + child: Container( + margin: const EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 38, + alignment: Alignment.center, + child: const Text( + "鐧诲綍/娉ㄥ唽", + style: TextStyle(color: Colors.white, fontSize: 15), + ), + decoration: const BoxDecoration( + color: Color(0xFF0E96FF), + borderRadius: BorderRadius.all(Radius.elliptical(10, 10))), + ), + )); + } + + Widget getUserInfoWidget() { + return Container( + height: 50, + padding: EdgeInsets.zero, + margin: const EdgeInsets.fromLTRB(10, 0, 0, 0), + alignment: Alignment.topLeft, + child: Flex( + direction: Axis.vertical, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + userInfo!.nickname!, + style: const TextStyle(color: Color(0xFF40C7FF), fontSize: 18), + ), + Text( + "ID:" + userInfo!.id!.toString(), + style: const TextStyle(color: Color(0xFF40C7FF), fontSize: 12), + ), + ], + ), + ); + } + + Widget _getVIPLeftTime() { + List<TextSpan> spanList = [const TextSpan(text: '鍓╀綑')]; + if (userInfo != null && userInfo!.vipExpireTime != null) { + int leftTime = + userInfo!.vipExpireTime! - DateTime.now().millisecondsSinceEpoch; + leftTime = leftTime ~/ 1000; + if (leftTime >= 60 * 60 * 24) { + spanList.add(TextSpan( + text: '${leftTime ~/ (60 * 60 * 24)}', + style: const TextStyle(fontWeight: FontWeight.bold))); + spanList.add(const TextSpan(text: '澶╁埌鏈�')); + } else if (leftTime >= 60 * 60) { + spanList.add(TextSpan( + text: '${leftTime ~/ (60 * 60)}', + style: const TextStyle(fontWeight: FontWeight.bold))); + spanList.add(const TextSpan(text: '灏忔椂鍒版湡')); + } else { + spanList.add(TextSpan( + text: '${leftTime ~/ (60)}', + style: const TextStyle(fontWeight: FontWeight.bold))); + spanList.add(const TextSpan(text: '鍒嗛挓鍒版湡')); + } + } + + return Text.rich(TextSpan( + children: spanList, + style: const TextStyle(color: Colors.white, fontSize: 17.5), + )); + } + + List<Widget> getVIPContent() { + List<Widget> list = []; + list.add(Image.asset( + "assets/imgs/mine/icon_vip.png", + width: 44, + )); + if (isVIP&&userInfo!=null) { + list.add(Container( + margin: const EdgeInsets.fromLTRB(6, 0, 0, 0), + child: _getVIPLeftTime())); + } else { + list.add(Container( + margin: const EdgeInsets.fromLTRB(6, 0, 0, 0), + child: Image.asset( + "assets/imgs/mine/label_vip.png", + height: 20, + ), + )); + } + return list; + } + + @override + Widget build(BuildContext context) { + _loadAd(); + return Scaffold( + backgroundColor: Colors.white, + body: SingleChildScrollView( + child: Column( + children: [ + Container( + decoration: const BoxDecoration( + image: DecorationImage( + image: + AssetImage("assets/imgs/mine/ic_mine_top_bg.png"), + fit: BoxFit.cover)), + height: 350, + width: 400, + padding: const EdgeInsets.all(10), + child: Column( + children: [ + //鐧诲綍涓庣敤鎴蜂俊鎭� + Container( + margin: EdgeInsets.fromLTRB(0, 105, 0, 0), + padding: EdgeInsets.all(15), + alignment: Alignment.centerLeft, + child: Container( + height: 50, + child: Flex( + direction: Axis.horizontal, + children: [ + Image.asset( + "assets/imgs/mine/icon_mine_default_portrait.png", + width: 50, + ), + //鐧诲綍鎸夐挳 + getLoginContentView(), + //淇℃伅 + ], + )), + height: 105, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.all(Radius.elliptical(10, 10)))), + //浼氬憳 + Container( + margin: EdgeInsets.fromLTRB(0, 10, 0, 0), + height: 105, + width: 400, + child: Stack( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(18.5, 0, 0, 0), + child: Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flex( + direction: Axis.horizontal, + crossAxisAlignment: CrossAxisAlignment.center, + children: getVIPContent(), + ), + Container( + margin: const EdgeInsets.fromLTRB(0, 8, 0, 0), + child: const Text( + "瀹氫綅瀹堟姢浜插弸锛岄粦绉戞妧淇濋┚鎶よ埅", + style: TextStyle( + fontSize: 15, color: Colors.white), + ), + ) + ], + ), + ), + Positioned( + right: 0, + top: 17, + child: InkWell( + onTap: () { + //鏌ョ湅璇︽儏鎴栧紑閫� + print(isVIP ? "鏌ョ湅璇︽儏" : "寮�閫氫細鍛�"); + UserUtil.isLogin().then((value) { + if (!value) { + NavigatorUtil.navigateToNextPage( + context, LoginPage(title: ""), + (data) { + _getUserInfo(); + }); + } + }); + }, + child: InkWell( + onTap: () { + UserUtil.isLogin().then((value) { + if (!value) { + NavigatorUtil.navigateToNextPage( + context, LoginPage(title: ""), + (data) { + _getUserInfo(); + }); + return; + } + + ConfigUtil.getConfig(context, + ConfigKey.vipLink) + .then((value) { + if (!StringUtil.isNullOrEmpty( + value)) { + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: "浼氬憳", + url: value!), (data) { + _getUserInfo(); + }); + } + }); + }); + }, + child: Container( + width: 99, + height: 26, + alignment: Alignment.center, + child: Text( + isVIP ? "鏌ョ湅璇︽儏" : "绔嬪嵆寮�閫�", + style: const TextStyle( + color: Color(0xFFD4A880)), + ), + decoration: const BoxDecoration( + color: Color(0xFFFAEAB9), + borderRadius: BorderRadius.only( + topLeft: Radius.circular(13), + bottomLeft: + Radius.circular(13)), + boxShadow: [ + BoxShadow( + color: Color(0x4D0E96FF), + blurRadius: 2.0, + offset: Offset(0.0, 3.0), + //闃村奖y杞村亸绉婚噺 + spreadRadius: 0 //闃村奖鎵╂暎绋嬪害 + ) + ], + ))))) + ], + ), + decoration: const BoxDecoration( + boxShadow: [ + BoxShadow( + color: Color(0x4D0E96FF), + blurRadius: 5.0, + offset: Offset(0.0, 8.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 0 //闃村奖鎵╂暎绋嬪害 + ) + ], + gradient: LinearGradient( + stops: [.5, 1], + colors: [Color(0xFF4699FF), Color(0xFF00DEFF)]), + color: Colors.white, + borderRadius: + BorderRadius.all(Radius.elliptical(10, 10)))) + ], + ), + ), + + Container( + child: adView ?? Container(), + margin: EdgeInsets.only(left: 10, right: 10), + ), + //骞垮憡 + + //鍔熻兘鍖哄煙 + Container( + height: 340, + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(10), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.elliptical(10, 10)), + color: Color(0xFFF4FFFF)), + child: GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, mainAxisSpacing: 10), + physics: const NeverScrollableScrollPhysics(), + children: list), + ), + ], + ))); + } + + @override + bool get wantKeepAlive => true; +} + +class Functions { + Functions(this.icon, this.name, this.key, this.needLogin); + + String icon; + String name; + String key; + bool needLogin; +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/about_us.dart b/src/main/resources/code/flutter_module/lib/ui/mine/about_us.dart new file mode 100644 index 0000000..895f09c --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/about_us.dart @@ -0,0 +1,166 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../ui/common/browser.dart'; +import '../../utils/config_util.dart'; +import '../../utils/share_preference.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/pageutils.dart'; + +import 'advice_submit.dart'; +import 'package:launch_review/launch_review.dart'; +import 'package:package_info/package_info.dart'; + +class AboutUsPage extends StatefulWidget { + AboutUsPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _AboutUsPageState createState() => _AboutUsPageState(); +} + +class _AboutUsPageState extends State<AboutUsPage> + with SingleTickerProviderStateMixin { + String versionName = ""; + + @override + void initState() { + super.initState(); + PackageInfo.fromPlatform().then((PackageInfo packageInfo) { + setState(() { + versionName = packageInfo.version; + }); + }); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: const Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFFFFFFFF), + body: Container( + child: Flex( + direction: Axis.vertical, + children: [ + TopNavBar(title: "鍏充簬鎴戜滑"), + const SizedBox( + height: 50, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12), + child: Image.asset( + "assets/imgs/icon.png", + height: 82, + ), + ), + const SizedBox( + height: 17, + ), + Text( + "鐗堟湰鍙凤細$versionName", + style: + const TextStyle(fontSize: 13, color: Color(0xFF999999)), + ) + ], + ), + const SizedBox( + height: 80, + ), + getItemView( + title: "鐢ㄦ埛鍗忚", + onTap: () { + NavigatorUtil.navigateToNextPage( + context, + BrowserPage(title: "鐢ㄦ埛鍗忚", url: Constant.PROTOCOL_URL), + (data) {}); + }), + getItemView( + title: "鑱旂郴鎴戜滑", + onTap: () { + MySharedPreferences.getInstance() + .getString("contactUsLink") + .then((value) { + if (StringUtil.isNullOrEmpty(value)) { + return; + } + + NavigatorUtil.navigateToNextPage(context, + BrowserPage(title: "鑱旂郴鎴戜滑", url: value!), (data) {}); + }); + }), + getItemView( + title: "娉ㄩ攢璐︽埛", + onTap: () { + MySharedPreferences.getInstance() + .getString("unRegisterLink") + .then((value) { + if (!StringUtil.isNullOrEmpty(value)) { + NavigatorUtil.navigateToNextPage(context, + BrowserPage(title: "璐︽埛娉ㄩ攢", url: value!), (data) {}); + } + }); + }), + ], + ), + )); + } + + Widget getItemView( + {required String title, required GestureTapCallback onTap}) { + return InkWell( + onTap: () { + onTap(); + }, + child: Container( + height: 40, + padding: const EdgeInsets.fromLTRB(25, 0, 25, 0), + margin: const EdgeInsets.fromLTRB(10, 0, 10, 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: TextStyle(fontSize: 15, color: const Color(0xFF00A8FF)), + ), + Expanded(child: Container()), + const Icon( + Icons.chevron_right, + color: Color(0xFF00A8FF), + ) + ], + ), + ), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/advice.dart b/src/main/resources/code/flutter_module/lib/ui/mine/advice.dart new file mode 100644 index 0000000..c6f17c0 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/advice.dart @@ -0,0 +1,153 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../utils/ui_constant.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/pageutils.dart'; + +import 'advice_submit.dart'; +import 'package:launch_review/launch_review.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '鎰忚鍙嶉', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: AdvicePage(title: ''), + ); + } +} + +class AdvicePage extends StatefulWidget { + AdvicePage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _AdvicePageState createState() => _AdvicePageState(); +} + +class _AdvicePageState extends State<AdvicePage> + with SingleTickerProviderStateMixin { + @override + void initState() { + super.initState(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Color(0xFFFFFFFF), + body: Container( + child: Flex( + direction: Axis.vertical, + children: [ + TopNavBar(title: "鎰忚鍙嶉"), + Image.asset("assets/imgs/advice/ic_advice_top.png"), + getItemView( + bgColor: Color(0xFFFFC945), + shadowColor: Color(0x4DFCA235), + title: "鎻愭剰瑙�", + subTitle: "鏈夊缓璁紝璇风暀瑷�", + icon: "assets/imgs/advice/icon_advice_msg.png", + onTap: () { + Navigator.of(context) + .push(CustomRouteSlide(AdviceSubmitPage(title: ""))); + }, + ), + getItemView( + bgColor: const Color(0xFF29D5FF), + shadowColor: const Color(0x4D0E96FF), + title: "缁欓紦鍔�", + subTitle: "鎮ㄧ殑榧撳姳鏄垜浠墠杩涚殑鍔ㄥ姏", + icon: "assets/imgs/advice/icon_advice_like.png", + onTap: () { + if (Platform.isIOS) { + LaunchReview.launch(iOSAppId: Constant.IOS_APP_ID); + } else { + LaunchReview.launch(); + } + }, + ), + ], + ), + )); + } + + Widget getItemView( + {required Color bgColor, + required Color shadowColor, + required String title, + required String subTitle, + required String icon, + required GestureTapCallback onTap}) { + return InkWell( + onTap: () { + onTap(); + }, + child: Container( + height: 92.5, + padding: EdgeInsets.fromLTRB(20, 0, 24, 0), + margin: EdgeInsets.fromLTRB(10, 12.5, 10, 4), + decoration: getItemDecoration(bgColor, shadowColor), + child: Flex( + direction: Axis.horizontal, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle(color: Colors.white, fontSize: 24), + ), + Text( + subTitle, + style: TextStyle(color: Colors.white, fontSize: 14), + ) + ], + ), + Expanded(child: Container()), + Image.asset( + icon, + height: 62, + ) + ], + ), + ), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/advice_submit.dart b/src/main/resources/code/flutter_module/lib/ui/mine/advice_submit.dart new file mode 100644 index 0000000..853d895 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/advice_submit.dart @@ -0,0 +1,198 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/feed_back_api.dart'; +import '../../utils/ui_constant.dart'; + +import '../../ui/widget/nav.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_utils.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '鎻愭剰瑙�', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: AdviceSubmitPage(title: ''), + ); + } +} + +class AdviceSubmitPage extends StatefulWidget { + AdviceSubmitPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _AdviceSubmitPageState createState() => _AdviceSubmitPageState(); +} + +class _AdviceSubmitPageState extends State<AdviceSubmitPage> + with SingleTickerProviderStateMixin { + List<String> questions = ["璐﹀彿闂", "瑙嗛鎾斁闂", "鍏朵粬闂"]; + + final TextEditingController _contentController = TextEditingController(); + + @override + void initState() { + super.initState(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: const Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Color(0xFFFFFFFF), + body: Container( + child: Flex( + direction: Axis.vertical, + children: [ + TopNavBar(title: "鎻愭剰瑙�"), + Container( + padding: const EdgeInsets.fromLTRB(17.5, 15, 17.5, 15), + decoration: BoxDecoration( + color: const Color(0xFFFAFAFA), + border: Border.all(color: Color(0xFFDBDBDB))), + child: Flex( + direction: Axis.vertical, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + children: getQuestionTypes(), + ), + TextField( + controller: _contentController, + maxLines: 9, + maxLength: 200, + decoration: const InputDecoration( + focusedBorder: InputBorder.none, + border: InputBorder.none, + counterStyle: TextStyle(color: Color(0xFF666666)), + hintText: "璇锋弿杩版偍瑕佸弽棣堢殑闂鎴栨剰瑙�", + hintStyle: TextStyle( + fontSize: 15, color: Color(0xFF999999))), + style: + const TextStyle(color: Color(0xFF666666), fontSize: 15), + onChanged: (value) {}, + ) + ], + ), + ), + //鎸夐挳 + Container( + margin: const EdgeInsets.fromLTRB(18, 50, 18, 0), + child: InkWell( + onTap: () { + var content = _contentController.text; + if (StringUtil.isNullOrEmpty(content)) { + ToastUtil.toast("璇峰~鍐欓棶棰�",context); + return; + } + var type; + if (checkIndex > -1) { + type = questions[checkIndex]; + } + + FeedBackApiUtil.advice(context, type, content).then((value) { + if (value == null) { + return; + } + if (value["code"] == 0) { + popPage(context); + } else { + ToastUtil.toast(value["msg"],context); + } + }); + }, + child: Container( + height: 42.5, + alignment: Alignment.center, + decoration: const BoxDecoration( + color: ColorConstant.theme, + borderRadius: BorderRadius.all(Radius.circular(10))), + child: const Text( + "鎻愪氦鍙嶉", + style: TextStyle(color: Colors.white, fontSize: 18), + ), + ), + ), + ) + ], + ), + )); + } + + int checkIndex = -1; + + List<Widget> getQuestionTypes() { + List<Widget> list = []; + + for (int i = 0; i < questions.length; i++) { + list.add(InkWell( + onTap: () { + if (checkIndex == i) { + setState(() { + checkIndex = -1; + }); + } else { + setState(() { + checkIndex = i; + }); + } + }, + child: Container( + padding: const EdgeInsets.fromLTRB(10, 5, 10, 5), + margin: const EdgeInsets.fromLTRB(0, 0, 17.5, 0), + // height: 35, + // width: 80, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(17.5), + border: Border.all( + color: i == checkIndex + ? ColorConstant.theme + : const Color(0xFFB4B4B4))), + child: Text( + questions[i], + style: TextStyle( + color: + i == checkIndex ? ColorConstant.theme : Color(0xFF666666), + fontSize: 14), + ), + ))); + } + + return list; + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/email_login.dart b/src/main/resources/code/flutter_module/lib/ui/mine/email_login.dart new file mode 100644 index 0000000..96e2016 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/email_login.dart @@ -0,0 +1,381 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx; +import '../../api/user_api.dart'; +import 'email_pwd_find.dart'; +import 'email_register.dart'; +import '../widget/nav.dart'; +import '../../utils/push_util.dart'; +import 'package:html/dom.dart' as dom; +import '../../api/http.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/button.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '鐧诲綍', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: EmailLoginPage(title: ''), + ); + } +} + +class EmailLoginPage extends StatefulWidget { + //闃块噷浜戜竴閿櫥褰� + static const messageChannel = + BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec()); + + EmailLoginPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _EmailLoginPageState createState() => _EmailLoginPageState(); +} + +class _EmailLoginPageState extends State<EmailLoginPage> + with SingleTickerProviderStateMixin { + bool oneKeyLogin = false; + bool checked = false; + TextEditingController? emailController = TextEditingController(); + TextEditingController? pwdController = TextEditingController(); + String phone = ""; + String code = ""; + Timer? timer; + + //閲嶆柊鍙戦�侀獙璇佺爜鍊掕鏃� + int? reSendSMSTimeLeft; + + @override + void initState() { + super.initState(); + reSendSMSTimeLeft = -1; + //鍒濆鍖栧井淇$櫥褰曠洃鍚� + fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) { + if (res is fluwx.WeChatAuthResponse) { + int errCode = res.errCode; + if (errCode == 0) { + String? code = res.code; + //TODO 鎶婂井淇$櫥褰曡繑鍥炵殑code浼犵粰鍚庡彴锛屽墿涓嬬殑浜嬪氨浜ょ粰鍚庡彴澶勭悊 + } else if (errCode == -4) { + //showToast("鐢ㄦ埛鎷掔粷鎺堟潈"); + } else if (errCode == -2) { + // showToast("鐢ㄦ埛鍙栨秷鎺堟潈"); + } + } + }); + } + + + + void qqLogin() async { + Map value = await UserUtil.loginQQ(); + } + + void _loginSuccess(UserInfo user) { + UserUtil.setUserInfo(user).then((value) { + print("鐧诲綍鎴愬姛"); + eventBus.fire(LoginEventBus(true)); + PushUtil.setAlias(user.id!.toString()).then((value) { + popPage(context); + }); + }); + } + + @override + void dispose() { + if (timer != null) { + timer!.cancel(); + } + super.dispose(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: const Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Colors.white, + body: Stack( + children: [ + Column(children: [ + //鐧诲綍鍐呭鍖哄煙 + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.fromLTRB(40, 120, 40, 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/ic_login_logo.png", + width: 131, + ), + Container( + height: 70, + ), + Container( + constraints: const BoxConstraints(minHeight: 180), + child: getLoginContent()), + Row( + children: [ + InkWell( + child: const Text( + "娉ㄥ唽", + style: TextStyle( + color: ColorConstant.theme), + ), + onTap: () { + NavigatorUtil.navigateToNextPage( + context, + EmailRegisterPage(title: ""), + (data) { + FocusScope.of(context).requestFocus(FocusNode()); + }); + }, + ), + Expanded(child: Container()), + InkWell( + child: const Text("鎵惧洖瀵嗙爜?", + style: TextStyle( + color: ColorConstant.theme)), + onTap: () { + NavigatorUtil.navigateToNextPage( + context, + EmailFindPwdPage(title: ""), + (data) { + FocusScope.of(context).requestFocus(FocusNode()); + }); + + }, + ), + ], + ) + ])))), + //鐢ㄦ埛鍗忚涓庨殣绉佹斂绛� + Row(children: [ + Container( + width: 25, + ), + RoundCheckBox( + value: checked, + onChanged: (value) { + setState(() { + checked = value; + }); + }, + ), + Expanded( + child: Container( + child: HtmlWidget( + "<p>鐧诲綍鍗宠〃鏄庡悓鎰� <a href='${Constant.PROTOCOL_URL}'>鐢ㄦ埛鍗忚</a> 鍜� <a href='${Constant.PRIVACY_URL}'>闅愮鏀跨瓥</a> </p>", + textStyle: + const TextStyle(color: Color(0xFF999999)), + customStylesBuilder: (element) { + // print(element.localName); + // if (element.localName == 'a') { + // return {'color': 'red'}; + // } + + return null; + }, onTapUrl: (url) { + String? title = ""; + if (url == Constant.PROTOCOL_URL) { + title = "鐢ㄦ埛鍗忚"; + } else { + title = "闅愮鏀跨瓥"; + } + + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: title, + url: url, + ), + (data) {}); + + return true; + }))), + ]) + ]), + //鍏抽棴鎸夐挳 + Positioned( + top: 30, + left: 20, + child: InkWell( + onTap: () { + popPage(context); + }, + child: const Icon( + Icons.close, + size: 30, + ))) + ], + )); + } + + Widget getLoginContent() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 5, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_email.png", + width: 17, + height: 15, + ), + Container(width: 14), + Expanded( + child: TextField( + style: const TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + phone = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.emailAddress, + controller: emailController, + maxLength: 60, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ偖绠�", + hintStyle: + TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_pwd.png", + width: 16.5, + ), + Container(width: 14), + Expanded( + child: TextField( + style:const TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + phone = value; + }); + }, + obscureText: true, + textAlign: TextAlign.start, + keyboardType: TextInputType.visiblePassword, + controller: pwdController, + maxLength: 32, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ュ瘑鐮�", + hintStyle: + TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 20), + MyFillButton( + "鐧� 褰�", + 10, + height: 45, + color: const Color(0xFFFF2B4B), + fontSize: 17, + enable: StringUtil.isEmail(emailController!.value.text) && + pwdController!.value.text.length >= 6, + + onClick: () { + if (!(StringUtil.isEmail(emailController!.value.text) && + pwdController!.value.text.length >= 6)) { + return; + } + + if (!checked) { + ToastUtil.toast("璇峰悓鎰忕敤鎴峰崗璁笌闅愮鏀跨瓥", context); + return; + } + + UserApiUtil.loginByEmail(context, emailController!.value.text, + pwdController!.value.text) + .then((value) { + print("缁撴灉锛� $value"); + if (value!["IsPost"] == "true") { + UserInfo user = UserInfo.fromJson(value["Data"]); + _loginSuccess(user); + } else { + ToastUtil.toast(value["Error"], context); + } + }); + }, + ), + ], + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/email_pwd_find.dart b/src/main/resources/code/flutter_module/lib/ui/mine/email_pwd_find.dart new file mode 100644 index 0000000..ab52252 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/email_pwd_find.dart @@ -0,0 +1,346 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx; +import '../../api/user_api.dart'; +import 'package:html/dom.dart' as dom; +import '../../api/http.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/button.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import '../widget/nav.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '娉ㄥ唽', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: EmailFindPwdPage(title: ''), + ); + } +} + +class EmailFindPwdPage extends StatefulWidget { + //闃块噷浜戜竴閿櫥褰� + static const messageChannel = + BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec()); + + EmailFindPwdPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _EmailFindPwdPageState createState() => _EmailFindPwdPageState(); +} + +class _EmailFindPwdPageState extends State<EmailFindPwdPage> + with SingleTickerProviderStateMixin { + TextEditingController? emailController = TextEditingController(); + TextEditingController? codeController = TextEditingController(); + TextEditingController? pwdController = TextEditingController(); + String email = ""; + String code = ""; + Timer? timer; + + //閲嶆柊鍙戦�侀獙璇佺爜鍊掕鏃� + int? reSendSMSTimeLeft; + + @override + void initState() { + super.initState(); + reSendSMSTimeLeft = -1; + } + + @override + void dispose() { + if (timer != null) { + timer!.cancel(); + } + super.dispose(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: const Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Colors.white, + body: Stack( + children: [ + Column(children: [ + //鐧诲綍鍐呭鍖哄煙 + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.fromLTRB(40, 120, 40, 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/ic_login_logo.png", + width: 131, + ), + Container( + height: 70, + ), + Container( + constraints: const BoxConstraints(minHeight: 200), + child: getLoginContent()), + Container( + height: 30, + ) + ])))), + ]), + //鍏抽棴鎸夐挳 + Positioned( + top: 30, + left: 20, + child: InkWell( + onTap: () { + popPage(context); + }, + child: const Icon( + Icons.arrow_back_ios_new, + size: 25, + ))) + ], + )); + } + + Widget getLoginContent() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 5, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_email.png", + width: 17, + height: 15, + ), + Container(width: 14), + Expanded( + child: TextField( + style: const TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.emailAddress, + controller: emailController, + maxLength: 60, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ偖绠�", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_code.png", + width: 16.5, + ), + Container(width: 14), + Expanded( + child: TextField( + style: const TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.phone, + controller: codeController, + maxLength: 8, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ獙璇佺爜", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + MyFillButton( + (reSendSMSTimeLeft! > 0 + ? (reSendSMSTimeLeft.toString() + "S閲嶆柊鑾峰彇") + : reSendSMSTimeLeft == -1 + ? "鑾峰彇楠岃瘉鐮�" + : "閲嶆柊鑾峰彇") + .toString(), + 10, + height: 34, + padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + enable: (reSendSMSTimeLeft! < 1 && + StringUtil.isEmail(emailController!.value.text)), + onClick: () { + if (!(reSendSMSTimeLeft! < 1 && + StringUtil.isEmail(emailController!.value.text))) { + return; + } + //鍙戦�侀獙璇佺爜 + UserApiUtil.sendEmailCode(context, emailController!.value.text) + .then((value) { + if (value != null && value["IsPost"] == "true") { + setState(() { + reSendSMSTimeLeft = 60; + //鍊掕鏃� + timer = + Timer.periodic(const Duration(seconds: 1), (timer) { + if (reSendSMSTimeLeft! > 0) { + setState(() { + reSendSMSTimeLeft = reSendSMSTimeLeft! - 1; + }); + } else { + timer.cancel(); + } + }); + }); + } else { + ToastUtil.toast(value!["Error"], context); + } + }); + }, + ), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_pwd.png", + width: 16.5, + ), + Container(width: 14), + Expanded( + child: TextField( + style: TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + obscureText: true, + textAlign: TextAlign.start, + keyboardType: TextInputType.visiblePassword, + controller: pwdController, + maxLength: 32, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ュ瘑鐮�", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 52), + MyFillButton( + "璁剧疆瀵嗙爜", + 10, + height: 45, + color: ColorConstant.theme, + fontSize: 17, + enable: StringUtil.isEmail(emailController!.value.text) && + codeController!.value.text.length >= 4 && + pwdController!.value.text.length >= 6, + onClick: () { + if (!(StringUtil.isEmail(emailController!.value.text) && + codeController!.value.text.length >= 4 && + pwdController!.value.text.length >= 6)) { + return; + } + + UserApiUtil.setPwd(context, emailController!.value.text, + pwdController!.value.text, codeController!.value.text) + .then((value) { + print("缁撴灉锛� $value"); + if (value!["IsPost"] == "true") { + ToastUtil.toast("瀵嗙爜璁剧疆鎴愬姛", context); + popPage(context); + }else{ + ToastUtil.toast(value["Error"], context); + } + }); + }, + ), + ], + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/email_register.dart b/src/main/resources/code/flutter_module/lib/ui/mine/email_register.dart new file mode 100644 index 0000000..260bc94 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/email_register.dart @@ -0,0 +1,421 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx; +import '../../api/user_api.dart'; +import 'package:html/dom.dart' as dom; +import '../../api/http.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/button.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import '../widget/nav.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '娉ㄥ唽', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: EmailRegisterPage(title: ''), + ); + } +} + +class EmailRegisterPage extends StatefulWidget { + //闃块噷浜戜竴閿櫥褰� + static const messageChannel = + BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec()); + + EmailRegisterPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _EmailRegisterPageState createState() => _EmailRegisterPageState(); +} + +class _EmailRegisterPageState extends State<EmailRegisterPage> + with SingleTickerProviderStateMixin { + bool checked = false; + TextEditingController? emailController = TextEditingController(); + TextEditingController? codeController = TextEditingController(); + TextEditingController? pwdController = TextEditingController(); + String email = ""; + String code = ""; + Timer? timer; + + //閲嶆柊鍙戦�侀獙璇佺爜鍊掕鏃� + int? reSendSMSTimeLeft; + + @override + void initState() { + super.initState(); + reSendSMSTimeLeft = -1; + //鍒濆鍖栧井淇$櫥褰曠洃鍚� + fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) { + if (res is fluwx.WeChatAuthResponse) { + int errCode = res.errCode; + if (errCode == 0) { + String? code = res.code; + //TODO 鎶婂井淇$櫥褰曡繑鍥炵殑code浼犵粰鍚庡彴锛屽墿涓嬬殑浜嬪氨浜ょ粰鍚庡彴澶勭悊 + } else if (errCode == -4) { + //showToast("鐢ㄦ埛鎷掔粷鎺堟潈"); + } else if (errCode == -2) { + // showToast("鐢ㄦ埛鍙栨秷鎺堟潈"); + } + } + }); + } + + void _loginSuccess(UserInfo user) { + UserUtil.setUserInfo(user).then((value) { + print("鐧诲綍鎴愬姛"); + eventBus.fire(LoginEventBus(true)); + PushUtil.setAlias(user.id!.toString()).then((value) { + popPage(context); + }); + }); + } + + @override + void dispose() { + if (timer != null) { + timer!.cancel(); + } + super.dispose(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: const Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Colors.white, + body: Stack( + children: [ + Column(children: [ + //鐧诲綍鍐呭鍖哄煙 + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.fromLTRB(40, 120, 40, 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/ic_login_logo.png", + width: 131, + ), + Container( + height: 70, + ), + Container( + constraints: + const BoxConstraints(minHeight: 200), + child: getLoginContent()), + Container( + height: 30, + ) + ])))), + //鐢ㄦ埛鍗忚涓庨殣绉佹斂绛� + Row(children: [ + Container( + width: 25, + ), + RoundCheckBox( + value: checked, + onChanged: (value) { + setState(() { + checked = value; + }); + }, + ), + Expanded( + child: HtmlWidget( + "<p>鐧诲綍鍗宠〃鏄庡悓鎰� <a href='${Constant.PROTOCOL_URL}'>鐢ㄦ埛鍗忚</a> 鍜� <a href='${Constant.PRIVACY_URL}'>闅愮鏀跨瓥</a> </p>", + textStyle: const TextStyle(color: Color(0xFF999999)), + onTapUrl: (url) { + String? title = ""; + if (url == Constant.PROTOCOL_URL) { + title = "鐢ㄦ埛鍗忚"; + } else { + title = "闅愮鏀跨瓥"; + } + + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: title, + url: url, + ), + (data) {}); + + return true; + })), + ]) + ]), + //鍏抽棴鎸夐挳 + Positioned( + top: 30, + left: 20, + child: InkWell( + onTap: () { + popPage(context); + }, + child: const Icon( + Icons.arrow_back_ios_new, + size: 25, + ))) + ], + )); + } + + Widget getLoginContent() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.fromLTRB(20, 0, 5, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_email.png", + width: 17, + height: 15, + ), + Container(width: 14), + Expanded( + child: TextField( + style: TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.emailAddress, + controller: emailController, + maxLength: 60, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ偖绠�", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_code.png", + width: 16.5, + ), + Container(width: 14), + Expanded( + child: TextField( + style: TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.phone, + controller: codeController, + maxLength: 8, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ獙璇佺爜", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + MyFillButton( + (reSendSMSTimeLeft! > 0 + ? (reSendSMSTimeLeft.toString() + "S閲嶆柊鑾峰彇") + : reSendSMSTimeLeft == -1 + ? "鑾峰彇楠岃瘉鐮�" + : "閲嶆柊鑾峰彇") + .toString(), + 10, + height: 34, + padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + enable: (reSendSMSTimeLeft! < 1 && + StringUtil.isEmail(emailController!.value.text)), + onClick: () { + if (!(reSendSMSTimeLeft! < 1 && + StringUtil.isEmail(emailController!.value.text))) { + return; + } + //鍙戦�侀獙璇佺爜 + UserApiUtil.sendEmailCode(context, emailController!.value.text) + .then((value) { + if (value != null && value["IsPost"] == "true") { + setState(() { + reSendSMSTimeLeft = 60; + //鍊掕鏃� + timer = + Timer.periodic(const Duration(seconds: 1), (timer) { + if (reSendSMSTimeLeft! > 0) { + setState(() { + reSendSMSTimeLeft = reSendSMSTimeLeft! - 1; + }); + } else { + timer.cancel(); + } + }); + }); + } else { + ToastUtil.toast(value!["Error"], context); + } + }); + }, + ), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_pwd.png", + width: 16.5, + ), + Container(width: 14), + Expanded( + child: TextField( + style: const TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + email = value; + }); + }, + obscureText: true, + textAlign: TextAlign.start, + keyboardType: TextInputType.visiblePassword, + controller: pwdController, + maxLength: 32, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ュ瘑鐮�", + hintStyle: TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 52), + MyFillButton( + "娉� 鍐�", + 10, + height: 45, + color: ColorConstant.theme, + fontSize: 17, + enable: StringUtil.isEmail(emailController!.value.text) && + codeController!.value.text.length >= 4 && + pwdController!.value.text.length >= 6, + onClick: () { + if (!(StringUtil.isEmail(emailController!.value.text) && + codeController!.value.text.length >= 4 && + pwdController!.value.text.length >= 6)) { + return; + } + + if (!checked) { + ToastUtil.toast("璇峰悓鎰忕敤鎴峰崗璁笌闅愮鏀跨瓥", context); + return; + } + + UserApiUtil.registerByEmail(context, emailController!.value.text, + pwdController!.value.text, codeController!.value.text) + .then((value) { + print("缁撴灉锛� $value"); + if (value!["IsPost"] == "true") { + ToastUtil.toast("娉ㄥ唽鎴愬姛", context); + //鍘荤櫥褰� + UserApiUtil.loginByEmail( + context, email, pwdController!.value.text) + .then((value) { + if (value!["IsPost"] == "true") { + UserInfo user = UserInfo.fromJson(value["Data"]); + _loginSuccess(user); + } + }); + } else { + ToastUtil.toast(value["Error"], context); + } + }); + }, + ), + ], + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/login.dart b/src/main/resources/code/flutter_module/lib/ui/mine/login.dart new file mode 100644 index 0000000..078d1ee --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/login.dart @@ -0,0 +1,496 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx; +import '../../api/user_api.dart'; +import 'package:html/dom.dart' as dom; +import '../../api/http.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/button.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import '../widget/nav.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '鐧诲綍', + theme: ThemeData(primaryColor: Color(0xFFF5F5F5)), + home: LoginPage(title: ''), + ); + } +} + +class LoginPage extends StatefulWidget { + //闃块噷浜戜竴閿櫥褰� + static const messageChannel = + BasicMessageChannel('AliyunPhoneNumberAuth', StandardMessageCodec()); + + LoginPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State<LoginPage> + with SingleTickerProviderStateMixin { + bool oneKeyLogin = false; + bool checked = false; + TextEditingController? phoneController = TextEditingController(); + TextEditingController? codeController = TextEditingController(); + String phone = ""; + String code = ""; + Timer? timer; + + //閲嶆柊鍙戦�侀獙璇佺爜鍊掕鏃� + int? reSendSMSTimeLeft; + + @override + void initState() { + super.initState(); + reSendSMSTimeLeft = -1; + //鍒濆鍖栧井淇$櫥褰曠洃鍚� + fluwx.weChatResponseEventHandler.distinct((a, b) => a == b).listen((res) { + if (res is fluwx.WeChatAuthResponse) { + int errCode = res.errCode; + if (errCode == 0) { + String? code = res.code; + //TODO 鎶婂井淇$櫥褰曡繑鍥炵殑code浼犵粰鍚庡彴锛屽墿涓嬬殑浜嬪氨浜ょ粰鍚庡彴澶勭悊 + } else if (errCode == -4) { + //showToast("鐢ㄦ埛鎷掔粷鎺堟潈"); + } else if (errCode == -2) { + // showToast("鐢ㄦ埛鍙栨秷鎺堟潈"); + } + } + }); + } + + bool _aliyunLogin = false; + + void aliyunOneKeyLogin() async { + if (_aliyunLogin) { + return; + } + _aliyunLogin = true; + Future.delayed(const Duration(milliseconds: 1000), () { + _aliyunLogin = false; + }); + + showLoading(context); + + Map value = + await LoginPage.messageChannel.send({"method": "checkEnv"}) as Map; + dismissDialog(context); + if (value["code"] == 0) { + value = + await LoginPage.messageChannel.send({"method": "startLogin"}) as Map; + print("杩斿洖鍐呭锛�${jsonEncode(value)}"); + if (value["code"] == 0) { + Map<String, dynamic>? resultValue = + await UserApiUtil.loginByPhone(context, "", "", value["token"]); + + LoginPage.messageChannel.send({"method": "closeLogin"}); + + if (resultValue == null) return; + + if (resultValue["code"] == 0) { + UserInfo user = UserInfo.fromJson(resultValue["data"]); + _loginSuccess(user); + } else { + ToastUtil.toast(value["msg"],context); + } + } else if (value["code"] == 700000) { + //Fluttertoast.showToast(msg: "鍙栨秷鐧诲綍浜�"); + } + } else { + ToastUtil.toast(value["msg"],context); + setState(() { + oneKeyLogin = false; + }); + } + } + + void qqLogin() async { + Map value = await UserUtil.loginQQ(); + } + + void _loginSuccess(UserInfo user) { + UserUtil.setUserInfo(user).then((value) { + print("鐧诲綍鎴愬姛"); + eventBus.fire(LoginEventBus(true)); + PushUtil.setAlias(user.id!.toString()).then((value) { + Navigator.pop(context); + }); + }); + } + + @override + void dispose() { + if (timer != null) { + timer!.cancel(); + } + super.dispose(); + } + + BoxDecoration getItemDecoration(Color bgColor, Color shadowColor) { + return BoxDecoration( + borderRadius: const BorderRadius.all(Radius.elliptical(10, 10)), + color: bgColor, + boxShadow: [ + BoxShadow( + color: shadowColor, + blurRadius: 2.0, + offset: Offset(0.0, 5.0), //闃村奖y杞村亸绉婚噺 + spreadRadius: 1 //闃村奖鎵╂暎绋嬪害 + ) + ]); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + resizeToAvoidBottomInset: false, + backgroundColor: Colors.white, + body: Stack( + children: [ + Column(children: [ + //鐧诲綍鍐呭鍖哄煙 + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.fromLTRB(40, 100, 40, 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/ic_login_logo.png", + width: 131, + ), + Container( + height: 70, + ), + Container( + constraints: BoxConstraints(minHeight: 200), + child: getLoginContent()), + Container( + height: 30, + ), + Text( + "鍏朵粬鏂瑰紡鐧诲綍", + style: TextStyle( + color: Color(0xFF666666), fontSize: 14), + ), + Container( + height: 21, + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceAround, + children: [ + // getThirdLoginItem("寰俊鐧诲綍", + // "assets/images/login/ic_login_wx.png"), + // getThirdLoginItem("QQ鐧诲綍", + // "assets/images/login/ic_login_qq.png"), + oneKeyLogin + ? getThirdLoginItem("鎵嬫満鍙风櫥褰�", + "assets/imgs/login/ic_login_phone.png") + : getThirdLoginItem("涓�閿櫥褰�", + "assets/imgs/login/ic_login_onekey.png"), + ], + ) + ])))), + //鐢ㄦ埛鍗忚涓庨殣绉佹斂绛� + Row(children: [ + Container( + width: 25, + ), + RoundCheckBox( + value: checked, + onChanged: (value) { + setState(() { + checked = value; + }); + }, + ), + Expanded( + child: Container( + child: HtmlWidget( + "<p>鐧诲綍鍗宠〃鏄庡悓鎰� <a href='${Constant.PROTOCOL_URL}'>鐢ㄦ埛鍗忚</a> 鍜� <a href='${Constant.PRIVACY_URL}'>闅愮鏀跨瓥</a> </p>", + textStyle: + const TextStyle(color: Color(0xFF999999)), + onTapUrl: (url) { + String? title = ""; + if (url == Constant.PROTOCOL_URL) { + title = "鐢ㄦ埛鍗忚"; + } else { + title = "闅愮鏀跨瓥"; + } + + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: title, + url: url, + ), + (data) {}); + + return true; + }))), + ]) + ]), + //鍏抽棴鎸夐挳 + Positioned( + top: 30, + left: 20, + child: InkWell( + onTap: () { + popPage(context); + }, + child: Icon( + Icons.close, + size: 30, + ))) + ], + )); + } + + Widget getLoginContent() { + return oneKeyLogin + ? Container( + child: Column(children: [ + MyFillButton( + "鏈満鍙风爜涓�閿櫥褰�", + 10, + height: 45, + fontSize: 17, + onClick: () { + aliyunOneKeyLogin(); + }, + ) + ])) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.fromLTRB(20, 0, 5, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_phone.png", + width: 14, + height: 20, + ), + Container(width: 14), + Expanded( + child: TextField( + style: TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + phone = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.phone, + controller: phoneController, + maxLength: 11, + decoration: InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ユ墜鏈哄彿", + hintStyle: + TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + ], + ), + ), + Container(height: 10), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.fromLTRB(20, 0, 7, 0), + decoration: BoxDecoration( + color: const Color(0xFFF5F5F5), + borderRadius: BorderRadius.circular(10)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + "assets/imgs/login/icon_code.png", + width: 16, + ), + Container(width: 14), + Expanded( + child: TextField( + style: TextStyle(color: Color(0xFF333333), fontSize: 17), + onChanged: (value) { + setState(() { + phone = value; + }); + }, + textAlign: TextAlign.start, + keyboardType: TextInputType.phone, + controller: codeController, + maxLength: 8, + decoration: const InputDecoration( + counterText: "", + hintText: "璇疯緭鍏ラ獙璇佺爜", + hintStyle: + TextStyle(color: Color(0xFFCCCCCC), fontSize: 17), + contentPadding: EdgeInsets.only(bottom: 3), + border: InputBorder.none, + focusedBorder: InputBorder.none, + ), + )), + MyFillButton( + (reSendSMSTimeLeft! > 0 + ? (reSendSMSTimeLeft.toString() + "S閲嶆柊鑾峰彇") + : reSendSMSTimeLeft == -1 + ? "鑾峰彇楠岃瘉鐮�" + : "閲嶆柊鑾峰彇") + .toString(), + 10, + height: 34, + padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + enable: (reSendSMSTimeLeft! < 1 && + StringUtil.isMobile(phoneController!.value.text)), + onClick: () { + if (!(reSendSMSTimeLeft! < 1 && + StringUtil.isMobile(phoneController!.value.text))) { + return; + } + //鍙戦�侀獙璇佺爜 + UserApiUtil.sendSMS( + context, phoneController!.value.text) + .then((value) { + if (value != null && value["code"] == 0) { + setState(() { + reSendSMSTimeLeft = 60; + //鍊掕鏃� + timer = Timer.periodic(const Duration(seconds: 1), + (timer) { + if (reSendSMSTimeLeft! > 0) { + setState(() { + reSendSMSTimeLeft = reSendSMSTimeLeft! - 1; + }); + } else { + timer.cancel(); + } + }); + }); + } else { + ToastUtil.toast(value!["msg"],context); + } + }); + }, + ), + ], + ), + ), + Container(height: 10), + const Text("鏈敞鍐岀殑鎵嬫満鍙锋敞鍐屽悗绯荤粺浼氳嚜鍔ㄥ垱寤鸿处鎴�", + style: TextStyle(color: Color(0xFFA0A0A0), fontSize: 12)), + Container(height: 20), + MyFillButton( + "鐧诲綍", + 10, + height: 45, + color: const Color(0xFFFF2B4B), + fontSize: 17, + enable: StringUtil.isMobile(phoneController!.value.text) && + codeController!.value.text.length >= 4, + onClick: () { + if (!(StringUtil.isMobile(phoneController!.value.text) && + codeController!.value.text.length >= 4)) { + return; + } + + if (!checked) { + ToastUtil.toast("璇峰悓鎰忕敤鎴峰崗璁笌闅愮鏀跨瓥",context); + return; + } + + UserApiUtil.loginByPhone(context, phoneController!.value.text, + codeController!.value.text, "") + .then((value) { + print("缁撴灉锛� $value"); + if (value!["code"] == 0) { + UserInfo user = UserInfo.fromJson(value["data"]); + _loginSuccess(user); + } else { + ToastUtil.toast(value["msg"],context); + } + }); + }, + ), + ], + ); + } + + Widget getThirdLoginItem(String name, String iconAsset) { + return InkWell( + onTap: () { + if (name == "涓�閿櫥褰�" || name == "鎵嬫満鍙风櫥褰�") { + setState(() { + oneKeyLogin = !oneKeyLogin; + }); + } else if (name == "QQ鐧诲綍") { + qqLogin(); + } else if (name == "寰俊鐧诲綍") { + UserUtil.loginWX(); + } + }, + child: Container( + constraints: BoxConstraints(minWidth: 80), + child: Column( + children: [ + Image.asset( + iconAsset, + height: 49, + ), + Container( + height: 8, + ), + Text( + name, + style: const TextStyle(color: Color(0xFF9DAAB3), fontSize: 12), + ) + ], + )), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/person_info.dart b/src/main/resources/code/flutter_module/lib/ui/mine/person_info.dart new file mode 100644 index 0000000..8fb2a50 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/person_info.dart @@ -0,0 +1,536 @@ +import 'dart:ui'; +import 'dart:io'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/user_api.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/image_util.dart'; +import '../../api/http.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/cache_util.dart'; +import '../../utils/config_util.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/setting_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; +import 'package:image_picker/image_picker.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '涓汉淇℃伅', + theme: ThemeData(primaryColor: const Color(0xFFF5F5F5)), + home: PersonInfoPage(title: ''), + ); + } +} + +class PersonInfoPage extends StatefulWidget { + PersonInfoPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _PersonInfoPageState createState() => _PersonInfoPageState(); +} + +class _PersonInfoPageState extends State<PersonInfoPage> + with SingleTickerProviderStateMixin { + UserInfo? _user; + + String? editPortrait; + String? editNickName; + int? editSex; + DateTime? editBirthday; + String? editSign; + + @override + void initState() { + super.initState(); + UserUtil.getUserInfo().then((value) { + setState(() { + _user = value; + }); + }); + } + + void selectImg() async { + File? image = await ImageUtil.selectAndCropImage(); + if (image == null) { + return; + } + //image.path + setState(() { + editPortrait = image.path; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFDADADA), + body: _user == null + ? Container() + : Column( + children: [ + TopNavBar( + title: "涓汉淇℃伅", + rightText: canSave() ? "淇濆瓨" : null, + rightClick: canSave() + ? () { + //淇濆瓨 + UserApiUtil.updateUserInfo(context, + nickName: editNickName, + sex: editSex, + birthDay: editBirthday == null + ? null + : "${editBirthday!.year}/${editBirthday!.month}/${editBirthday!.day}", + sign: editSign, + portraitPath: editPortrait) + .then((value) { + if (value == null) { + return; + } + + if (value["IsPost"] == "true") { + ToastUtil.toast("淇敼鎴愬姛", context); + popPage(context); + } else { + ToastUtil.toast(value["Error"], context); + } + }); + } + : null, + ), + Expanded( + child: SingleChildScrollView( + child: Column(children: [ + Container( + height: 10, + color: const Color(0xFFDADADA), + ), + getPortraitItemView(onClick: () { + //閫夋嫨澶村儚 + selectImg(); + }), + getCommonItemView( + title: "鏄电О", + content: editNickName ?? _user!.nickname!, + onClick: () { + showEditNickName(editNickName ?? _user!.nickname!); + }), + getCommonItemView( + title: "鎬у埆", + content: + _getSexDesc((editSex ?? int.parse(_user!.sex!))), + onClick: () { + showEditSex(editSex ?? int.parse(_user!.sex!)); + }), + getCommonItemView( + title: "鐢熸棩", + content: _getBirthDay(), + onClick: () { + showEditBirthDay((editBirthday ?? + ((_user!.birthday == null || + _user!.birthday!.isEmpty) + ? null + : parseDate(_user!.birthday!)))); + }), + getCommonItemView( + title: "閭", + content: _user!.email!, + onClick: () {}, + canIn: false), + getCommonItemView( + title: "ID", + content: _user!.id!, + onClick: () {}, + canIn: false), + Container( + height: 10, + color: const Color(0xFFDADADA), + ), + getSignItemView(onClick: () { + showEditSign(editSign ?? _user!.sign); + }), + ]))), + ], + )); + } + + bool canSave() { + return editNickName != null || + editSex != null || + editBirthday != null || + editSign != null || + editPortrait != null; + } + + String _getSexDesc(int? sex) { + switch (sex) { + case 1: + return "鐢�"; + case 2: + return "濂�"; + } + return "鏈煡"; + } + + String _getBirthDay() { + DateTime? date = (editBirthday ?? + ((_user!.birthday == null || _user!.birthday!.isEmpty) + ? null + : parseDate(_user!.birthday!))); + if (date == null) { + return "璇烽�夋嫨鍑虹敓鏃ユ湡"; + } + + return "${date.year}/${date.month}/${date.day}"; + } + + DateTime parseDate(String date) { + List<String> sts = date.split("/"); + + return DateTime(int.parse(sts[0]), int.parse(sts[1]), int.parse(sts[2])); + } + + Widget getCommonItemView( + {required String title, + String content = "", + required GestureTapCallback onClick, + bool canIn = true}) { + return Container( + height: 53, + margin: const EdgeInsets.fromLTRB(0, 0, 0, 1), + color: Colors.white, + child: InkWell( + onTap: () { + onClick(); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Flex( + crossAxisAlignment: CrossAxisAlignment.center, + direction: Axis.horizontal, + children: [ + Text( + title, + style: + const TextStyle(fontSize: 16, color: Color(0xFF333333)), + ), + Expanded( + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + child: Text(content, + style: const TextStyle( + fontSize: 14, color: Color(0xFF959595))), + margin: const EdgeInsets.fromLTRB(0, 0, 13.5, 0), + ), + canIn + ? Image.asset( + "assets/imgs/icon_person_info_input.png", + height: 13.5, + ) + : Container() + ], + )) + ], + )), + ), + ); + } + + Widget getPortraitItemView({required GestureTapCallback onClick}) { + return Container( + height: 75, + margin: const EdgeInsets.fromLTRB(0, 0, 0, 1), + color: Colors.white, + child: InkWell( + onTap: () { + onClick(); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Flex( + crossAxisAlignment: CrossAxisAlignment.center, + direction: Axis.horizontal, + children: [ + const Text( + "澶村儚", + style: TextStyle(fontSize: 16, color: Color(0xFF333333)), + ), + Expanded( + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + child: ClipRRect( + borderRadius: BorderRadius.circular(60), + child: editPortrait == null + ? CommonImage( + _user!.portrait!, + height: 57, + width: 57, + ) + : Image.file( + File(editPortrait!), + fit: BoxFit.cover, + height: 57, + width: 57, + )), + margin: const EdgeInsets.fromLTRB(0, 0, 13.5, 0), + ), + Image.asset( + "assets/imgs/icon_person_info_input.png", + height: 13.5, + ) + ], + )) + ], + )), + ), + ); + } + + Widget getSignItemView({required GestureTapCallback onClick}) { + return Container( + height: 200, + margin: const EdgeInsets.fromLTRB(0, 0, 0, 1), + color: Colors.white, + child: InkWell( + onTap: () { + onClick(); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Container( + height: 18, + ), + Flex( + crossAxisAlignment: CrossAxisAlignment.center, + direction: Axis.horizontal, + children: [ + const Text( + "涓�х鍚�", + style: TextStyle(fontSize: 16, color: Color(0xFF333333)), + ), + Expanded( + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Image.asset( + "assets/imgs/icon_person_info_input.png", + height: 13.5, + ) + ], + )) + ], + ), + Container( + height: 21, + ), + Text( + _getSign(), + textAlign: TextAlign.start, + style: const TextStyle(color: Color(0xFFBEBEBE), fontSize: 15), + ) + ]), + ), + )); + } + + _getSign() { + return editSign ?? + ((_user!.sign == null || _user!.sign!.isEmpty) + ? "杩欎釜浜哄緢鎳掞紝浠�涔堜篃娌℃湁鐣欎笅" + : _user!.sign!); + } + + showAlertDilaog(String title, Widget content, VoidCallback sure, + {VoidCallback? cancel}) { + showCupertinoDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(title), + content: content, + actions: <Widget>[ + TextButton( + child: Text('鍙栨秷'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: Text('纭畾'), + onPressed: () { + sure(); + }, + ), + ], + ); + }); + } + + void showEditNickName(String nickName) { + TextEditingController controller = TextEditingController(); + controller.text = nickName; + showAlertDilaog( + "淇敼鏄电О", + TextField( + controller: controller, + ), () { + if (controller.text.isEmpty) { + return; + } + Navigator.of(context).pop(); + setState(() { + editNickName = controller.text; + }); + }); + } + + void showEditSex(int sex) { + int? tempSex; + setState(() { + tempSex = sex; + }); + DialogUtil.showDialogBottom( + context, + Container( + margin: const EdgeInsets.all(10), + alignment: Alignment.bottomCenter, + child: Stack(alignment: Alignment.bottomCenter, children: [ + Container( + margin: const EdgeInsets.only(bottom: 50), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10)), + child: Container( + height: 100, + alignment: Alignment.center, + child: Column( + children: [ + InkWell( + onTap: () { + setState(() { + editSex = 1; + }); + Navigator.of(context).pop(); + }, + child: Container( + height: 50, + alignment: Alignment.center, + child: const Text( + "鐢�", + style: TextStyle( + fontSize: 18, + color: ColorConstant.theme), + ))), + InkWell( + onTap: () { + setState(() { + editSex = 2; + }); + Navigator.of(context).pop(); + }, + child: Container( + height: 50, + alignment: Alignment.center, + child: const Text( + "濂�", + style: TextStyle( + fontSize: 18, + color: ColorConstant.theme), + ))), + ], + ))), + MyFillButton( + "鍙栨秷", + 20, + height: 40, + color: Colors.grey, + fontSize: 14, + onClick: () { + popPage(context); + }, + ) + ]))); + } + + //缂栬緫鐢熸棩 + void showEditBirthDay(DateTime? dateTime) async { + dateTime ??= DateTime.now(); + Locale myLocale = Localizations.localeOf(context); + DateTime? date = await showDatePicker( + context: context, + initialDate: dateTime, + firstDate: DateTime(1900), + lastDate: DateTime(DateTime.now().year + 1), + locale: myLocale); + if (date != null) { + setState(() { + editBirthday = date; + }); + } + } + + void showEditSign(String? sign) { + sign ??= ""; + TextEditingController controller = TextEditingController(); + controller.text = sign; + showAlertDilaog( + "缂栬緫绛惧悕", + TextField( + controller: controller, + maxLines: 5, + maxLength: 200, + decoration: const InputDecoration( + focusedBorder: InputBorder.none, + border: InputBorder.none, + counterStyle: TextStyle(color: Color(0xFF666666)), + hintText: "璇疯緭鍏ョ鍚嶅唴瀹�", + hintStyle: TextStyle(fontSize: 15, color: Color(0xFF999999))), + ), () { + if (controller.text.isEmpty) { + return; + } + + Navigator.of(context).pop(); + setState(() { + editSign = controller.text; + }); + }); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/mine/settings.dart b/src/main/resources/code/flutter_module/lib/ui/mine/settings.dart new file mode 100644 index 0000000..0e42acb --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/mine/settings.dart @@ -0,0 +1,363 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/user_api.dart'; +import '../../utils/share_preference.dart'; +import '../../api/http.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/cache_util.dart'; +import '../../utils/config_util.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/setting_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '璁剧疆', + theme: ThemeData(primaryColor: const Color(0xFFF5F5F5)), + home: SettingPage(title: ''), + ); + } +} + +class SettingPage extends StatefulWidget { + SettingPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _SettingPageState createState() => _SettingPageState(); +} + +class _SettingPageState extends State<SettingPage> + with SingleTickerProviderStateMixin { + bool msg = false; + bool ad = false; + bool login = false; + + String cacheSize = "0B"; + String version = ""; + + @override + void initState() { + super.initState(); + UserUtil.isLogin().then((value) { + setState(() { + login = value; + }); + }); + + _getCacheSize(); + _getVersion(); + + SettingUtil.isEnablePush().then((value) { + setState(() { + msg = value; + }); + }); + + SettingUtil.isEnableRecommendAd().then((value) { + ad = value; + }); + } + + ///鑾峰彇缂撳瓨澶у皬 + void _getCacheSize() async { + int byteSize = await CacheUtil.total(); + String? desc; + if (byteSize < 1000) { + desc = byteSize.toString() + "KB"; + } else if (byteSize < 1000 * 1000) { + desc = (byteSize / 1000).toStringAsFixed(0) + "KB"; + } else if (byteSize < 1000 * 1000 * 1000) { + desc = (byteSize / (1000 * 1000)).toStringAsFixed(1) + "MB"; + } else { + desc = (byteSize / (1000 * 1000 * 1000)).toStringAsFixed(1) + "GB"; + } + + setState(() { + cacheSize = desc!; + }); + } + + void _getVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + setState(() { + version = packageInfo.version; + }); + } + + Future logout() async { + Map<String, dynamic>? map = { + "code": 0 + }; //await UserApiUtil.logout(context, uid!); + if (map["code"] == 0) { + UserUtil.logout().then((value) { + setState(() { + login = false; + }); + ToastUtil.toast("閫�鍑烘垚鍔�", context); + UserUtil.getUid(); + // popPage(context); + }); + + } else { + ToastUtil.toast(map["msg"], context); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F5F5), + body: Flex( + direction: Axis.vertical, + children: [ + TopNavBar(title: "璁剧疆"), + getBigItemView( + title: "鎺ㄩ�佸厤鎵撴壈", + content: "鍏抽棴鍚庯紝21:00-09:00涓嶆帴鍙椾换浣曟帹閫�", + marginTop: 14, + marginBottom: 1, + checked: msg, + changed: (bool value) { + SettingUtil.setPush(value).then((value1) { + setState(() { + msg = value; + }); + }); + }), + getBigItemView( + title: "涓�у寲骞垮憡鎺ㄨ崘", + content: "鍏抽棴鍚庯紝骞垮憡鏁伴噺灏嗕笉鍙橈紝浣嗗唴瀹圭浉鍏冲害浼氶檷浣�", + marginTop: 0, + marginBottom: 16, + checked: ad, + changed: (bool value) { + SettingUtil.setRecommendAd(value).then((value1) { + setState(() { + ad = value; + }); + }); + }), + getCommonItemView( + title: "娓呯悊缂撳瓨", + content: cacheSize, + onClick: () { + showCupertinoDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("鏄惁娓呮缂撳瓨"), + actions: <Widget>[ + TextButton( + child: const Text('鍙栨秷'), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text('纭畾'), + onPressed: () { + CacheUtil.clear().then((value) { + ToastUtil.toast("缂撳瓨娓呴櫎鎴愬姛", context); + _getCacheSize(); + }); + Navigator.of(context).pop(); + }, + ), + ], + ); + }); + }), + getCommonItemView( + title: "妫�鏌ユ洿鏂�", + content: "鐗堟湰鍙凤細$version", + onClick: () { + ToastUtil.toast("宸茬粡鏄渶鏂扮増鏈�", context); + }), + getCommonItemView( + title: "璐︽埛娉ㄩ攢", + content: "", + onClick: () { + + MySharedPreferences.getInstance().getString("unRegisterLink").then((value) { + if (!StringUtil.isNullOrEmpty(value)) { + NavigatorUtil.navigateToNextPage(context, + BrowserPage(title: "璐︽埛娉ㄩ攢", url: value!), (data) {}); + } + }); + }), + getCommonItemView( + title: "闅愮鎶曡瘔", + content: "", + onClick: () { + MySharedPreferences.getInstance().getString("feedBackLink").then((value) { + if (!StringUtil.isNullOrEmpty(value)) { + NavigatorUtil.navigateToNextPage(context, + BrowserPage(title: "闅愮鎶曡瘔", url: value!), (data) {}); + } + }); + }), + // getCommonItemView( + // title: "绗笁鏂筍DK鍒楄〃", + // content: "", + // onClick: () { + // + // ConfigUtil.getConfig(ConfigKey.sdkList).then((value) { + // if (!StringUtil.isNullOrEmpty(value)) { + // NavigatorUtil.navigateToNextPage( + // context, BrowserPage(title: "绗笁鏂筍DK鍒楄〃", url: value!), (data) {}); + // } + // }); + // }), + Expanded( + child: Container(), + ), + login + ? Container( + child: Stack( + children: [ + InkWell( + onTap: () { + print("閫�鍑虹櫥褰�"); + logout(); + }, + child: Container( + alignment: Alignment.center, + height: 48, + color: Colors.white, + child: const Text( + "閫�鍑虹櫥褰�", + style: TextStyle( + color: ColorConstant.theme, fontSize: 16), + ))) + ], + )) + : Container(), + ], + )); + } + + Widget getCommonItemView( + {required String title, + String content = "", + required GestureTapCallback onClick}) { + return Container( + height: 53, + margin: const EdgeInsets.fromLTRB(0, 0, 0, 1), + color: Colors.white, + child: InkWell( + onTap: () { + onClick(); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Flex( + crossAxisAlignment: CrossAxisAlignment.center, + direction: Axis.horizontal, + children: [ + Text( + title, + style: + const TextStyle(fontSize: 16, color: Color(0xFF333333)), + ), + Expanded( + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + child: Text(content, + style: const TextStyle( + fontSize: 14, color: Color(0xFF959595))), + margin: const EdgeInsets.fromLTRB(0, 0, 13.5, 0), + ), + Image.asset( + "assets/imgs/common/icon_array_right.png", + height: 13.5, + ) + ], + )) + ], + )), + ), + ); + } + + Widget getBigItemView( + {required String title, + String content = "", + bool checked = false, + required ValueChanged<bool> changed, + double marginTop = 0, + double marginBottom = 0}) { + return Container( + height: 68, + margin: EdgeInsets.fromLTRB(0, marginTop, 0, marginBottom), + color: Colors.white, + child: Container( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Stack(children: [ + Flex( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + direction: Axis.vertical, + children: [ + Text( + title, + style: TextStyle(fontSize: 16, color: Color(0xFF333333)), + ), + Container( + child: Text(content, + style: const TextStyle( + fontSize: 9, color: Color(0xFF959595))), + margin: const EdgeInsets.fromLTRB(0, 5, 0, 0), + ) + ], + ), + Container(), + Positioned( + right: 0, + top: 0, + bottom: 0, + child: InkWell( + onTap: () { + changed(!checked); + }, + child: Image.asset( + checked + ? "assets/imgs/common/icon_check_true.png" + : "assets/imgs/common/icon_check_false.png", + height: 30, + width: 60, + ))) + ])), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/search/search.dart b/src/main/resources/code/flutter_module/lib/ui/search/search.dart new file mode 100644 index 0000000..3e494fc --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/search/search.dart @@ -0,0 +1,369 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/video_api.dart'; +import '../../ui/search/search_result.dart'; +import '../../ui/widget/ad_express.dart'; +import '../../ui/widget/search_widget.dart'; +import '../../utils/ad_util.dart'; +import '../../utils/video/search_record_util.dart'; + +import '../../utils/pageutils.dart'; + +class SearchPage extends StatefulWidget { + SearchPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _SearchPageState createState() => _SearchPageState(); +} + +class _SearchPageState extends State<SearchPage> + with AutomaticKeepAliveClientMixin { + final SugguestSearchController _sugguestSearchController = + SugguestSearchController(); + + bool showClose = false; + List<SearchKwModel> hotSearchList = []; + List<String> recordList = []; + AdType? adType; + + @override + void initState() { + super.initState(); + AdUtil.getAdType(AdPosition.videoSearch).then((value) { + setState(() { + adType = value; + }); + }); + + getHotSearch(); + setData(); + } + + void fromBack() { + FocusScope.of(context).requestFocus(FocusNode()); + setData(); + } + + void setData() async { + List<String> tempList = await SearchRecordUtil.listRecord(); + setState(() { + recordList = tempList; + }); + } + + void getHotSearch() async { + Map<String, dynamic>? result = await SearchApiUtil.getHotSearch(context); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + List<SearchKwModel> tempList = + List.filled(list.length, SearchKwModel(0, "")); + int row = + list.length % 2 == 0 ? (list.length ~/ 2) : ((list.length + 1) ~/ 2); + for (var i = 0; i < list.length; i++) { + if (i + 1 > row) { + tempList[(i + 1 - row) * 2 - 1] = SearchKwModel(i + 1, list[i]); + } else { + tempList[(i + 1) * 2 - 2] = SearchKwModel(i + 1, list[i]); + } + } + + // mList.sort((a, b) => a.index.compareTo(b.index)); + + setState(() { + hotSearchList = tempList; + }); + } + } + + void getSuggestSearch(String key) async { + Map<String, dynamic>? result = + await SearchApiUtil.getSuggestSearch(context, key); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + + List<String> suggestList = []; + list.forEach((element) { + suggestList.add(element); + }); + + _sugguestSearchController.setData!(suggestList); + if (suggestList.isNotEmpty) { + _sugguestSearchController.setShow!(true); + } else { + _sugguestSearchController.setShow!(false); + } + } else { + _sugguestSearchController.setData!([]); + _sugguestSearchController.setShow!(false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Stack(children: [ + Column( + children: [ + Container( + height: MediaQuery.of(context).viewPadding.top, + ), + Container( + height: 5, + ), + //鎼滅储鏍� + SearchBar( + hint: widget.title, + onChange: (content) { + if (content.isEmpty) { + _sugguestSearchController.setData!([]); + _sugguestSearchController.setShow!(false); + } else { + getSuggestSearch(content); + } + }, + onSubmit: (content) { + NavigatorUtil.navigateToNextPage( + context, + SearchResultPage( + title: content.isNotEmpty ? content : widget.title), + (data) { + fromBack(); + }); + }, + ), + //鍐呭 + Expanded( + child: SingleChildScrollView( + child: Column(children: [ + recordList.isNotEmpty + ? Container( + padding: const EdgeInsets.fromLTRB(12, 20, 12, 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text("鍘嗗彶鎼滅储", + style: TextStyle( + color: Color(0xFFA6A6A6), fontSize: 15)), + Expanded(child: Container()), + InkWell( + onTap: () { + SearchRecordUtil.clearRecord(); + setState(() { + recordList = []; + }); + }, + child: const Icon( + Icons.delete_outline, + color: Color(0xFFA6A6A6), + )) + ])) + : Container(), + Container( + padding: const EdgeInsets.fromLTRB(12, 7, 12, 10), + alignment: Alignment.topLeft, + child: Wrap( + spacing: 13.5, + runSpacing: 15.5, + alignment: WrapAlignment.start, + children: + recordList.map((e) => getRecordItem(e)).toList())), + + ///鐑棬鎼滅储 + Container( + margin: const EdgeInsets.fromLTRB(12, 20, 12, 10), + alignment: Alignment.center, + padding: const EdgeInsets.fromLTRB(15, 15, 15, 25), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + border: Border.all(color: Color(0xFFFFD7E4), width: 0.5), + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFFFFCEDE), + Color(0xFFFFFFFF), + Color(0xFFFFFFFF) + ])), + child: Column( + children: [ + const Text( + "鐑棬鎼滅储", + style: TextStyle(color: Color(0xFFFF558D)), + ), + Container( + height: 23, + ), + Wrap( + alignment: WrapAlignment.spaceBetween, + runSpacing: 11, + spacing: 10, + children: hotSearchList + .map((e) => getHotItem(e.index, e.kw)) + .toList()) + ], + ), + ), + + ///骞垮憡 + adType == null + ? Container() + : Container( + alignment: Alignment.center, + height: MediaQuery.of(context).size.width * 0.8, + child: _nativeAdView()) + ]))) + ], + ), + SugguestSearchView( + contentList: [], + sugguestSearchController: _sugguestSearchController, + onCancel: (value) { + _sugguestSearchController.setShow!(false); + }, + onItemClick: (value) { + _sugguestSearchController.setShow!(false); + NavigatorUtil.navigateToNextPage( + context, SearchResultPage(title: value), (data) { + fromBack(); + }); + }, + ) + ])); + } + + Widget _nativeAdView() { + return adType == AdType.csj + ? CSJEXpressAd( + CSJADConstant.PID_VIDEO_SEARCH, + MediaQuery.of(context).size.width - 20, + (MediaQuery.of(context).size.width - 20) * 0.8, + close: () { + setState(() { + adType = null; + }); + }, + loadFail: () { + setState(() { + adType = null; + }); + }, + ) + : GDTEXpressAd( + GDTADConstant.PID_VIDEO_SEARCH, + MediaQuery.of(context).size.width - 20, + (MediaQuery.of(context).size.width - 20) * 0.8, + close: () { + setState(() { + adType = null; + }); + }, + loadFail: () { + setState(() { + adType = null; + }); + }, + ); + } + + @override + bool get wantKeepAlive => true; + + Widget getRecordItem(String text) { + return InkWell( + onTap: () { + NavigatorUtil.navigateToNextPage( + context, SearchResultPage(title: text), (data) { + fromBack(); + }); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(23, 8, 23, 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), color: Color(0xFFF7F7F7)), + child: Text( + text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(color: Colors.black, fontSize: 12), + ), + )); + } + + Widget getHotItem(int index, String text) { + Color indexColor = const Color(0xFFD5D5D5); + switch (index) { + case 1: + indexColor = const Color(0xFFFF558D); + break; + case 2: + indexColor = const Color(0xFFFED73C); + break; + case 3: + indexColor = const Color(0xFF3CD7FE); + break; + } + return InkWell( + onTap: () { + NavigatorUtil.navigateToNextPage( + context, SearchResultPage(title: text), (data) { + fromBack(); + }); + }, + child: SizedBox( + width: (MediaQuery.of(context).size.width - 70) / 2, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 17, + height: 17, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), color: indexColor), + child: Text( + "$index", + style: const TextStyle(color: Colors.white, fontSize: 11), + ), + ), + Container( + width: 12.5, + ), + Expanded( + child: Text( + text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: const Color(0xFF4A4A4A), fontSize: 12), + )) + ], + ), + )); + } +} + +class SearchKwModel { + int index; + String kw; + + SearchKwModel(this.index, this.kw); +} diff --git a/src/main/resources/code/flutter_module/lib/ui/search/search_result.dart b/src/main/resources/code/flutter_module/lib/ui/search/search_result.dart new file mode 100644 index 0000000..149edbf --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/search/search_result.dart @@ -0,0 +1,286 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/video_api.dart'; +import '../../model/video/video_model.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/search_widget.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/video/search_record_util.dart'; +import '../../ui/mine/settings.dart'; +import '../../ui/mine/login.dart'; +import '../../ui/mine/advice.dart'; +import '../../api/http.dart'; +import '../../model/user/user_info.dart'; +import '../../ui/common/browser.dart'; +import '../../utils/config_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; + +class SearchResultPage extends StatefulWidget { + SearchResultPage({Key? key, required this.title}) : super(key: key); + final String title; + + @override + _SearchResultPageState createState() => _SearchResultPageState(); +} + +class _SearchResultPageState extends State<SearchResultPage> + with TickerProviderStateMixin { + final SugguestSearchController _sugguestSearchController = + SugguestSearchController(); + final SearchController _searchController=SearchController(); + final MyRefreshController _refreshController = MyRefreshController(); + bool showClose = false; + int page = 1; + bool hasMore = true; + int? type; + List<VideoType> typeList = []; + String? kw; + List<VideoInfoModel> videoList = []; + + TabController? _tabController; + + VoidCallback? listener; + + @override + void initState() { + super.initState(); + kw = widget.title; + + listener = () { + if(type!=null){ + if(type==typeList[_tabController!.index].id){ + return; + } + } + setState(() { + type = typeList[_tabController!.index].id; + }); + page = 1; + search(kw!, page); + }; + + _tabController = TabController(length: typeList.length, vsync: this); + } + + void initTab() { + if (_tabController != null) { + _tabController!.removeListener(listener!); + } + setState(() { + _tabController = TabController(length: typeList.length, vsync: this); + }); + _tabController!.addListener(listener!); + } + + void search(String content, int _page) async { + setState(() { + kw = content; + }); + + SearchRecordUtil.addRecord(content); + page = _page; + if (page == 1) { + setState(() { + videoList = []; + }); + } + Map<String, dynamic>? result = + await SearchApiUtil.search(context, content, _page, type); + //璇锋眰澶辫触浜� + if (result == null) { + if (page > 1) { + page = page - 1; + } + return; + } + + if (result["IsPost"] == "true") { + if (type == null && page == 1) { + List<dynamic> list = result["Data"]["typeList"]; + List<VideoType> tempVideoTypeTypeList = []; + list.forEach((element) { + tempVideoTypeTypeList.add(VideoType.fromJson(element)); + }); + setState(() { + typeList = tempVideoTypeTypeList; + }); + + initTab(); + } + + List<dynamic> list = result["Data"]["data"]; + List<VideoInfoModel> tempList = []; + for (var element in list) { + VideoInfoModel videoInfo = VideoInfoModel.fromJson(element); + //澶勭悊涓棿鍓ч泦 + List<VideoDetailInfo> detailList = videoInfo.videoDetailList!; + if (detailList.length > 5) { + detailList = [ + detailList[0], + detailList[1], + VideoDetailInfo(tag: "..."), + detailList[detailList.length - 2], + detailList[detailList.length - 1] + ]; + } + videoInfo.videoDetailList = detailList; + tempList.add(videoInfo); + } + hasMore = tempList.isNotEmpty; + setState(() { + if (_page == 1) { + videoList = tempList; + } else { + if (tempList.isNotEmpty) { + videoList.addAll(tempList); + } + } + }); + + if (_page == 1) { + hasMore = true; + } + + _refreshController.refreshCompleted(); + + if (!hasMore) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + } + } + + void getSuggestSearch(String key) async { + Map<String, dynamic>? result = + await SearchApiUtil.getSuggestSearch(context, key); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + + List<String> suggestList = []; + list.forEach((element) { + suggestList.add(element); + }); + + _sugguestSearchController.setData!(suggestList); + if (suggestList.isNotEmpty) { + _sugguestSearchController.setShow!(true); + } else { + _sugguestSearchController.setShow!(false); + } + } else { + _sugguestSearchController.setData!([]); + _sugguestSearchController.setShow!(false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Stack(children: [ + Column( + children: [ + Container( + height: MediaQuery.of(context).viewPadding.top, + ), + Container( + height: 5, + ), + //鎼滅储鏍� + SearchBar( + text: kw, + onChange: (content) { + if (content.isEmpty) { + _sugguestSearchController.setData!([]); + _sugguestSearchController.setShow!(false); + } else { + getSuggestSearch(content); + } + }, + onSubmit: (content) { + type = null; + search(content, 1); + }, + searchController: _searchController, + ), + Container( + height: 5, + ), + + ///鍐呭 + /// + Container( + alignment: Alignment.centerLeft, + child: TabBar( + padding: EdgeInsets.only(top: 5, bottom: 15, left: 2), + isScrollable: true, + indicatorSize: TabBarIndicatorSize.label, + labelColor: ColorConstant.theme, + labelStyle: const TextStyle(fontSize: 16), + labelPadding: EdgeInsets.only(left: 8, right: 8), + unselectedLabelColor: const Color(0xFF666666), + indicatorColor: ColorConstant.theme, + tabs: typeList.map((e) => Text(e.name!)).toList(), + controller: _tabController, + )), + + Expanded( + child: RefreshListView( + content: ListView.builder( + itemBuilder: (BuildContext context, int index) { + VideoInfoModel video = videoList[index]; + if (video.showType == 1) { + return VideoListUIUtil.getSearchVideoAlbum( + context, video); + } else { + return VideoListUIUtil.getSearchVideoCommon( + context, video); + } + }, + padding: const EdgeInsets.only(left: 0, right: 0, top: 0), + itemCount: videoList.length, + ), + refreshController: _refreshController, + refresh: () { + page = 1; + search(kw!, page); + }, + loadMore: () { + search(kw!, page + 1); + }, + ), + ) + ], + ), + SugguestSearchView( + contentList: [], + sugguestSearchController: _sugguestSearchController, + onCancel: (value) { + _sugguestSearchController.setShow!(false); + }, + onItemClick: (value) { + FocusScope.of(context).requestFocus(FocusNode()); + _searchController.setData!(value); + _sugguestSearchController.setShow!(false); + type = null; + search(value, 1); + }, + ) + ])); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/src/main/resources/code/flutter_module/lib/ui/test.dart b/src/main/resources/code/flutter_module/lib/ui/test.dart new file mode 100644 index 0000000..f808be9 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/test.dart @@ -0,0 +1,45 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class SplashPage extends StatefulWidget { + SplashPage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + _SplashPageState createState() => _SplashPageState(); +} + +class _SplashPageState extends State<SplashPage> + with SingleTickerProviderStateMixin { + int index = 1; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp(home: WillPopScope( + onWillPop: () async { + return false; + }, + child: Scaffold( + backgroundColor: Colors.red, + body: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("$index"), + InkWell( + onTap: () { + setState(() { + index += 1; + }); + }, + child: const Text("鐐瑰嚮浜嬩欢"), + ) + ], + )))); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_attention_list.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_attention_list.dart new file mode 100644 index 0000000..bd34859 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_attention_list.dart @@ -0,0 +1,228 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/video_api.dart'; +import '../../model/video/video_model.dart'; +import '../../model/video/watch_record_model.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/db_manager.dart'; +import '../../utils/video/video_util.dart'; +import '../../api/http.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/cache_util.dart'; +import '../../utils/config_util.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/setting_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; + +class VideoAttentionPage extends StatefulWidget { + VideoAttentionPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _VideoAttentionPageState createState() => _VideoAttentionPageState(); +} + +class _VideoAttentionPageState extends State<VideoAttentionPage> + with SingleTickerProviderStateMixin { + final MyRefreshController _refreshController = MyRefreshController(); + List<VideoInfoModel> videoList = []; + + int page = 1; + int toalCount = 0; + + @override + void initState() { + super.initState(); + } + + void loadVideoList(int _page) async { + page = _page; + Map<String, dynamic>? result = + await VideoApiUtil.getAttentionVideoList(context, page); + if (result == null) { + if (page == 1) { + _refreshController.apiError!(); + } + return; + } + + if (result["IsPost"] != "true") { + return; + } + + List<dynamic> list = result["Data"]["data"]; + List<VideoInfoModel> temList = []; + list.forEach((element) { + temList.add(VideoInfoModel.fromJson(element["VideoInfo"])); + }); + + int count = int.parse(result["Data"]["count"]); + setState(() { + toalCount = count; + }); + if (page == 1) { + setState(() { + videoList = temList; + }); + } else { + setState(() { + videoList.addAll(temList); + }); + } + + _refreshController.refreshCompleted(); + if (count >= videoList.length) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + if (videoList.isEmpty) { + _refreshController.dataEmpty!(); + } + } + + void deleteVideo(index) async { + Map<String, dynamic>? result = + await VideoApiUtil.cancelAttentionVideo(context, videoList[index].id!); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + setState(() { + videoList.removeAt(index); + }); + ToastUtil.toast("鍒犻櫎鎴愬姛", context); + if (videoList.isEmpty) { + loadVideoList(1); + } + } else { + ToastUtil.toast(result["Error"], context); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + TopNavBar( + title: "鎴戠殑杩藉墽", + ), + Container( + height: DimenUtil.getOnePixel(context), + color: const Color(0xFFDBDBDB), + ), + Expanded( + child: RefreshListView( + content: ListView.builder( + padding: const EdgeInsets.only(top: 10), + itemBuilder: (BuildContext context, int index) { + return getItem(index); + }, + itemCount: videoList.length, + ), + refreshController: _refreshController, + refresh: () { + loadVideoList(1); + }, + loadMore: () { + loadVideoList(page + 1); + }, + )), + ], + )); + } + + Widget getItem(index) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.fromLTRB(10, 8, 10, 8), + height: 80, + child: Stack( + children: [ + InkWell( + onTap: () { + jumpVideoDetail(context, videoList[index], false); + }, + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + VideoUtil.getHPicture(videoList[index]), + fit: BoxFit.cover, + height: 80, + width: 80 * 1.68, + )), + Container( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoList[index].name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFF232323), fontSize: 15), + ), + Text( + videoList[index].tag!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFFB8AFB5), fontSize: 12), + ), + Expanded(child: Container()), + const Text( + "", + style: + TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ) + ], + )) + ], + )), + Positioned( + width: 60, + right: 10, + top: 0, + bottom: 0, + child: Container( + alignment: Alignment.centerRight, + child: MyFillButton( + "鍙栨秷杩藉墽", + 5, + onClick: () { + deleteVideo(index); + }, + ))) + ], + )); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_collected_list.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_collected_list.dart new file mode 100644 index 0000000..fd96c3c --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_collected_list.dart @@ -0,0 +1,296 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/video_api.dart'; +import '../../model/video/video_model.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/video/video_util.dart'; + +import '../../ui/widget/nav.dart'; +import '../../utils/ui_utils.dart'; + +class VideoCollectedPage extends StatefulWidget { + VideoCollectedPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _VideoCollectedPageState createState() => _VideoCollectedPageState(); +} + +class _VideoCollectedPageState extends State<VideoCollectedPage> + with SingleTickerProviderStateMixin { + bool editMode = false; + Set<String> selectedSet = {}; + + final MyRefreshController _refreshController = MyRefreshController(initialRefresh: false); + List<VideoInfoModel> videoList = []; + + int page = 1; + int toalCount = 0; + + @override + void initState() { + super.initState(); + loadVideoList(1); + } + + + void loadVideoList(int _page) async { + page = _page; + Map<String, dynamic>? result = + await VideoApiUtil.getCollelctedVideos(context, page); + if (result == null) { + if (page == 1) { + _refreshController.apiError!(); + } + return; + } + + if (result["IsPost"] != "true") { + return; + } + + List<dynamic> list = result["Data"]["data"]; + List<VideoInfoModel> temList = []; + list.forEach((element) { + temList.add(VideoInfoModel.fromJson(element)); + }); + + int count = int.parse(result["Data"]["count"]); + setState(() { + toalCount = count; + }); + if (page == 1) { + setState(() { + videoList = temList; + }); + } else { + setState(() { + videoList.addAll(temList); + }); + } + + _refreshController.refreshCompleted(); + if (count >= videoList.length) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + if (videoList.isEmpty) { + _refreshController.dataEmpty!(); + } else { + //姝e父鐨勭姸鎬� + _refreshController.dataNormal!(); + } + } + + void deleteVideo() async { + if (selectedSet.isEmpty) { + return; + } + String videoIds = selectedSet.join(","); + + Map<String, dynamic>? result = + await VideoApiUtil.collelctVideo(context, videoIds, false); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + ToastUtil.toast("鍒犻櫎鎴愬姛", context); + setState(() { + selectedSet.clear(); + }); + loadVideoList(1); + } else { + ToastUtil.toast(result["Error"], context); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + TopNavBar( + title: "鎴戠殑鏀惰棌", + rightIcon: videoList.isNotEmpty + ? const Icon( + Icons.format_list_bulleted, + size: 25, + color: Color(0xFF202020), + ) + : null, + rightClick: () { + setState(() { + editMode = !editMode; + if (!editMode) { + selectedSet.clear(); + } + _refreshController.setEditMode!(editMode); + }); + }, + ), + Container( + height: DimenUtil.getOnePixel(context), + color: const Color(0xFFDBDBDB), + ), + Expanded( + child: RefreshListView( + content: ListView.builder( + padding: const EdgeInsets.only(top: 10), + itemBuilder: (BuildContext context, int index) { + return getItem(index); + }, + itemCount: videoList.length, + ), + refreshController: _refreshController, + refresh: () { + loadVideoList(1); + }, + loadMore: () { + loadVideoList(page + 1); + }, + )), + editMode && videoList.isNotEmpty + ? Container( + padding: const EdgeInsets.fromLTRB(28, 10, 28, 10), + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Color(0xFFDBDBDB), width: 0.5))), + child: Row( + children: [ + Expanded( + child: MyOutlineButton( + videoList.length == selectedSet.length ? "鍙嶉��" : "鍏ㄩ��", + 8, + height: 34, + fontSize: 16, + color: const Color(0xFFdbdbdb), + textColor: const Color(0xFF202020), + onClick: () { + if (videoList.length == selectedSet.length) { + setState(() { + selectedSet.clear(); + }); + } else { + Set<String> list = + videoList.map((e) => e.id.toString()).toSet(); + setState(() { + selectedSet = list; + }); + } + }, + )), + Container( + width: 20, + ), + Expanded( + child: MyFillButton( + selectedSet.isNotEmpty + ? ("鍒犻櫎(${selectedSet.length})") + : "鍒犻櫎", + 8, + height: 34, + fontSize: 16, + onClick: () { + deleteVideo(); + }, + )), + ], + ), + ) + : Container() + ], + )); + } + + Widget getItem(index) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.fromLTRB(10, 8, 10, 8), + height: 80, + child: InkWell( + child: Row( + children: [ + Stack( + alignment: Alignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + VideoUtil.getHPicture(videoList[index]), + fit: BoxFit.cover, + height: 80, + width: 80 * 1.68, + )), + (editMode + ? Image.asset( + selectedSet.contains("${videoList[index].id}") + ? "assets/imgs/video/icon_check_true.png" + : "assets/imgs/video/icon_check_false.png", + width: 35, + ) + : Container()) + ], + ), + Container( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoList[index].name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: + const TextStyle(color: Color(0xFF232323), fontSize: 15), + ), + Text( + videoList[index].tag!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: + TextStyle(color: const Color(0xFFB8AFB5), fontSize: 12), + ), + Expanded(child: Container()), + const Text( + "", + style: TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ) + ], + )) + ], + ), + onTap: () { + if (!editMode) { + return; + } + setState(() { + if (selectedSet.contains("${videoList[index].id}")) { + selectedSet.remove("${videoList[index].id}"); + } else { + selectedSet.add("${videoList[index].id}"); + } + }); + }, + )); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_detail.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_detail.dart new file mode 100644 index 0000000..ea92d3c --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_detail.dart @@ -0,0 +1,935 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import '../../api/video_api.dart'; +import '../../model/video/video_attention_model.dart'; +import '../../model/video/video_model.dart'; +import '../../model/video/video_play_url_model.dart'; +import '../../ui/mine/email_login.dart'; +import '../../ui/video/video_player_browser.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/ad_util.dart'; +import '../../utils/db_manager.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/share_preference.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +import '../../ui/widget/nav.dart'; + +class VideoDetailPage extends StatefulWidget { + VideoDetailPage( + {Key? key, this.videoId, this.videoInfo, required this.position}) + : super(key: key); + final String? videoId; + final VideoInfoModel? videoInfo; + final int position; + + @override + _VideoDetailPageState createState() => _VideoDetailPageState(); +} + +class _VideoDetailPageState extends State<VideoDetailPage> + with SingleTickerProviderStateMixin { + TabController? _tabController; + List tabs = [" 鍓ч泦 ", " 绠�浠� "]; + int _playPosition = 0; + VideoDetailStatus _status = VideoDetailStatus.going; + + VideoResource? _selectedVideoResource; + + VideoInfoModel? _videoInfoModel; + + VideoInfoModel? _simpleVideo; + + List<VideoInfoModel> relativeVideoList = []; + + VideoAttentionModel? videoAttention; + + //鏄惁宸茬粡鏀惰棌 + bool collected = false; + + //鏄惁灞曞紑鍓ч泦 + bool episodeExpand = false; + + bool adShown = false; + + final MyRefreshController _refreshController = + MyRefreshController(initialRefresh: false); + final ScrollController _scrollController = ScrollController(); + + int episodePage = 1; + bool episodeHasMore = true; + + String applink = ""; + + @override + void initState() { + super.initState(); + if (Platform.isAndroid) { + WebView.platform = SurfaceAndroidWebView(); + } + _playPosition = widget.position; + _simpleVideo = widget.videoInfo; + _tabController = TabController(length: tabs.length, vsync: this); + if (Platform.isIOS) { + MySharedPreferences.getInstance().getString("appLink").then((value) { + setState(() { + applink = value!; + }); + }); + } + + getVideoDetail(_simpleVideo!.id); + } + + void getVideoDetail(videoId) async { + setState(() { + _status = VideoDetailStatus.going; + //鍒濆鍖栫姸鎬� + episodePage = 1; + episodeHasMore = true; + episodeExpand = false; + }); + + Map<String, dynamic>? result = await VideoApiUtil.getVideoDetail( + context, + videoId, + _selectedVideoResource == null ? null : _selectedVideoResource!.id); + + if (result == null) { + setState(() { + _status = VideoDetailStatus.fail; + }); + return; + } + + if (result["IsPost"] == "true") { + VideoInfoModel temp = VideoInfoModel.fromJson(result["Data"]["Video"]); + if (temp.videoDetailList!.length <= _playPosition) { + setState(() { + _playPosition = 0; + }); + } + + setState(() { + _videoInfoModel = temp; + _status = VideoDetailStatus.success; + }); + _videoInfoModel!.resourceList!.forEach((element) { + if (element.checked!) { + setState(() { + _selectedVideoResource = element; + }); + } + }); + if (_selectedVideoResource == null) { + setState(() { + _selectedVideoResource = _videoInfoModel!.resourceList![0]; + }); + } + //瑙f瀽杩藉墽鏁版嵁 + if (result["Data"]["Attention"] != null) { + VideoAttentionModel temp = + VideoAttentionModel.fromJson(result["Data"]["Attention"]); + setState(() { + videoAttention = temp; + }); + } else { + setState(() { + videoAttention = null; + }); + } + + if (result["Data"]["AdInfo"] != null) { + if (result["Data"]["AdInfo"]["FullVideo"] && !adShown) { + AdUtil.loadFullScreenAd( + AdType.csj, CSJADConstant.PID_VIDEO_DETAIL_FULLSCREEN); + adShown = true; + } + } + + getRelativeVideos(videoId); + _isCollectedVideo(); + } else { + ToastUtil.toast(result["Error"], context); + popPage(context); + } + } + + void getEpisodeList(videoId, _page) async { + episodePage = _page; + Map<String, dynamic>? result = await VideoApiUtil.getVideoEpisodeList( + context, + videoId, + _selectedVideoResource == null ? null : _selectedVideoResource!.id, + _page); + if (result == null || result["IsPost"] != "true") { + //鍥為��椤电爜 + if (episodePage > 1) { + episodePage--; + } + return; + } + List<dynamic> list = result["Data"]["list"]; + episodeHasMore = result["Data"]["hasMore"]; + List<VideoDetailInfo> tempList = []; + list.forEach((element) { + tempList.add(VideoDetailInfo.fromJson(element)); + }); + setState(() { + _videoInfoModel!.videoDetailList!.addAll(tempList); + }); + + if (!episodeHasMore) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + } + + void getRelativeVideos(videoId) async { + Map<String, dynamic>? result = + await VideoApiUtil.getRelativeVideos(context, videoId); + if (result == null) { + return; + } + + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["data"]; + + List<VideoInfoModel> tempList = []; + + list.forEach((element) { + tempList.add(VideoInfoModel.fromJson(element)); + }); + setState(() { + relativeVideoList = tempList; + }); + } + } + + void getPlayUrl(position) async { + setState(() { + _playPosition = position; + }); + Map<String, dynamic>? result = await VideoApiUtil.getPlayUrl( + context, + _videoInfoModel!.id!, + _videoInfoModel!.videoDetailList![position].id!, + _selectedVideoResource!.id!); + + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + //娣诲姞瑙傜湅璁板綍 + _videoInfoModel!.tag = _simpleVideo!.tag; + DBManager.addWatchRecord(_videoInfoModel!, + _videoInfoModel!.videoDetailList![_playPosition], _playPosition); + + VideoPlayUrlModel temp = VideoPlayUrlModel.fromJson(result["Data"]); + if (temp.playType == 1) { + NavigatorUtil.navigateToNextPage( + context, + VideoPlayerWebPage( + title: _videoInfoModel!.videoDetailList![_playPosition].name!, + url: temp.url!), + (data) {}); + } + } + } + + String getPosterImg() { + if (_videoInfoModel == null) { + if (!StringUtil.isNullOrEmpty(widget.videoInfo!.hpicture)) { + return widget.videoInfo!.hpicture!; + } else { + return widget.videoInfo!.picture!; + } + } else { + if (!StringUtil.isNullOrEmpty(_videoInfoModel!.playPicture)) { + return _videoInfoModel!.playPicture!; + } + if (!StringUtil.isNullOrEmpty(_videoInfoModel!.hpicture)) { + return _videoInfoModel!.hpicture!; + } + return _videoInfoModel!.picture!; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + Container( + height: MediaQuery.of(context).viewPadding.top, + ), + //灏侀潰 + AspectRatio( + aspectRatio: 1.7778, + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 0, + right: 0, + top: 0, + bottom: 0, + child: VideoImage(getPosterImg()), + ), + // Positioned( + // left: 0, + // right: 0, + // top: 0, + // bottom: 0, + // child: _getPlayer(), + // ), + Positioned( + top: 10, + left: 10, + child: InkWell( + onTap: () { + popPage(context); + }, + child: Image.asset( + "assets/imgs/video/ic_play_back.png", + width: 29, + height: 29, + ))), + Positioned( + width: 60, + height: 60, + child: InkWell( + onTap: () { + getPlayUrl(_playPosition); + }, + child: + Image.asset("assets/imgs/video/ic_play.png"))) + ], + )), + + _status == VideoDetailStatus.success + ? Expanded( + child: Stack(children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack(alignment: Alignment.bottomLeft, children: [ + Container( + color: const Color(0xFFEEEEEE), + height: 2, + ), + Container( + margin: const EdgeInsets.only(left: 34), + alignment: Alignment.topLeft, + child: TabBar( + isScrollable: true, + labelPadding: + const EdgeInsets.fromLTRB(10, 10, 10, 10), + indicatorColor: const Color(0xFFFF558D), + labelColor: const Color(0xFFFF558D), + labelStyle: const TextStyle(fontSize: 15), + unselectedLabelColor: + const Color(0xFFF999999), + indicatorWeight: 4, + indicatorSize: TabBarIndicatorSize.label, + controller: _tabController, + tabs: tabs.map((e) => Text(e)).toList(), + )) + ]), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + SingleChildScrollView( + child: _getVideoEpisodeView(), + ), + _getVideoDescView() + ])) + ]), + //閫夐泦娴眰 + _videoInfoModel != null && episodeExpand + ? Container( + color: Colors.white, + child: Stack(children: [ + RefreshListView( + enablePullDown: false, + refreshController: _refreshController, + loadMore: () { + getEpisodeList( + _simpleVideo!.id, episodePage + 1); + }, + content: GridView.builder( + padding: const EdgeInsets.only( + top: 45, left: 10, right: 10, bottom: 10), + itemCount: + _videoInfoModel!.videoDetailList!.length, + itemBuilder: + (BuildContext context, int index) { + return _getEpisodeItem( + _videoInfoModel! + .videoDetailList![index], + index); + }, + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + //妯酱鍏冪礌涓暟 + crossAxisCount: + _videoInfoModel!.showType == 2 + ? 5 + : 2, + //绾佃酱闂磋窛 + mainAxisSpacing: 10.0, + //妯酱闂磋窛 + crossAxisSpacing: 10.0, + //瀛愮粍浠跺楂橀暱搴︽瘮渚� + childAspectRatio: + _videoInfoModel!.showType == 2 + ? 1.6 + : 2), + ), + ), + Positioned( + right: 0, + top: 0, + left: 0, + child: Container( + height: 40, + color: Colors.white, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + "閫夐泦", + style: TextStyle(fontSize: 16), + ), + Positioned( + right: 0, + child: InkWell( + onTap: () { + setState(() { + episodeExpand = false; + }); + }, + child: const Icon( + Icons.close, + color: Colors.black54, + ))) + ]))) + ]), + ) + : Container() + ])) + : Expanded(child: getStatusView()) + ], + )); + } + + Widget getStatusView() { + if (_status == VideoDetailStatus.going) { + return const SpinKitCircle( + color: ColorConstant.theme, + size: 60.0, + ); + } else if (_status == VideoDetailStatus.fail) { + return InkWell( + onTap: () { + getVideoDetail(_simpleVideo!.id); + }, + child: Container( + alignment: Alignment.center, + child: const Text( + "鍔犺浇澶辫触锛岀偣鍑婚噸璇�", + textAlign: TextAlign.center, + style: TextStyle(color: Colors.grey, fontSize: 18), + ))); + } + return Container(); + } + + void _collectVideo(bool collect) async { + if (!await UserUtil.isLogin()) { + NavigatorUtil.navigateToNextPage( + context, EmailLoginPage(title: ""), (data) {}); + return; + } + + Map<String, dynamic>? result = + await VideoApiUtil.collelctVideo(context, _simpleVideo!.id!, collect); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + ToastUtil.toast(collect ? "鏀惰棌鎴愬姛" : "鍙栨秷鏀惰棌鎴愬姛", context); + } + setState(() { + collected = collect; + }); + } + + void _isCollectedVideo() async { + if (!await UserUtil.isLogin()) { + return; + } + + Map<String, dynamic>? result = + await VideoApiUtil.isCollelctedVideo(context, _simpleVideo!.id!); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + setState(() { + collected = true; + }); + } else { + collected = false; + } + } + + void attentionClick() async { + bool login = await UserUtil.isLogin(); + if (!login) { + NavigatorUtil.navigateToNextPage( + context, EmailLoginPage(title: ""), (data) {}); + return; + } + + if (videoAttention!.attention!) { + //鍙栨秷鍏虫敞 + Map<String, dynamic>? result = + await VideoApiUtil.cancelAttentionVideo(context, _simpleVideo!.id!); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + ToastUtil.toast("鍙栨秷杩藉墽鎴愬姛", context); + setState(() { + videoAttention!.attention = false; + }); + } else { + ToastUtil.toast(result["Error"], context); + } + } else { + //鍏虫敞 + Map<String, dynamic>? result = + await VideoApiUtil.addAttentionVideo(context, _simpleVideo!.id!); + if (result == null) { + return; + } + if (result["IsPost"] == "true") { + ToastUtil.toast("杩藉墽鎴愬姛", context); + setState(() { + videoAttention!.attention = true; + }); + } else { + ToastUtil.toast(result["Error"], context); + } + } + } + + + + Widget _getPlayer() { + return WebView( + //http://192.168.3.122:8848/test/JsTest.html + initialUrl: "https://jx.parwix.com:4433/player/?url=https://v.youku.com/v_show/id_XNTg0ODEyODc0NA==.html", + navigationDelegate: (NavigationRequest request) { + if (!request.url.startsWith("http")) { + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + ); + } + + //鍓ч泦 + Widget _getVideoEpisodeView() { + return Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(12, 20, 12, 10), + child: + Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + _videoInfoModel!.name!, + style: const TextStyle(color: Colors.black, fontSize: 18), + )), + Container( + width: 10, + ), + _selectedVideoResource != null + ? InkWell( + onTap: () { + if (_videoInfoModel == null) { + return; + } + + DialogUtil.showDialogBottom( + context, + //璧勬簮閫夋嫨寮规 + _getResourceListDialog(), + borderRadius: const BorderRadius.only( + topRight: Radius.circular(10), + topLeft: Radius.circular(10))); + }, + child: Container( + padding: + const EdgeInsets.fromLTRB(9.5, 10, 8.5, 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: Color(0xFFBBBBBB), + width: DimenUtil.getOnePixel(context))), + child: Row(children: [ + Text.rich( + TextSpan( + text: "鏉ユ簮锛�", + style: const TextStyle( + color: Color(0xFF999999), fontSize: 9), + children: [ + TextSpan( + text: _selectedVideoResource!.name, + style: const TextStyle( + color: Color(0xFF666666), + fontSize: 11)) + ]), + ), + const Icon( + Icons.keyboard_arrow_down, + color: Color(0xFFE0E0E0), + ), + ]), + )) + : Container() + ], + ), + _videoInfoModel!.score != null && + _videoInfoModel!.score!.isNotEmpty + ? Text.rich( + TextSpan( + text: "璇勫垎锛�", + style: const TextStyle( + color: Color(0xFF999999), fontSize: 14), + children: [ + TextSpan( + text: _videoInfoModel!.score!, + style: const TextStyle( + color: Color(0xFFFB9F00), + fontSize: 14, + fontWeight: FontWeight.bold), + ) + ]), + ) + : Container(), + Container( + height: 30, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + onTap: () { + _collectVideo(!collected); + }, + child: Column(children: [ + Image.asset( + collected + ? "assets/imgs/video/icon_collected.png" + : "assets/imgs/video/icon_uncollected.png", + width: 22, + ), + Container( + height: 5, + ), + const Text( + "鏀惰棌", + style: + TextStyle(color: Color(0xFF9D9D9D), fontSize: 10), + ) + ])), + InkWell( + onTap: () { + ToastUtil.toast("鏆備笉鏀寔涓嬭浇", context); + }, + child: Column(children: [ + Image.asset( + "assets/imgs/video/icon_download.png", + width: 21, + ), + Container( + height: 5, + ), + const Text( + "涓嬭浇", + style: + TextStyle(color: Color(0xFF9D9D9D), fontSize: 10), + ) + ])), + InkWell( + onTap: () { + Share.share( + "鎴戞鍦ㄧ湅銆�${_simpleVideo!.name!}銆�,APP閾炬帴鍦板潃涓猴細$applink"); + }, + child: Column(children: [ + Image.asset( + "assets/imgs/video/icon_share.png", + width: 21, + ), + Container( + height: 5, + ), + const Text( + "鍒嗕韩", + style: + TextStyle(color: Color(0xFF9D9D9D), fontSize: 10), + ) + ])), + ], + ), + ])), + //杩藉墽 + videoAttention != null + ? Container( + height: 60, + margin: const EdgeInsets.fromLTRB(10, 10, 10, 0), + padding: const EdgeInsets.fromLTRB(5, 10, 5, 10), + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFF999999), + width: DimenUtil.getOnePixel(context)), + borderRadius: BorderRadius.circular(8)), + child: Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: CommonImage( + videoAttention!.picture!, + width: 40, + height: 40, + )), + const SizedBox( + width: 5, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoAttention!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 14), + ), + Expanded(child: Container()), + Text( + videoAttention!.updateInfo!, + style: const TextStyle( + fontSize: 10, color: Color(0xFF9D9D9D)), + ) + ], + )), + const SizedBox( + width: 5, + ), + MyFillButton( + videoAttention!.attention! ? "鍙栨秷杩藉墽" : "杩藉墽", + 6, + width: 60, + color: ColorConstant.theme, + onClick: () { + attentionClick(); + }, + ) + ], + ), + ) + : Container(), + + Container( + padding: const EdgeInsets.fromLTRB(12, 16, 12, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "閫夐泦", + style: TextStyle(color: Color(0xFF999999)), + ), + InkWell( + onTap: () { + setState(() { + episodeExpand = true; + }); + }, + child: Row(children: [ + Text( + _videoInfoModel!.tag ?? "", + style: const TextStyle(color: Color(0xFF999999)), + ), + const Icon( + Icons.arrow_forward_ios, + color: Color(0xFF999999), + size: 15, + ) + ])) + ])), + Container( + margin: const EdgeInsets.only(top: 12), + alignment: Alignment.topLeft, + height: _videoInfoModel!.showType == 2 ? 40 : 90, + child: ListView.separated( + padding: const EdgeInsets.only(left: 12, right: 12), + scrollDirection: Axis.horizontal, + controller: _scrollController, + shrinkWrap: true, + itemCount: _videoInfoModel!.videoDetailList!.length, + itemBuilder: (BuildContext context, int index) { + VideoDetailInfo detailInfo = + _videoInfoModel!.videoDetailList![index]; + return _getEpisodeItem(detailInfo, index); + }, + separatorBuilder: (BuildContext context, int index) { + return Container( + width: 8.5, + ); + }, + )), + relativeVideoList.isNotEmpty + ? Container( + margin: const EdgeInsets.fromLTRB(12, 30, 12, 20), + alignment: Alignment.topLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "鐩稿叧鎺ㄨ崘", + style: TextStyle(color: Colors.black, fontSize: 16.41), + ), + ListView.separated( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + setState(() { + _playPosition = 0; + _selectedVideoResource = null; + _simpleVideo = relativeVideoList[index]; + }); + getVideoDetail(_simpleVideo!.id); + }, + child: VideoListUIUtil.getRecommendVideo( + context, relativeVideoList[index])); + }, + separatorBuilder: (BuildContext context, int index) { + return Container( + height: 14, + ); + }, + itemCount: relativeVideoList.length) + ]), + ) + : Container() + ], + ); + } + + Widget _getEpisodeItem(VideoDetailInfo detailInfo, int index) { + //婊戝埌鎸囧畾浣嶇疆 + + if (_videoInfoModel!.showType == 2) { + return VideoListUIUtil.getTVEpisodeItem(detailInfo.tag, 60, 30, context, + checked: _playPosition == index, onClick: () { + _scrollController.animateTo(60.0 * index + 8.5 * index, + duration: const Duration(milliseconds: 200), curve: Curves.ease); + setState(() { + _playPosition = index; + episodeExpand = false; + }); + getPlayUrl(_playPosition); + }); + } else { + return VideoListUIUtil.getShowEpisodeItem(detailInfo.tag, context, + checked: _playPosition == index, onClick: () { + _scrollController.animateTo(200.0 * index + 8.5 * index, + duration: const Duration(milliseconds: 200), curve: Curves.ease); + setState(() { + _playPosition = index; + episodeExpand = false; + }); + getPlayUrl(_playPosition); + }); + } + } + + Widget _getVideoDescView() { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.all(10), + child: SingleChildScrollView( + child: Text( + _videoInfoModel!.introduction ?? "", + style: const TextStyle(fontSize: 16), + ))); + } + + Widget _getResourceListDialog() { + return Container( + margin: const EdgeInsets.all(10), + alignment: Alignment.bottomCenter, + child: Stack(alignment: Alignment.bottomCenter, children: [ + Container( + margin: const EdgeInsets.only(bottom: 50), + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(10)), + child: GridView.count( + childAspectRatio: 3, + shrinkWrap: true, + crossAxisCount: 2, + children: _videoInfoModel!.resourceList!.map((e) { + return InkWell( + onTap: () { + setState(() { + _selectedVideoResource = e; + }); + popPage(context); + getVideoDetail(_simpleVideo!.id); + }, + child: Container( + alignment: Alignment.center, + child: Text( + e.name!, + style: TextStyle(fontSize: 18), + ))); + }).toList(), + )), + MyFillButton( + "鍙栨秷", + 20, + height: 40, + color: Colors.grey, + fontSize: 14, + onClick: () { + popPage(context); + }, + ) + ])); + } +} + +enum VideoDetailStatus { success, fail, going } diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_download_list.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_download_list.dart new file mode 100644 index 0000000..5b3cd72 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_download_list.dart @@ -0,0 +1,302 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../model/video/watch_record_model.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/db_manager.dart'; +import '../../utils/video/video_util.dart'; +import '../../api/http.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/cache_util.dart'; +import '../../utils/config_util.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/setting_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '瑙嗛涓嬭浇', + theme: ThemeData(primaryColor: const Color(0xFFF5F5F5)), + home: VideoDownloadPage(title: ''), + ); + } +} + +class VideoDownloadPage extends StatefulWidget { + VideoDownloadPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _VideoDownloadPageState createState() => _VideoDownloadPageState(); +} + +class _VideoDownloadPageState extends State<VideoDownloadPage> + with SingleTickerProviderStateMixin { + bool editMode = false; + Set<String> selectedSet = {}; + + final MyRefreshController _refreshController = MyRefreshController(); + List<WatchRecordModel> recordList = []; + + int page = 1; + int toalCount = 0; + + @override + void initState() { + super.initState(); + } + + void loadWatchRecord(int _page) async { + page = _page; + List<WatchRecordModel> temp = []; + int count = 0; + setState(() { + toalCount = count; + }); + if (page == 1) { + setState(() { + recordList = temp; + }); + } else { + setState(() { + recordList.addAll(temp); + }); + } + + _refreshController.refreshCompleted(); + if (count >= recordList.length) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + if (toalCount == 0) { + _refreshController.dataEmpty!(); + } + } + + void deleteRecord() async{ + if (selectedSet.isEmpty) { + return; + } + await DBManager.deleteWatchRecord(selectedSet.toList()); + setState(() { + selectedSet.clear(); + }); + loadWatchRecord(1); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + TopNavBar( + title: "鎴戠殑涓嬭浇", + rightIcon: recordList.isNotEmpty? const Icon( + Icons.format_list_bulleted, + size: 25, + color: Color(0xFF202020), + ):null, + rightClick: () { + setState(() { + editMode = !editMode; + if (!editMode) { + selectedSet.clear(); + _refreshController.setPullDownEnable!(true); + _refreshController.setPullUpEnable!(true); + } else { + _refreshController.setPullDownEnable!(false); + _refreshController.setPullUpEnable!(false); + } + }); + }, + ), + Container( + height: DimenUtil.getOnePixel(context), + color: const Color(0xFFDBDBDB), + ), + Expanded( + child: RefreshListView( + content: ListView.builder( + padding: const EdgeInsets.only(top: 10), + itemBuilder: (BuildContext context, int index) { + return getItem(index); + }, + itemCount: recordList.length, + ), + refreshController: _refreshController, + refresh: () { + loadWatchRecord(1); + }, + loadMore: () { + loadWatchRecord(page + 1); + }, + )), + editMode + ? Container( + padding: const EdgeInsets.fromLTRB(28, 10, 28, 10), + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Color(0xFFDBDBDB), width: 0.5))), + child: Row( + children: [ + Expanded( + child: MyOutlineButton( + recordList.length == selectedSet.length ? "鍙嶉��" : "鍏ㄩ��", + 8, + height: 34, + fontSize: 16, + color: const Color(0xFFdbdbdb), + textColor: const Color(0xFF202020), + onClick: () { + if (recordList.length == selectedSet.length) { + setState(() { + selectedSet.clear(); + }); + } else { + Set<String> list = recordList + .map((e) => e.id.toString()) + .toSet(); + setState(() { + selectedSet = list; + }); + } + }, + )), + Container( + width: 20, + ), + Expanded( + child: MyFillButton( + selectedSet.isNotEmpty + ? ("鍒犻櫎(${selectedSet.length})") + : "鍒犻櫎", + 8, + height: 34, + fontSize: 16, + onClick: () { + deleteRecord(); + }, + )), + ], + ), + ) + : Container() + ], + )); + } + + Widget getItem(index) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.fromLTRB(10, 8, 10, 8), + height: 80, + child: InkWell( + child: Row( + children: [ + Stack( + alignment: Alignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + VideoUtil.getHPicture(recordList[index].video!), + height: 80, + width: 80 * 1.68, + )), + (editMode + ? Image.asset( + selectedSet.contains("${recordList[index].id}") + ? "assets/imgs/video/icon_check_true.png" + : "assets/imgs/video/icon_check_false.png", + width: 35, + ) + : Container()) + ], + ), + Container( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + recordList[index].video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: + const TextStyle(color: Color(0xFF232323), fontSize: 15), + ), + Text( + recordList[index].video!.tag ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: + const TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ), + Expanded(child: Container()), + Text( + _getPositionDesc(recordList[index]), + style: + const TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ) + ], + )) + ], + ), + onTap: () { + if (!editMode) { + return; + } + print("check: index-$index"); + setState(() { + if (selectedSet.contains("${recordList[index].id}")) { + selectedSet.remove("${recordList[index].id}"); + } else { + selectedSet.add("${recordList[index].id}"); + } + }); + }, + )); + } + + String _getPositionDesc(WatchRecordModel record) { + if (record.video!.tag != null && record.video!.tag!.contains("闆�")) { + return "瑙傜湅鑷�${record.position! + 1}闆�"; + } else { + return ""; + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_list.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_list.dart new file mode 100644 index 0000000..2c821e4 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_list.dart @@ -0,0 +1,166 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../api/video_api.dart'; +import '../../model/video/home_type_model.dart'; +import '../../model/video/video_model.dart'; +import '../../ui/search/search.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../widget/button.dart'; + +import '../../ui/widget/nav.dart'; +import '../widget/refresh_listview.dart'; + +class VideoListPage extends StatefulWidget { + final String kw; + + VideoListPage({Key? key, required this.title, required this.kw}) + : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _VideoListPageState createState() => _VideoListPageState(); +} + +class _VideoListPageState extends State<VideoListPage> + with SingleTickerProviderStateMixin { + int page = 1; + List<VideoInfoModel> videoList = []; + int column = 2; + bool hasNextPage = true; + + final MyRefreshController _refreshController = MyRefreshController(); + + @override + void initState() { + super.initState(); + getVideoList(1); + } + + void getVideoList(int _page) async { + page = _page; + Map<String, dynamic>? result = + await VideoApiUtil.getSearchSpecialVideoList(context, widget.kw, _page); + _refreshController.refreshCompleted(resetFooterState: true); + //璇锋眰澶辫触浜� + if (result == null) { + if (page > 1) { + page = page - 1; + } + if (videoList.isEmpty) { + _refreshController.apiError!(); + } + return; + } + if (result["IsPost"] == "true") { + List<dynamic> list = result["Data"]["list"]; + hasNextPage = result["Data"]["hasNextPage"]; + column = result["Data"]["column"]; + List<VideoInfoModel> tempList = []; + for (var element in list) { + tempList.add(VideoInfoModel.fromJson(element)); + } + setState(() { + if (_page == 1) { + videoList = tempList; + } else { + if (tempList.isNotEmpty) { + videoList.addAll(tempList); + } + } + }); + + if (!hasNextPage) { + _refreshController.loadNoData(); + } else { + if (_page > 1) { + _refreshController.loadComplete(); + } + } + + if (videoList.isEmpty) { + _refreshController.dataEmpty!(); + } else { + //姝e父鐨勭姸鎬� + _refreshController.dataNormal!(); + } + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + TopNavBar( + title: "", + leftText: widget.title, + rightIcon: const Icon( + Icons.search, + size: 40, + color: ColorConstant.theme, + ), + rightClick: () { + NavigatorUtil.navigateToNextPage( + context, SearchPage(title: ""), (data) {}); + }, + ), + Container( + height: DimenUtil.getOnePixel(context), + color: const Color(0xFFDBDBDB), + ), + Expanded( + child: RefreshListView( + content: GridView.builder( + padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), + itemCount: videoList.length, + itemBuilder: (BuildContext context, int index) { + if (column == 2) { + return VideoListUIUtil.getVideoItemColumn_2( + MediaQuery.of(context).size.width - 10 * 2, + HomeVideoModel(video: videoList[index]), + context); + } else { + return VideoListUIUtil.getVideoItemColumn_3( + MediaQuery.of(context).size.width - 10 * 2, + HomeVideoModel(video: videoList[index]), + context); + } + }, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + //妯酱鍏冪礌涓暟 + crossAxisCount: column, + //绾佃酱闂磋窛 + mainAxisSpacing: 10.0, + //妯酱闂磋窛 + crossAxisSpacing: 10.0, + //瀛愮粍浠跺楂橀暱搴︽瘮渚� + childAspectRatio: column == 2 ? 1.25 : 0.55), + ), + refreshController: _refreshController, + refresh: () { + getVideoList(1); + }, + loadMore: () { + getVideoList(page + 1); + }, + )), + ], + )); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_player_browser.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_player_browser.dart new file mode 100644 index 0000000..de56f9c --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_player_browser.dart @@ -0,0 +1,321 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +import '../../ui/widget/nav.dart'; +import '../../utils/jsinterface.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class VideoPlayerWebPage extends StatefulWidget { + VideoPlayerWebPage({Key? key, required this.title, required this.url}) + : super(key: key); + final String title; + final String url; + + @override + _VideoPlayerWebPageState createState() => + _VideoPlayerWebPageState(title, url); +} + +class _VideoPlayerWebPageState extends State<VideoPlayerWebPage> + with SingleTickerProviderStateMixin { + String title = ""; + String? url; + double progress = 0; + + _VideoPlayerWebPageState(this.title, this.url); + + @override + void initState() { + super.initState(); + if (Platform.isAndroid) { + WebView.platform = SurfaceAndroidWebView(); + } + } + + WebViewController? _webViewController; + + _back() { + if (_webViewController == null) { + popPage(context); + } else { + _webViewController!.canGoBack().then((value) { + if (value) { + _webViewController!.goBack(); + } else { + popPage(context); + } + }); + } + } + + Future<void> _launchInBrowser(String url) async { + if (!await launch(url)) throw 'Could not launch $url'; + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + _back(); + return false; + }, + child: Scaffold( + backgroundColor: Colors.white, + body: Flex( + direction: Axis.vertical, + children: [ + Flex(direction: Axis.vertical, children: [ + Container( + height: MediaQuery.of(context).viewPadding.top, + color: Colors.white, + ), + Container( + color: Colors.white, + height: 48, + child: Stack( + alignment: Alignment.centerLeft, + children: [ + Positioned( + child: Container( + alignment: Alignment.center, + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 70, + ), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18, + color: Colors.black), + ), + Text( + url!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 10, + color: Color(0xFF999999)), + ) + ])), + Container( + width: 50, + ) + ], + ))), + Positioned( + right: 0, + top: 0, + bottom: 0, + child: InkWell( + onTap: () {}, + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(right: 10), + child: PopupMenuButton( + offset: const Offset(0, 48), + icon: const Icon( + Icons.more_horiz, + size: 38, + color: Color(0xFF666666), + ), + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + textStyle: const TextStyle( + fontSize: 15, + color: Color(0xFF666666)), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/imgs/common/icon_browser_refresh.png", + width: 17, + ), + Container( + width: 9, + ), + const Text("鍒锋柊") + ]), + onTap: () { + if (_webViewController == null) { + return; + } + _webViewController!.reload(); + }, + ), + PopupMenuItem( + textStyle: const TextStyle( + fontSize: 15, + color: Color(0xFF666666)), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/imgs/common/icon_browser_link.png", + width: 17, + ), + Container( + width: 9, + ), + const Text("澶嶅埗閾炬帴") + ]), + onTap: () { + if (url == null) { + return; + } + Clipboard.setData( + ClipboardData(text: url)); + ToastUtil.toast("澶嶅埗鎴愬姛", context); + }, + ), + PopupMenuItem( + textStyle: const TextStyle( + fontSize: 15, + color: Color(0xFF666666)), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.asset( + "assets/imgs/common/icon_browser_out.png", + width: 17, + ), + Container( + width: 9, + ), + const Text("澶栭儴娴忚鍣ㄦ墦寮�") + ]), + onTap: () { + if (url == null) { + return; + } + _launchInBrowser(url!); + }, + ), + ]; + }, + ), + ))), + Positioned( + left: 30, + child: InkWell( + onTap: () { + popPage(context); + }, + child: const Icon( + Icons.clear, + color: Color(0xFF666666), + size: 35, + )), + ), + InkWell( + onTap: () { + _webViewController!.canGoBack().then((value) { + if (value) { + _webViewController!.goBack(); + } else { + popPage(context); + } + }); + }, + child: Container( + padding: const EdgeInsets.only(left: 10), + height: 48, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + Icon( + Icons.arrow_back_ios, + color: Color(0xFF666666), + size: 30, + ), + ]), + )) + ], + ), + ) + ]), + SizedBox( + height: 1, + child: LinearProgressIndicator( + backgroundColor: Colors.white, + valueColor: + const AlwaysStoppedAnimation(ColorConstant.theme), + value: progress, + ), + ), + Expanded( + child: WebView( + //http://192.168.3.122:8848/test/JsTest.html + initialUrl: url, + onWebViewCreated: (WebViewController webViewController) { + _webViewController = webViewController; + }, + javascriptMode: JavascriptMode.unrestricted, + javascriptChannels: + JavascriptInterface(context, _webViewController) + .getInterfaces(), + navigationDelegate: (NavigationRequest request) { + print("閾炬帴锛�${request.url}"); + if (!request.url.startsWith("http")) { + launch(request.url); + return NavigationDecision.prevent; + } + return NavigationDecision.navigate; + }, + onPageStarted: (url) { + print("process:onPageStarted-$url"); + }, + onPageFinished: (url) { + print("process:onPageFinished-$url"); + _webViewController!.getTitle().then((value) { + if (value != null) { + setState(() { + title = value; + }); + } + }); + }, + onProgress: (int process) { + print("process:$process"); + if (progress == 0) { + if (_webViewController != null) { + _webViewController!.currentUrl().then((value) { + setState(() { + url = value; + }); + }); + } + } + setState(() { + if (process == 100) { + progress = 0; + } else { + progress = process / 100.0; + } + }); + }, + )) + ], + ))); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/video/video_scan_record_list.dart b/src/main/resources/code/flutter_module/lib/ui/video/video_scan_record_list.dart new file mode 100644 index 0000000..114e68f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/video/video_scan_record_list.dart @@ -0,0 +1,321 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../model/video/watch_record_model.dart'; +import '../../ui/video/video_detail.dart'; +import '../../ui/widget/button.dart'; +import '../../ui/widget/refresh_listview.dart'; +import '../../ui/widget/video_item.dart'; +import '../../utils/db_manager.dart'; +import '../../utils/video/video_util.dart'; +import '../../api/http.dart'; +import '../../ui/common/browser.dart'; +import '../../ui/widget/dialog.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/cache_util.dart'; +import '../../utils/config_util.dart'; +import '../../utils/event_bus_util.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/push_util.dart'; +import '../../utils/setting_util.dart'; +import '../../utils/string_util.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import '../../utils/user_util.dart'; +import 'package:package_info/package_info.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: '瑙嗛鏀惰棌', + theme: ThemeData(primaryColor: const Color(0xFFF5F5F5)), + home: VideoScanRecordPage(title: ''), + ); + } +} + +class VideoScanRecordPage extends StatefulWidget { + VideoScanRecordPage({Key? key, required this.title}) : super(key: key); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + _VideoScanRecordPageState createState() => _VideoScanRecordPageState(); +} + +class _VideoScanRecordPageState extends State<VideoScanRecordPage> + with SingleTickerProviderStateMixin { + bool editMode = false; + Set<String> selectedSet = {}; + + final MyRefreshController _refreshController = + MyRefreshController(initialRefresh: false); + List<WatchRecordModel> recordList = []; + + int page = 1; + int toalCount = 0; + + @override + void initState() { + super.initState(); + loadWatchRecord(1); + } + + Future loadWatchRecord(int _page) async { + page = _page; + List<WatchRecordModel> temp = await DBManager.listWatchRecord(page, 20); + int count = await DBManager.countWatchRecord(); + setState(() { + toalCount = count; + }); + if (page == 1) { + setState(() { + recordList = temp; + }); + } else { + setState(() { + recordList.addAll(temp); + }); + } + + _refreshController.refreshCompleted(); + if (count >= recordList.length) { + _refreshController.loadNoData(); + } else { + _refreshController.loadComplete(); + } + if (toalCount == 0) { + _refreshController.dataEmpty!(); + } + } + + void deleteRecord() async { + if (selectedSet.isEmpty) { + return; + } + await DBManager.deleteWatchRecord(selectedSet.toList()); + setState(() { + selectedSet.clear(); + }); + loadWatchRecord(1).then((value) { + if (recordList.isEmpty) { + setState(() { + editMode = false; + }); + _refreshController.setEditMode!(false); + } + }); + } + + Widget getListView() { + return ListView.builder( + padding: const EdgeInsets.only(top: 10), + itemBuilder: (BuildContext context, int index) { + return getItem(index); + }, + itemCount: recordList.length, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + TopNavBar( + title: "瑙傜湅璁板綍", + rightIcon: recordList.isNotEmpty + ? const Icon( + Icons.format_list_bulleted, + size: 25, + color: Color(0xFF202020), + ) + : null, + rightClick: () { + setState(() { + editMode = !editMode; + _refreshController.setEditMode!(editMode); + if (!editMode) { + selectedSet.clear(); + } else {} + }); + }, + ), + Container( + height: DimenUtil.getOnePixel(context), + color: const Color(0xFFDBDBDB), + ), + Expanded( + child: RefreshListView( + content: getListView(), + refreshController: _refreshController, + refresh: () { + loadWatchRecord(1); + }, + loadMore: () { + loadWatchRecord(page + 1); + }, + )), + editMode && recordList.isNotEmpty + ? Container( + padding: const EdgeInsets.fromLTRB(28, 10, 28, 10), + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: Color(0xFFDBDBDB), width: 0.5))), + child: Row( + children: [ + Expanded( + child: MyOutlineButton( + recordList.length == selectedSet.length ? "鍙嶉��" : "鍏ㄩ��", + 8, + height: 34, + fontSize: 16, + color: const Color(0xFFdbdbdb), + textColor: const Color(0xFF202020), + onClick: () { + if (recordList.length == selectedSet.length) { + setState(() { + selectedSet.clear(); + }); + } else { + Set<String> list = recordList + .map((e) => e.id.toString()) + .toSet(); + setState(() { + selectedSet = list; + }); + } + }, + )), + Container( + width: 20, + ), + Expanded( + child: MyFillButton( + selectedSet.isNotEmpty + ? ("鍒犻櫎(${selectedSet.length})") + : "鍒犻櫎", + 8, + height: 34, + fontSize: 16, + onClick: () { + deleteRecord(); + }, + )), + ], + ), + ) + : Container() + ], + )); + } + + Widget getItem(index) { + return Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.fromLTRB(10, 8, 10, 8), + height: 80, + child: InkWell( + child: Row( + children: [ + Stack( + alignment: Alignment.center, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + VideoUtil.getHPicture(recordList[index].video!), + height: 80, + width: 80 * 1.68, + )), + (editMode + ? Image.asset( + selectedSet.contains("${recordList[index].id}") + ? "assets/imgs/video/icon_check_true.png" + : "assets/imgs/video/icon_check_false.png", + width: 35, + ) + : Container()) + ], + ), + Container( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + recordList[index].video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: + const TextStyle(color: Color(0xFF232323), fontSize: 15), + ), + Text( + recordList[index].video!.tag ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: + const TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ), + Expanded(child: Container()), + Text( + _getPositionDesc(recordList[index]), + style: + const TextStyle(color: Color(0xFFB8AFB5), fontSize: 12), + ) + ], + )) + ], + ), + onTap: () { + if (!editMode) { + //璺宠浆璇︽儏 + NavigatorUtil.navigateToNextPage(context, VideoDetailPage(position:recordList[index].position! ,videoInfo:recordList[index].video ,), (data) { + + + }); + + + return; + } + print("check: index-$index"); + setState(() { + if (selectedSet.contains("${recordList[index].id}")) { + selectedSet.remove("${recordList[index].id}"); + } else { + selectedSet.add("${recordList[index].id}"); + } + }); + }, + )); + } + + String _getPositionDesc(WatchRecordModel record) { + if (record.video!.tag != null && record.video!.tag!.contains("闆�")) { + return "瑙傜湅鑷�${record.position! + 1}闆�"; + } else { + return ""; + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/ad_express.dart b/src/main/resources/code/flutter_module/lib/ui/widget/ad_express.dart new file mode 100644 index 0000000..a55d624 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/ad_express.dart @@ -0,0 +1,149 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; + +import 'common_ui.dart'; + +class ExpressAdController { + VoidCallback? refresh; +} + +//TODO 鍏煎android +class CSJEXpressAd extends StatefulWidget { + final String pid; + final double width; + final double height; + final ExpressAdController? controller; + final VoidCallback? close; + final VoidCallback? loadFail; + + CSJEXpressAd(this.pid, this.width, this.height, + {this.controller, this.close, this.loadFail}); + + @override + State<StatefulWidget> createState() => _CSJEXpressAdState(); +} + +class _CSJEXpressAdState extends State<CSJEXpressAd> { + MethodChannel? _expressAdChannel; + + @override + void initState() { + super.initState(); + if (widget.controller != null) { + widget.controller!.refresh = () { + if (_expressAdChannel != null) { + _expressAdChannel!.invokeMethod("refresh"); + } + }; + } + } + + @override + Widget build(BuildContext context) { + return KeepAliveWrapper( + child: Platform.isAndroid + ? AndroidView( + viewType: "ad-csj-express-view", + layoutDirection: TextDirection.ltr, + onPlatformViewCreated: onPlatformViewCreated, + creationParams: <String, dynamic>{ + "width": widget.width, + "height": widget.height, + "pid": widget.pid + }, + creationParamsCodec: const StandardMessageCodec(), + ) + : UiKitView( + viewType: "ad-csj-express-view", + layoutDirection: TextDirection.ltr, + onPlatformViewCreated: onPlatformViewCreated, + creationParams: <String, dynamic>{ + "width": widget.width, + "height": widget.height, + "pid": widget.pid + }, + creationParamsCodec: const StandardMessageCodec(), + )); + } + + Future<void> onPlatformViewCreated(id) async { + _expressAdChannel = MethodChannel("ad-csj-express-view-$id"); + _expressAdChannel!.setMethodCallHandler((call) { + if ("close" == call.method) { + if (widget.close != null) { + widget.close!(); + } + } else if ("loadFail" == call.method) { + if (widget.loadFail != null) { + widget.loadFail!(); + } + } + return Future.value(""); + }); + } +} + +class GDTEXpressAd extends StatefulWidget { + final String pid; + final double width; + final double height; + final ExpressAdController? controller; + final VoidCallback? close; + final VoidCallback? loadFail; + + GDTEXpressAd(this.pid, this.width, this.height, + {this.controller, this.close, this.loadFail}); + + @override + State<StatefulWidget> createState() => _GDTEXpressAdState(); +} + +class _GDTEXpressAdState extends State<GDTEXpressAd> { + MethodChannel? _expressAdChannel; + + @override + void initState() { + super.initState(); + if (widget.controller != null) { + widget.controller!.refresh = () { + if (_expressAdChannel != null) { + _expressAdChannel!.invokeMethod("refresh"); + } + }; + } + } + + @override + Widget build(BuildContext context) { + return KeepAliveWrapper( + child: UiKitView( + viewType: "ad-gdt-express-view", + layoutDirection: TextDirection.ltr, + onPlatformViewCreated: onPlatformViewCreated, + creationParams: <String, dynamic>{ + "width": widget.width, + "height": widget.height, + "pid": widget.pid + }, + creationParamsCodec: const StandardMessageCodec(), + )); + } + + Future<void> onPlatformViewCreated(id) async { + _expressAdChannel = MethodChannel("ad-gdt-express-view-$id"); + _expressAdChannel!.setMethodCallHandler((call) { + if ("close" == call.method) { + if (widget.close != null) { + widget.close!(); + } + } else if ("loadFail" == call.method) { + if (widget.loadFail != null) { + widget.loadFail!(); + } + } + return Future.value(""); + }); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/base_ui.dart b/src/main/resources/code/flutter_module/lib/ui/widget/base_ui.dart new file mode 100644 index 0000000..9ccb217 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/base_ui.dart @@ -0,0 +1,2 @@ +import 'package:flutter/cupertino.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/button.dart b/src/main/resources/code/flutter_module/lib/ui/widget/button.dart new file mode 100644 index 0000000..d9a2437 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/button.dart @@ -0,0 +1,138 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import '../../utils/ui_constant.dart'; + +class MyOutlineButton extends StatelessWidget { + final String text; + final double radius; + final double height; + final double? width; + final Color color; + Color? textColor; + final double fontSize; + final GestureTapCallback? onClick; + final EdgeInsets? padding; + + MyOutlineButton(this.text, this.radius, + {GestureTapCallback? this.onClick, + Color this.color = ColorConstant.theme, + Color? textColor, + double this.height = 26, + double this.fontSize = 12, + double? this.width = null, + EdgeInsets? this.padding = null}) { + textColor ??= this.color; + } + + @override + Widget build(BuildContext context) { + Container child = Container( + alignment: Alignment.center, + height: height, + width: width, + padding: padding, + decoration: BoxDecoration( + color: Colors.transparent, + border: Border.all(color: color, width: 1), + borderRadius: BorderRadius.circular(radius), + ), + child: Text( + text, + style: TextStyle(color: textColor, fontSize: fontSize), + ), + ); + + return InkWell( + onTap: () { + onClick!(); + }, + child: child); + } +} + +class MyFillButton extends StatelessWidget { + final String text; + final Color textColor; + final double radius; + final double height; + final double? width; + final Color color; + final double fontSize; + final GestureTapCallback? onClick; + final EdgeInsets? padding; + final bool? enable; + + MyFillButton(this.text, this.radius, + {GestureTapCallback? this.onClick, + Color this.color = ColorConstant.theme, + Color this.textColor = Colors.white, + double this.height = 26, + double this.fontSize = 12, + double? this.width = null, + EdgeInsets? this.padding = null, + bool? this.enable = true}); + + @override + Widget build(BuildContext context) { + Container child = Container( + alignment: Alignment.center, + height: height, + width: width, + padding: padding, + decoration: BoxDecoration( + color: enable! ? color : const Color(0xFFCBCBCB), + borderRadius: BorderRadius.circular(radius), + ), + child: Text( + text, + style: TextStyle(color: textColor, fontSize: fontSize), + ), + ); + + return InkWell( + onTap: () { + onClick!(); + }, + child: child); + } +} + +class RoundCheckBox extends StatefulWidget { + var value = false; + + Function(bool) onChanged; + + RoundCheckBox({Key? key, required this.value, required this.onChanged}) + : super(key: key); + + @override + _RoundCheckBoxState createState() => _RoundCheckBoxState(); +} + +class _RoundCheckBoxState extends State<RoundCheckBox> { + @override + Widget build(BuildContext context) { + return Center( + child: GestureDetector( + onTap: () { + widget.value = !widget.value; + widget.onChanged(widget.value); + }, + child: Padding( + padding: const EdgeInsets.all(10.0), + child: widget.value + ? const Icon( + Icons.check_circle, + size: 19, + color: Colors.green, + ) + : const Icon( + Icons.panorama_fish_eye, + size: 19, + color: Colors.black12, + ), + )), + ); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/capture.dart b/src/main/resources/code/flutter_module/lib/ui/widget/capture.dart new file mode 100644 index 0000000..943f9b5 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/capture.dart @@ -0,0 +1,47 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +///瀵箇idget鎴浘 + +class CaptureWidget extends StatelessWidget { +//鎴浘缁勪欢 + GlobalKey rootWidgetKey = GlobalKey(); + final Widget widget; + final CaptureController captureController; + + CaptureWidget({required this.widget, required this.captureController}) { + captureController.setGlobalKey(rootWidgetKey); + } + + @override + Widget build(BuildContext context) { + return RepaintBoundary(key: rootWidgetKey, child: widget); + } +} + +class CaptureController { + GlobalKey? _globalKey; + + setGlobalKey(GlobalKey globalKey) { + _globalKey = globalKey; + } + + Future<String> capturePng() async { + try { + RenderRepaintBoundary? boundary = _globalKey!.currentContext! + .findRenderObject() as RenderRepaintBoundary; + var image = await boundary.toImage(pixelRatio: 3.0); + ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); + Uint8List pngBytes = byteData!.buffer.asUint8List(); + String str = base64.encode(pngBytes); + return str; + } catch (e) {} + return ""; + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/common_ui.dart b/src/main/resources/code/flutter_module/lib/ui/widget/common_ui.dart new file mode 100644 index 0000000..bf39b96 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/common_ui.dart @@ -0,0 +1,35 @@ +import 'package:flutter/widgets.dart'; + +class KeepAliveWrapper extends StatefulWidget { + const KeepAliveWrapper({ + Key? key, + this.keepAlive = true, + required this.child, + }) : super(key: key); + final bool keepAlive; + final Widget child; + + @override + _KeepAliveWrapperState createState() => _KeepAliveWrapperState(); +} + +class _KeepAliveWrapperState extends State<KeepAliveWrapper> + with AutomaticKeepAliveClientMixin { + @override + Widget build(BuildContext context) { + super.build(context); + return widget.child; + } + + @override + void didUpdateWidget(covariant KeepAliveWrapper oldWidget) { + if (oldWidget.keepAlive != widget.keepAlive) { + // keepAlive 鐘舵�侀渶瑕佹洿鏂帮紝瀹炵幇鍦� AutomaticKeepAliveClientMixin 涓� + updateKeepAlive(); + } + super.didUpdateWidget(oldWidget); + } + + @override + bool get wantKeepAlive => widget.keepAlive; +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/dialog.dart b/src/main/resources/code/flutter_module/lib/ui/widget/dialog.dart new file mode 100644 index 0000000..8822e93 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/dialog.dart @@ -0,0 +1,415 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import '../../ui/search/search.dart'; +import 'package:html/dom.dart' as dom; +import '../../ui/common/browser.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/ui_constant.dart'; +import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; +import '../widget/nav.dart'; + +//閫氱敤寮规 +class NotifyDialog extends Dialog { + BuildContext? context; + final String title; + final String content; + final GestureTapCallback onCancel; + final GestureTapCallback onSure; + final bool richText; + final double fontSize; + final double height; + final Color contentColor; + bool touchOutCancel = false; + final String cancelName; + final String sureName; + + NotifyDialog(this.title, this.content, this.onCancel, this.onSure, + {this.fontSize = 16.0, + this.richText = false, + this.height = 240, + this.contentColor = const Color(0xFF333333), + this.cancelName = "鍙栨秷", + this.sureName = "纭畾"}); + + Widget getContent(BuildContext context) { + if (richText) { + return SingleChildScrollView( + child: HtmlWidget(content, onTapUrl: (String url) { + NavigatorUtil.navigateToNextPage( + context, + BrowserPage( + title: "", + url: url, + ), + (data) {}); + + return true; + })); + } else { + return Text( + content, + style: TextStyle(color: contentColor, fontSize: fontSize), + ); + } + } + + Offset? offset; + + @override + Widget build(BuildContext context) { + this.context = context; + double width = MediaQuery.of(context).size.width; + double dialogWidth = width * 4 / 5; + print("灞忓箷瀹斤細$width"); + + //鍏抽棴寮规 + // Navigator.pop(context); + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Material( + type: MaterialType.transparency, + child: Align( + alignment: Alignment.center, + child: Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(15)), + color: Colors.white), + alignment: Alignment.topCenter, + height: height, + width: dialogWidth, + child: Flex( + mainAxisAlignment: MainAxisAlignment.start, + direction: Axis.vertical, + children: [ + //-------鏍囬鍖哄煙-------- + Container( + alignment: Alignment.center, + height: 60, + child: Text( + title, + style: TextStyle(fontSize: 18, color: Colors.white), + ), + decoration: const BoxDecoration( + color: ColorConstant.theme, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15)), + )), + //-------鍐呭鍖哄煙-------- + Expanded( + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.fromLTRB(15, 5, 15, 5), + child: getContent(context), + )), + + //------鎸夐挳鍖哄煙-------- + Flex( + direction: Axis.horizontal, + children: [ + Expanded( + child: InkWell( + onTap: () { + Navigator.pop(context); + onCancel(); + }, + child: Container( + margin: EdgeInsets.fromLTRB(15, 0, 6, 15), + alignment: Alignment.center, + height: 44, + decoration: BoxDecoration( + border: Border.all(color: ColorConstant.theme), + borderRadius: BorderRadius.circular(10)), + child: Text( + cancelName, + style: const TextStyle( + color: ColorConstant.theme, fontSize: 18), + ), + ), + )), + Expanded( + child: InkWell( + onTap: () { + Navigator.pop(context); + onSure(); + }, + child: Container( + margin: const EdgeInsets.fromLTRB(6, 0, 15, 15), + alignment: Alignment.center, + height: 44, + decoration: BoxDecoration( + color: ColorConstant.theme, + borderRadius: BorderRadius.circular(10)), + child: Text( + sureName, + style: const TextStyle( + color: Colors.white, fontSize: 18), + ), + ), + )) + ], + ) + ], + ), + )))); + } +} + +///鏉冮檺寮规 +class PermissionNotifyDialog extends Dialog { + final GestureTapCallback onOpen; + + PermissionNotifyDialog(this.onOpen); + + @override + Widget build(BuildContext context) { + double width = MediaQuery.of(context).size.width; + double dialogWidth = width * 4 / 5; + print("灞忓箷瀹斤細$width"); + //鍏抽棴寮规 + // Navigator.pop(context); + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Material( + type: MaterialType.transparency, + child: Align( + alignment: Alignment.center, + child: Container( + width: dialogWidth, + child: Flex( + mainAxisAlignment: MainAxisAlignment.center, + direction: Axis.vertical, + children: [ + Image.asset( + "assets/imgs/common/ic_permission_notify_top.png"), + Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(15), + bottomRight: Radius.circular(15)), + color: Colors.white), + child: Flex( + direction: Axis.vertical, + children: [ + getPermissionItem( + title: "鎵嬫満", + content: "鏍¢獙鎵嬫満璇嗗埆鐮侊紝闃叉璐﹀彿琚洍", + icon: Image.asset( + "assets/imgs/common/icon_permission_notify_phone.png", + width: 21, + )), + Container( + height: 30, + ), + getPermissionItem( + title: "瀛樺偍", + content: "缂撳瓨鍥剧墖鍜岃棰戯紝闄嶄綆娴侀噺娑堣��", + icon: Image.asset( + "assets/imgs/common/icon_permission_notify_save.png", + width: 26, + )), + Container( + height: 30, + ), + getPermissionItem( + title: "浣嶇疆", + content: "瀹氫綅鐢ㄦ埛", + icon: Image.asset( + "assets/imgs/common/icon_permission_notify_location.png", + width: 26, + )), + Container( + height: 36, + ), + InkWell( + onTap: () { + onOpen(); + }, + child: Container( + height: 44, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: const Color(0xFF0E96FF)), + child: const Text( + "绔嬪嵆寮�鍚�", + style: TextStyle( + fontSize: 18, color: Colors.white), + ), + ), + ) + ], + ), + ), + ], + ))))); + } + + //鏉冮檺椤� + Widget getPermissionItem( + {required Image icon, required String title, required String content}) { + return Flex( + direction: Axis.horizontal, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 34, + ), + icon, + Container( + width: 4, + ), + Expanded( + child: Flex( + crossAxisAlignment: CrossAxisAlignment.start, + direction: Axis.vertical, + children: [ + Text(title, + style: TextStyle(fontSize: 17, color: Color(0xFF6B6B6B))), + Text(content, + style: TextStyle(fontSize: 12, color: Color(0xFFA0A0A0)), + softWrap: false, + overflow: TextOverflow.ellipsis) + ], + )) + ], + ); + } +} + +///ListView寮规 +class ListViewDialog extends Dialog { + BuildContext? context; + final ListView listView; + final GestureTapCallback onClose; + final double maxHeight; + final double? dwidth; + + ListViewDialog(this.listView, this.onClose, + {this.maxHeight = 420, this.dwidth}); + + @override + Widget build(BuildContext context) { + this.context = context; + double swidth = MediaQuery.of(context).size.width; + + double dialogWidth = swidth - 20; + print("灞忓箷瀹斤細$swidth"); + if (dwidth != null) { + dialogWidth = dwidth!; + } + //鍏抽棴寮规 + // Navigator.pop(context); + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Material( + type: MaterialType.transparency, + child: Align( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Container( + constraints: BoxConstraints(maxHeight: maxHeight), + color: Colors.white, + alignment: Alignment.topCenter, + width: dialogWidth, + child: listView, + )), + Container( + height: 20, + ), + InkWell( + child: Image.asset( + "assets/imgs/common/icon_dialog_close.png", + height: 32, + width: 32, + ), + onTap: () { + onClose(); + }, + ), + ])))); + } +} + +///ListView寮规 +class LoadingDialog extends Dialog { + BuildContext? context; + final String? text; + + LoadingDialog(this.text); + + @override + Widget build(BuildContext context) { + this.context = context; + + //鍏抽棴寮规 + // Navigator.pop(context); + return WillPopScope( + onWillPop: () async { + return false; + }, + child: const Material( + type: MaterialType.transparency, + child: Align( + alignment: Alignment.center, + child: SpinKitCircle( + color: ColorConstant.theme, + size: 80.0, + )))); + } +} + +class CustomDialog extends Dialog { + BuildContext? context; + final Widget contentWidget; + + CustomDialog(this.contentWidget); + + @override + Widget build(BuildContext context) { + this.context = context; + //鍏抽棴寮规 + // Navigator.pop(context); + return WillPopScope( + onWillPop: () async { + return false; + }, + child: Material( + type: MaterialType.transparency, + child: Align( + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + contentWidget, + Container( + height: 20, + ), + InkWell( + onTap: () { + popPage(context); + }, + child: Image.asset( + "assets/imgs/common/icon_dialog_close.png", + height: 32, + width: 32, + )) + ], + )))); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/nav.dart b/src/main/resources/code/flutter_module/lib/ui/widget/nav.dart new file mode 100644 index 0000000..191c1f4 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/nav.dart @@ -0,0 +1,129 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../utils/ui_utils.dart'; +import 'package:flutter_boost/flutter_boost.dart'; + +void popPage(BuildContext context) { + if (Navigator.canPop(context)) { + Navigator.of(context).pop(); + } else { + //uiMethodChannel.invokeMethod("popPage", ""); + print("popPage"); + BoostNavigator.instance.pop(""); + } +} + +class TopNavBar extends StatelessWidget { + final String title; + GestureTapCallback? back; + String? rightText; + String? leftText; + Icon? rightIcon; + GestureTapCallback? rightClick; + final Color textColor; + final Color backGround; + final Image backIcon; + + TopNavBar( + {required this.title, + this.back, + this.rightText, + this.rightIcon, + this.leftText = "", + this.rightClick, + this.textColor = const Color(0xFF333333), + this.backGround = Colors.white, + this.backIcon = const Image( + image: AssetImage( + "assets/imgs/common/icon_back.png", + ), + height: 19)}); + + @override + Widget build(BuildContext context) { + return Flex(direction: Axis.vertical, children: [ + Container( + height: MediaQuery.of(context).viewPadding.top, + color: backGround, + ), + Container( + color: backGround, + height: 48, + child: Stack( + alignment: Alignment.centerLeft, + children: [ + Positioned( + child: Container( + alignment: Alignment.center, + child: Flex( + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 50, + ), + Expanded( + child: Center( + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 18, color: textColor), + ))), + Container( + width: 50, + ) + ], + ))), + ((rightText != null && rightText!.isNotEmpty) || rightIcon != null) + ? Positioned( + right: 0, + top: 0, + bottom: 0, + child: InkWell( + onTap: () { + rightClick!(); + }, + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(right: 10), + child: rightIcon ?? + Text( + rightText!, + style: + TextStyle(fontSize: 15, color: textColor), + ), + ))) + : Container(), + InkWell( + onTap: () { + if (back != null) { + back!(); + } else { + popPage(context); + } + }, + child: Container( + padding: const EdgeInsets.only(left: 10), + height: 48, + child: Row(mainAxisSize: MainAxisSize.min,crossAxisAlignment: CrossAxisAlignment.center, children: [ + Icon( + Icons.arrow_back_ios, + color: textColor, + ), + leftText!.isNotEmpty + ? Text( + leftText!, + style: const TextStyle(fontSize: 16), + ) + : Container() + ]), + )) + ], + ), + ) + ]); + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/refresh_listview.dart b/src/main/resources/code/flutter_module/lib/ui/widget/refresh_listview.dart new file mode 100644 index 0000000..add315e --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/refresh_listview.dart @@ -0,0 +1,244 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +typedef OnRefresh = void Function(); +typedef OnLoadMore = void Function(); +typedef BoolCallback = void Function(bool b); +typedef GetViewState = RefreshListViewState Function(); + +class MyRefreshController extends RefreshController { + MyRefreshController({ + bool initialRefresh = true, + }) : super( + initialRefresh: initialRefresh, initialLoadStatus: LoadStatus.idle); + + //api閿欒 + VoidCallback? apiError; + + //绌烘暟鎹� + VoidCallback? dataEmpty; + + //姝e父鏁版嵁 + VoidCallback? dataNormal; + + //涓嬫媺鍒锋柊 + BoolCallback? setPullDownEnable; + + //涓婃媺鍔犺浇 + BoolCallback? setPullUpEnable; + + //缂栬緫妯″紡锛岀紪杈戞ā寮忎笅涓嶅厑璁镐笅鎷夊埛鏂颁笌涓婃媺鍔犺浇 + BoolCallback? setEditMode; + + //鐣岄潰鐨勭姸鎬� + GetViewState? viewState; + + void dispose() { + apiError = null; + dataEmpty = null; + dataNormal=null; + setPullDownEnable = null; + setPullUpEnable = null; + setEditMode = null; + } +} + +class RefreshListView extends StatefulWidget { + final MyRefreshController refreshController; + final Widget content; + final OnRefresh? refresh; + final OnLoadMore? loadMore; + bool? enablePullDown; + + bool? enablePullUp; + + RefreshListView( + {required this.refreshController, + required this.content, + this.refresh, + this.loadMore, + this.enablePullDown = true, + this.enablePullUp = true}); + + @override + State<StatefulWidget> createState() => _RefreshListViewState(); +} + +class _RefreshListViewState extends State<RefreshListView> { + bool editMode = false; + + @override + void initState() { + super.initState(); + bindController(); + } + + bindController() { + widget.refreshController.apiError = _apiError; + widget.refreshController.dataEmpty = _dataEmpty; + widget.refreshController.dataNormal =_dataNormal; + widget.refreshController.setPullDownEnable = _pullDownEnable; + widget.refreshController.setPullUpEnable = _pullUpEnable; + widget.refreshController.viewState = () { + return _viewState; + }; + widget.refreshController.setEditMode = (bool enable) { + setState(() { + editMode = enable; + }); + }; + + } + + //瑙嗗浘鐘舵�� 0-姝e父 1-绌鸿鍥� 2-缃戠粶璇锋眰閿欒 + RefreshListViewState _viewState = RefreshListViewState.normal; + + _apiError() { + setState(() { + _viewState = RefreshListViewState.error; + }); + } + + _dataEmpty() { + setState(() { + _viewState = RefreshListViewState.empty; + }); + } + + _dataNormal() { + setState(() { + _viewState = RefreshListViewState.normal; + }); + } + + _pullDownEnable(bool enable) { + setState(() { + widget.enablePullDown = enable; + }); + } + + _pullUpEnable(bool enable) { + setState(() { + widget.enablePullUp = enable; + }); + } + + void _onRefresh() async { + if (widget.refresh != null) { + widget.refresh!(); + } + } + + void _onLoading() async { + if (widget.loadMore != null) { + widget.loadMore!(); + } + } + + Widget getView() { + switch (_viewState) { + case RefreshListViewState.normal: + return contentView(); + case RefreshListViewState.empty: + return emptyView(); + case RefreshListViewState.error: + return errorView(); + } + } + + Widget contentView() { + return editMode + ? widget.content + : SmartRefresher( + enablePullDown: widget.enablePullDown!, + enablePullUp: widget.enablePullUp!, + header: const WaterDropHeader( + complete: Text( + "鍒锋柊瀹屾垚", + style: TextStyle(color: Color(0xFFB8AFB5)), + ), + ), + footer: CustomFooter( + builder: (BuildContext context, LoadStatus? mode) { + Widget body; + if (mode == LoadStatus.idle) { + body = const Text("", + style: TextStyle(color: Color(0xFFB8AFB5))); + } else if (mode == LoadStatus.loading) { + body = const CupertinoActivityIndicator(); + } else if (mode == LoadStatus.failed) { + body = const Text("鍔犺浇澶辫触锛佺偣鍑婚噸璇曪紒", + style: TextStyle(color: Color(0xFFB8AFB5))); + } else if (mode == LoadStatus.canLoading) { + body = const Text("鏉炬墜,鍔犺浇鏇村!", + style: TextStyle(color: Color(0xFFB8AFB5))); + } else { + body = const Text("娌℃湁鏇村鏁版嵁浜�!", + style: TextStyle(color: Color(0xFFB8AFB5))); + } + return SizedBox( + height: 55.0, + child: Center(child: body), + ); + }, + ), + controller: widget.refreshController, + onRefresh: _onRefresh, + onLoading: _onLoading, + child: widget.content, + ); + } + + Widget emptyView() { + return Container( + width: MediaQuery.of(context).size.width * 2 / 3, + alignment: Alignment.center, + // color: Colors.yellow, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Image.asset( + "assets/imgs/common/ic_empty.png", + ), + const Text( + "鏆傛棤鏁版嵁 ", + style: TextStyle(color: Color(0xFFF698C9), fontSize: 18), + ) + ], + ), + ); + } + + Widget errorView() { + return InkWell( + onTap: () { + _onRefresh(); + }, + child: Container( + alignment: Alignment.center, + // color: Colors.yellow, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Image.asset("assets/imgs/common/ic_network_error.png", + width: MediaQuery.of(context).size.width * 2 / 5), + const Text( + "缃戠粶寮傚父锛岀偣鍑婚噸鏂板姞杞�", + style: TextStyle(color: Color(0xFFF698C9), fontSize: 18), + ) + ], + ), + )); + } + + @override + Widget build(BuildContext context) { + return Stack(alignment: Alignment.center, children: [ + getView() +//缃戠粶閿欒椤甸潰 + ]); + } +} + +enum RefreshListViewState { normal, empty, error } diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/search_widget.dart b/src/main/resources/code/flutter_module/lib/ui/widget/search_widget.dart new file mode 100644 index 0000000..e10092d --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/search_widget.dart @@ -0,0 +1,313 @@ +import 'package:flutter/material.dart'; +import '../../ui/widget/nav.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; + +class SearchBar extends StatefulWidget { + final String? hint; + final String? text; + final ValueChanged<String>? onSubmit; + final ValueChanged<String>? onChange; + + final SearchController? searchController; + + SearchBar( + {Key? key, + this.hint, + this.text, + this.onSubmit, + this.onChange, + this.searchController}) + : super(key: key); + + @override + _SearchBarState createState() => _SearchBarState(); +} + +class _SearchBarState extends State<SearchBar> { + final TextEditingController _searchKeyController = TextEditingController(); + final FocusNode _focusNode = FocusNode(); + bool _showClose = false; + + @override + void initState() { + super.initState(); + _focusNode.addListener(() { + if (!_focusNode.hasFocus) { +// print('澶卞幓鐒︾偣'); + setState(() { + _showClose = false; + }); + } else { +// print('寰楀埌鐒︾偣'); + if (_searchKeyController.text.isNotEmpty) { + setState(() { + _showClose = true; + }); + } + } + }); + if (widget.text != null) { + _searchKeyController.text = widget.text!; + } + + if (widget.searchController != null) { + widget.searchController!.setData = (content) { + _searchKeyController.text = content; + _searchKeyController.selection=TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: content.length)); + }; + } + } + + @override + void dispose() { + _focusNode.unfocus(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: 11, + ), + Expanded( + child: Stack(children: [ + Container( + height: 34, + decoration: BoxDecoration( + color: Color(0xFFEEEDED), + borderRadius: BorderRadius.circular(17)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 13, + ), + Image.asset( + "assets/imgs/icon_search_home.png", + height: 17, + ), + Expanded( + child: TextField( + cursorRadius: const Radius.circular(1), + cursorColor: ColorConstant.theme, + maxLines: 1, + textInputAction: TextInputAction.search, + style: TextStyle( + color: Color(0xFF787878), + ), + decoration: InputDecoration( + hintText: widget.hint, + hintStyle: TextStyle( + color: Color(0xFF787878), + ), + border: InputBorder.none, + enabledBorder: InputBorder.none, + disabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 12)), + controller: _searchKeyController, + focusNode: _focusNode, + onSubmitted: (content) { + if (widget.onSubmit != null) { + widget.onSubmit!(content); + } + }, + onChanged: (content) { + if (content.isEmpty) { + setState(() { + _showClose = false; + }); + } else { + setState(() { + _showClose = true; + }); + } + + if (widget.onChange != null) { + widget.onChange!(content); + } + }, + )), + Container( + width: 13, + ), + ], + ), + ), + _showClose + ? Positioned( + right: 5, + top: 0, + bottom: 0, + child: InkWell( + onTap: () { + _searchKeyController.text = ""; + setState(() { + _showClose = false; + }); + }, + child: Icon( + Icons.highlight_off, + color: Color(0xFF787878), + ))) + : Container() + ])), + InkWell( + onTap: () { + popPage(context); + }, + child: Container( + height: 34, + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 20, right: 20), + child: const Text( + "鍙栨秷", + style: TextStyle(color: Color(0xFFFF558D)), + )), + ) + ], + ); + } +} + +class SugguestSearchView extends StatefulWidget { + final ValueChanged<String>? onCancel; + + final ValueChanged<String>? onItemClick; + + final List<String> contentList; + + final SugguestSearchController? sugguestSearchController; + + SugguestSearchView( + {Key? key, + this.onCancel, + this.onItemClick, + required this.contentList, + this.sugguestSearchController}) + : super(key: key); + + @override + _SugguestSearchViewState createState() => _SugguestSearchViewState(); +} + +class _SugguestSearchViewState extends State<SugguestSearchView> { + List<String> contentList = []; + bool _show = false; + + @override + void initState() { + super.initState(); + contentList.addAll(widget.contentList); + if (contentList.isNotEmpty) { + setState(() { + _show = true; + }); + } else { + setState(() { + _show = false; + }); + } + bindController(); + } + + void bindController() { + if (widget.sugguestSearchController == null) { + return; + } + widget.sugguestSearchController!.setData = (list) { + setState(() { + contentList = list; + }); + }; + widget.sugguestSearchController!.setShow = (show) { + setState(() { + _show = show; + }); + }; + } + + @override + Widget build(BuildContext context) { + return _show + ? Positioned( + top: MediaQuery.of(context).viewPadding.top + 39, + child: InkWell( + onTap: () { + if (widget.onCancel != null) { + setState(() { + _show = false; + }); + widget.onCancel!(""); + } + }, + child: Container( + color: Colors.transparent, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + padding: const EdgeInsets.fromLTRB(50, 0, 80, 0), + margin: EdgeInsets.all(0), + child: Column(children: [ + Container( + height: 40 * contentList.length + + (contentList.length - 1) * + DimenUtil.getOnePixel(context), + decoration: const BoxDecoration( + color: Color(0xFF999999), + boxShadow: [ + BoxShadow( + color: Color(0x30000000), + blurRadius: 10, + offset: Offset(0, 3)) + ]), + child: ListView.builder( + padding: EdgeInsets.all(0), + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + if (widget.onItemClick != null) { + widget.onItemClick!(contentList[index]); + } + }, + child: getSuggestSearchItem(contentList[index])); + }, + itemCount: contentList.length, + )), + Expanded(child: Container()) + ]), + ), + )) + : Container(); + } + + Widget getSuggestSearchItem(String text) { + return Container( + height: 40, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(bottom: DimenUtil.getOnePixel(context)), + color: Colors.white, + padding: EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Text( + text, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12, color: const Color(0xFF4A4A4A)), + ), + ); + } +} + +class SugguestSearchController { + ValueChanged<List<String>>? setData; + + ValueChanged<bool>? setShow; +} + +class SearchController { + ValueChanged<String>? setData; +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/sos_ui.dart b/src/main/resources/code/flutter_module/lib/ui/widget/sos_ui.dart new file mode 100644 index 0000000..0b9a24f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/sos_ui.dart @@ -0,0 +1,105 @@ +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +///SOS闆疯揪鎵弿View +class RadarView extends StatefulWidget { + @override + _RadarViewState createState() => _RadarViewState(); +} + +class _RadarViewState extends State<RadarView> + with SingleTickerProviderStateMixin { + AnimationController? _controller; + Animation<double>? _animation; + + @override + void initState() { + _controller = + AnimationController(vsync: this, duration: Duration(seconds: 2)); + _animation = Tween(begin: .0, end: pi * 2).animate(_controller!); + _controller!.repeat(); + super.initState(); + } + + @override + void dispose() { + _controller!.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation!, + builder: (context, child) { + return CustomPaint( + painter: RadarPainter(_animation!.value), + ); + }, + ); + } +} + +///SOS闆疯揪鎵弿鍔ㄧ敾 +class RadarPainter extends CustomPainter { + final double angle; + + final Paint _bgPaint = Paint() + ..color = Colors.white + ..strokeWidth = 1 + ..style = PaintingStyle.stroke; + + final Paint _paint = Paint()..style = PaintingStyle.fill; + + int circleCount = 0; + + RadarPainter(this.angle); + + @override + void paint(Canvas canvas, Size size) { + var radius = min(size.width / 2, size.height / 2); + + // canvas.drawLine(Offset(size.width / 2, size.height / 2 - radius), + // Offset(size.width / 2, size.height / 2 + radius), _bgPaint); + // canvas.drawLine(Offset(size.width / 2 - radius, size.height / 2), + // Offset(size.width / 2 + radius, size.height / 2), _bgPaint); + + for (var i = 1; i <= circleCount; ++i) { + canvas.drawCircle(Offset(size.width / 2, size.height / 2), + radius * i / circleCount, _bgPaint); + } + + _paint.shader = ui.Gradient.sweep( + Offset(size.width / 2, size.height / 2), + [Colors.white.withOpacity(.01), Colors.yellow.withOpacity(.6)], + [.0, 1.0], + TileMode.clamp, + .0, + pi / 4); + + canvas.save(); + double r = sqrt(pow(size.width, 2) + pow(size.height, 2)); + double startAngle = atan(size.height / size.width); + Point p0 = Point(r * cos(startAngle), r * sin(startAngle)); + Point px = Point(r * cos(angle + startAngle), r * sin(angle + startAngle)); + canvas.translate((p0.x - px.x) / 2, (p0.y - px.y) / 2); + canvas.rotate(angle); + + canvas.drawArc( + Rect.fromCircle( + center: Offset(size.width / 2, size.height / 2), radius: radius), + 0, + pi / 4, + true, + _paint); + canvas.restore(); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/src/main/resources/code/flutter_module/lib/ui/widget/video_item.dart b/src/main/resources/code/flutter_module/lib/ui/widget/video_item.dart new file mode 100644 index 0000000..971196b --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/ui/widget/video_item.dart @@ -0,0 +1,755 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import '../../model/video/home_type_model.dart'; +import '../../model/video/video_model.dart'; +import '../../ui/video/video_detail.dart'; +import '../../utils/jump_page.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/ui_constant.dart'; +import '../../utils/ui_utils.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import '../../utils/video/video_util.dart'; + +import '../../main.dart'; + +class VideoListUIUtil { + static Widget getHomeTypeItem( + double width, HomeTypeModel homeType, BuildContext context) { + double padding = 8; + width = width - padding * 2; + Widget videoList = Container(); + + List<Widget> list = + List.filled(homeType.homeVideoList!.length, Container()); + //濉厖澶у浘 + for (var i = 0; i < homeType.homeVideoList!.length; i++) { + HomeVideoModel homeVideo = homeType.homeVideoList![i]; + if (homeVideo.bigPicture!) { + list[i] = getVideoItemColumn_1(width, homeVideo, context, native: Platform.isIOS); + } else { + switch (homeType.columns) { + case 1: + list[i] = + getVideoItemColumn_1(width, homeVideo, context, native: Platform.isIOS); + break; + case 2: + list[i] = + getVideoItemColumn_2(width, homeVideo, context, native: Platform.isIOS); + break; + case 3: + list[i] = + getVideoItemColumn_3(width, homeVideo, context, native: Platform.isIOS); + break; + default: + list[i] = + getVideoItemColumn_2(width, homeVideo, context, native: Platform.isIOS); + } + } + } + + if (homeType.columns! <= 3) { + videoList = Wrap( + alignment: WrapAlignment.spaceBetween, + runSpacing: 10, + spacing: 7, + children: list, + ); + } else { + videoList = ListView.separated( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return getVideoItemColumn_n( + width, homeType.homeVideoList![index], context); + }, + itemCount: homeType.homeVideoList!.length, + separatorBuilder: (BuildContext context, int index) { + return Container( + width: 10, + ); + }, + ); + } + + return Container( + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: const EdgeInsets.only(top: 0, bottom: 10), + child: Row(children: [ + Text( + homeType.name!, + style: + const TextStyle(color: Color(0xFF5F5F5F), fontSize: 16), + ) + ])), + videoList, + ], + ), + ); + } + + static Widget getVideoItemColumn_1( + double mx, HomeVideoModel homeVideo, BuildContext context, + {bool native = false}) { + return InkWell( + onTap: () { + jumpVideoDetail(context, homeVideo.video, native); + }, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.5), + child: VideoImage( + (homeVideo.picture != null && homeVideo.picture!.isNotEmpty) + ? homeVideo.picture! + : ((homeVideo.video!.hpicture != null && + homeVideo.video!.hpicture!.isNotEmpty) + ? homeVideo.video!.hpicture + : homeVideo.video!.picture), + width: mx, + height: mx * 0.4382, + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(5, 5, 5, 5), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12.5), + bottomRight: Radius.circular(12.5)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0x00000000), + Color(0x80000000), + ], + ), + ), + child: Row( + children: [ + Text( + homeVideo.video!.tag!, + style: _getTagStyle(), + ), + Expanded( + child: Container(), + ), + _getScoreText("9") + ], + ), + ) + ], + )); + } + + static Widget getVideoItemColumn_2( + double mx, HomeVideoModel homeVideo, BuildContext context, + {bool native = false}) { + return InkWell( + onTap: () { + jumpVideoDetail(context, homeVideo.video, native); + }, + child: Container( + width: (mx - 7.5) / 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.bottomLeft, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.5), + child: VideoImage( + (homeVideo.picture != null && + homeVideo.picture!.isNotEmpty) + ? homeVideo.picture! + : ((homeVideo.video!.hpicture != null && + homeVideo.video!.hpicture!.isNotEmpty) + ? homeVideo.video!.hpicture + : homeVideo.video!.picture), + width: (mx - 7.5) / 2, + height: ((mx - 7.5) / 2) * 0.5629, + fit: BoxFit.cover, + ), + ), + Container( + padding: EdgeInsets.fromLTRB(5, 5, 5, 5), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12.5), + bottomRight: Radius.circular(12.5)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0x00000000), + Color(0x80000000), + ], + ), + ), + child: Row( + children: [ + Text( + homeVideo.video!.tag != null + ? homeVideo.video!.tag! + : "", + style: _getTagStyle()), + Expanded( + child: Container(), + ), + _getScoreText("9.3") + ], + ), + ) + ], + ), + Text( + homeVideo.video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: _getTitleStyle(), + ), + ], + ), + )); + } + + static Widget getVideoItemColumn_3( + double mx, HomeVideoModel homeVideo, BuildContext context, + {bool native = false}) { + return InkWell( + onTap: () { + jumpVideoDetail(context, homeVideo.video, native); + }, + child: Container( + width: (mx - 8 * 2) / 3, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.bottomLeft, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.5), + child: VideoImage( + (homeVideo.picture != null && + homeVideo.picture!.isNotEmpty) + ? homeVideo.picture! + : ((homeVideo.video!.vpicture != null && + homeVideo.video!.vpicture!.isNotEmpty) + ? homeVideo.video!.vpicture + : homeVideo.video!.picture), + width: (mx - 8) / 3, + height: ((mx - 8) / 3) * 1.4, + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(5, 5, 5, 5), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12.5), + bottomRight: Radius.circular(12.5)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0x00000000), + Color(0x80000000), + ], + ), + ), + child: Row( + children: [ + Expanded( + child: Text(homeVideo.video!.tag!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: _getTagStyle()), + ), + _getScoreText(homeVideo.video!.score) + ], + ), + ) + ], + ), + Text( + homeVideo.video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: _getTitleStyle(), + ) + ], + ), + )); + } + + static Widget getVideoItemColumn_n( + double mx, HomeVideoModel homeVideo, BuildContext context, + {bool native = false}) { + return InkWell( + onTap: () { + jumpVideoDetail(context, homeVideo.video, native); + }, + child: Container( + width: (mx - 7.5) / 2.5, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.bottomLeft, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.5), + child: VideoImage( + (homeVideo.picture != null && + homeVideo.picture!.isNotEmpty) + ? homeVideo.picture! + : ((homeVideo.video!.hpicture != null && + homeVideo.video!.hpicture!.isNotEmpty) + ? homeVideo.video!.hpicture + : homeVideo.video!.picture), + width: (mx - 7.5) / 2.5, + height: ((mx - 7.5) / 2.5) * 0.5629, + ), + ), + Container( + padding: EdgeInsets.fromLTRB(5, 5, 5, 5), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(12.5), + bottomRight: Radius.circular(12.5)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0x00000000), + Color(0x80000000), + ], + ), + ), + child: Row( + children: [ + Text(homeVideo.video!.tag!, style: _getTagStyle()), + Expanded( + child: Container(), + ), + _getScoreText(homeVideo.video!.score) + ], + ), + ) + ], + ), + Text( + homeVideo.video!.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: _getTitleStyle(), + ), + ], + ), + )); + } + + static Widget getSearchVideoAlbum( + BuildContext context, VideoInfoModel videoInfo) { + const double paddingLeft = 10; + const double paddingRight = 10; + double episodeWidth = (MediaQuery.of(context).size.width - + paddingRight - + paddingLeft - + 10 * 4 - + 1) / + 5; + + double episodeHeight = episodeWidth * 0.656; + + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.fromLTRB( + paddingLeft, + 0, + paddingRight, + 15, + ), + child: Column( + children: [ + SizedBox( + height: 154, + child: Row( + children: [ + //灏侀潰 + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + videoInfo.vpicture != null && + videoInfo.vpicture!.isNotEmpty + ? videoInfo.vpicture + : videoInfo.picture, + width: 112, + height: 154), + ), + Container( + width: 14, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + videoInfo.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFF232323), fontSize: 16), + )), + Container( + width: 10, + ), + _getAlbumScoreText(videoInfo.score) + ]), + Container( + height: 8, + ), + Text( + videoInfo.tag!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFFAEAEAE), fontSize: 12), + ), + Container( + height: 10, + ), + Text( + videoInfo.mainActor ?? "", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFFAEAEAE), fontSize: 12), + ), + Expanded(child: Container()), + Container( + alignment: Alignment.centerRight, + child: InkWell( + onTap: () { + jumpVideoDetail(context, videoInfo, false); + }, + child: Container( + width: 127, + height: 30, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: ColorConstant.theme), + child: const Text( + "绔嬪嵆瑙傜湅", + style: TextStyle( + color: Colors.white, fontSize: 14), + ), + ))) + ], + )) + ], + )), + //鍓ч泦 + videoInfo.videoDetailList!.isNotEmpty + ? Container( + alignment: Alignment.centerLeft, + margin: const EdgeInsets.only(top: 16), + child: Wrap( + alignment: WrapAlignment.start, + spacing: 10, + children: videoInfo.videoDetailList! + .map((e) => getTVEpisodeItem( + e.tag, episodeWidth, episodeHeight, context, + onClick: () { + jumpVideoDetail(context, videoInfo, false, + position: int.tryParse(e.tag!) != null + ? int.parse(e.tag!) + : 0); + })) + .toList())) + : Container() + ], + ), + ); + } + + static Widget getSearchVideoCommon( + BuildContext context, VideoInfoModel videoInfo) { + const double paddingLeft = 10; + const double paddingRight = 10; + return InkWell( + onTap: () { + jumpVideoDetail(context, videoInfo, false); + }, + child: Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.fromLTRB(paddingLeft, 20, paddingRight, 0), + child: Column( + children: [ + SizedBox( + height: 76, + child: Row( + children: [ + //灏侀潰 + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + videoInfo.picture, + width: 136, + ), + ), + Container( + width: 14, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoInfo.name!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Color(0xFF232323), fontSize: 16), + ), + Expanded(child: Container()), + Container( + alignment: Alignment.centerRight, + child: Text( + videoInfo.duration ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFFB8AFB5), fontSize: 12), + )) + ], + )) + ], + )), + ], + ), + )); + } + + static Widget getRecommendVideo( + BuildContext context, VideoInfoModel videoInfo) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + child: Column( + children: [ + Container( + height: 76, + child: Row( + children: [ + //灏侀潰 + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: VideoImage( + VideoUtil.getHPicture(videoInfo), + width: 136, + ), + ), + Container( + width: 14, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + videoInfo.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFF232323), fontSize: 16), + )), + Container( + width: 10, + ), + _getAlbumScoreText(videoInfo.score) + ]), + Text( + videoInfo.tag ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xFFAEAEAE), fontSize: 12), + ), + Expanded(child: Container()), + const Text( + "", + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: + TextStyle(color: Color(0xFFAEAEAE), fontSize: 12), + ), + ], + )) + ], + )), + ], + ), + ); + } + + static TextStyle _getTagStyle() { + return TextStyle(color: Colors.white, fontSize: 12); + } + + static TextStyle _getTitleStyle() { + return TextStyle(color: Color(0xFF464646), fontSize: 14); + } + + static TextStyle _getSubTitleStyle() { + return TextStyle(color: Color(0xFFB3ADB3), fontSize: 12); + } + + static Text _getScoreText(String? score) { + if (score == null || score.isEmpty) { + return const Text(""); + } + List<TextSpan> spanList = []; + spanList.add(TextSpan( + text: score.split(".")[0], + style: const TextStyle(fontSize: 13, color: Color(0xFFFB9F00)))); + if (score.split(".").length > 1) { + spanList.add(TextSpan( + text: ".${score.split(".")[1]}", + style: const TextStyle(fontSize: 9, color: Color(0xFFFB9F00)))); + } + spanList.add(const TextSpan( + text: "鍒�", style: TextStyle(fontSize: 8, color: Color(0xAAFB9F00)))); + return Text.rich(TextSpan(children: spanList)); + } + + static Text _getAlbumScoreText(String? score) { + if (score == null || score.isEmpty) { + return Text(""); + } + List<TextSpan> spanList = []; + spanList.add(TextSpan( + text: score, style: TextStyle(fontSize: 15, color: Color(0xFFFF9C00)))); + + spanList.add(const TextSpan( + text: "鍒�", style: TextStyle(fontSize: 12, color: Color(0xFFB8B8B8)))); + return Text.rich(TextSpan(children: spanList)); + } + + static Widget getTVEpisodeItem( + text, double width, double height, BuildContext context, + {checked: false, VoidCallback? onClick}) { + return InkWell( + onTap: () { + if (onClick != null) { + onClick(); + } + }, + child: Container( + width: width, + alignment: Alignment.center, + height: height, + decoration: BoxDecoration( + color: checked ? ColorConstant.theme : Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + width: checked ? 0 : DimenUtil.getOnePixel(context), + color: const Color(0xFFBBBBBB))), + child: Text(text, + style: TextStyle( + color: checked ? Colors.white : Color(0xFF232323), + fontSize: 16)), + )); + } + + static Widget getShowEpisodeItem(text, BuildContext context, + {checked: false, VoidCallback? onClick}) { + return InkWell( + onTap: () { + if (onClick != null) { + onClick(); + } + }, + child: Container( + padding: const EdgeInsets.all(10), + width: 200, + alignment: Alignment.topLeft, + decoration: BoxDecoration( + color: checked ? ColorConstant.theme : Colors.white, + borderRadius: BorderRadius.circular(10), + border: Border.all( + width: checked ? 0 : DimenUtil.getOnePixel(context), + color: const Color(0xFFBBBBBB))), + child: Text(text, + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: checked ? Colors.white : const Color(0xFF232323), + fontSize: 16)), + )); + } +} + +Widget VideoImage(url, {fit = BoxFit.cover, double? width, double? height}) { + return CachedNetworkImage( + imageUrl: url, + fit: fit, + width: width, + height: height, + placeholder: (context, st) => Container( + color: const Color(0xFFFFE8F0), + ), + errorWidget: (context, url, error) => const Icon(Icons.error), + ); +} + +Widget CommonImage(String url, + {fit = BoxFit.cover, + double? width, + double? height, + Widget? defaultWidget}) { + if (url.startsWith("http")) { + return CachedNetworkImage( + imageUrl: url, + fit: fit, + width: width, + height: height, + placeholder: (context, st) => + defaultWidget ?? + Container( + color: const Color(0xFFFFE8F0), + ), + errorWidget: (context, url, error) => + defaultWidget ?? const Icon(Icons.error), + ); + } else { + return Image.asset( + url, + width: width, + height: height, + fit: fit, + ); + } +} + +void jumpVideoDetail(BuildContext context, VideoInfoModel? video, bool native, + {int position = 0}) { + JumpPageUtil.jumpPage("VideoDetailPage", context, + params: {"position": position, "video": video!.toJson()}, native: native); +} diff --git a/src/main/resources/code/flutter_module/lib/utils/ad_util.dart b/src/main/resources/code/flutter_module/lib/utils/ad_util.dart new file mode 100644 index 0000000..b686b0d --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/ad_util.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import '../../utils/share_preference.dart'; +import '../../utils/string_util.dart'; + +MethodChannel adMethodChannel = const MethodChannel('com.yeshi.video/ad'); + +enum AdType { csj, gdt } + +enum AdPosition { + //鎺ㄨ崘 + other, + //鎼滅储 + videoSearch, + //鍏ㄥ睆瑙嗛 + videoDetailFullVideo, + videoPlayPre +} + +class CSJADConstant { + //鎺ㄨ崘棣栭〉澶у浘 + static const String PID_RECOMMEND_BIG_PICTURE = "948067685"; + //鎼滅储椤靛箍鍛� + static const String PID_VIDEO_SEARCH = "948119448"; + //鍏ㄥ睆瑙嗛骞垮憡 + static const String PID_VIDEO_DETAIL_FULLSCREEN = "948119553"; +} + +class GDTADConstant { + //鎺ㄨ崘棣栭〉澶у浘 + static const String PID_RECOMMEND_BIG_PICTURE = "5033406516973331"; + + //鎼滅储椤靛箍鍛� + static const String PID_VIDEO_SEARCH = "4033805974201049"; + + //鍏ㄥ睆瑙嗛骞垮憡 + static const String PID_VIDEO_DETAIL_FULLSCREEN = "6053800914701167"; +} + +class AdUtil { + static void loadFullScreenAd(AdType adType, String pid) { + if (adType == AdType.csj) { + adMethodChannel.invokeMethod("loadCSJFullScreenAd", {"pid": pid}); + } else { + adMethodChannel.invokeMethod("loadGDTFullScreenAd", {"pid": pid}); + } + } + + //鑾峰彇骞垮憡閰嶇疆淇℃伅 + static Future<Map<String, dynamic>?> _getAdConfig() async { + String result = await dataMethodChannel.invokeMethod("getAdConfig"); + print("骞垮憡閰嶇疆锛�$result"); + if (StringUtil.isNullOrEmpty(result)) { + return null; + } + return jsonDecode(result); + } + + static Future<AdType?> getAdType(AdPosition position) async { + String key = ""; + if (position == AdPosition.other) { + key = "other"; + } else if (position == AdPosition.videoSearch) { + key = "videoSearch"; + } else if (position == AdPosition.videoDetailFullVideo) { + key = "videoDetailFullVideo"; + } else if (position == AdPosition.videoPlayPre) { + key = "videoPlayPre"; + } + + Map<String, dynamic>? config = await _getAdConfig(); + if (config == null) { + return null; + } + + print("骞垮憡key:$key 绫诲瀷${config[key]}"); + if (config[key] == null) { + return null; + } + if (config[key]["type"] == "csj") { + return AdType.csj; + } + + if (config[key]["type"] == "gdt") { + return AdType.gdt; + } + + return null; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/app_util.dart b/src/main/resources/code/flutter_module/lib/utils/app_util.dart new file mode 100644 index 0000000..40ce8ab --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/app_util.dart @@ -0,0 +1,70 @@ +import 'dart:io'; + +import 'package:device_info/device_info.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart'; +import 'package:package_info/package_info.dart'; + +import 'global.dart'; + +class AppUtil { + + static bool _inited = false; + + //鍒濆鍖栧簲鐢� + static Future<bool> initApp(BuildContext context) async { + if (_inited) { + return true; + } + _inited = true; + print("initApp"); + await registerWxApi( + appId: "wxd930ea5d5a228f5f", + universalLink: "https://your.univerallink.com/link/"); + + //鍒濆鍖栧箍鍛� + // await AdUtil.init(context); + + //鍒濆鍖栨湰鍦板簲鐢� + await _initNativeApp(); + + //鍒濆鍖栫増鏈� + if (Platform.isAndroid) { + DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + AndroidDeviceInfo _androidInfo = await deviceInfo.androidInfo; + Global.androidSDK = _androidInfo.version.sdkInt; + } + + // //鍒濆鍖栭樋閲屼簯鎺堟潈鐧诲綍 + // await LoginPage.messageChannel.send({ + // "method": "init", + // "secret": Constant.ALIYUN_AUTH_SECRETINFO, + // "privacy": Constant.PRIVACY_URL, + // "protocol": Constant.PROTOCOL_URL + // }) as Map; + + return true; + } + + //鏈湴搴旂敤鍒濆鍖� + static _initNativeApp() async { + if (Platform.isAndroid) { + const platform = MethodChannel("com.yeshi.location/init"); //鍒嗘瀽1 + try { + await platform.invokeMethod("initApp"); //鍒嗘瀽2 + } on PlatformException catch (e) { + print(e.toString()); + } + //濉厖utdid + await Global.loadUtdId(); + //濉厖channel + await Global.loadChannel(); + } + } + + static Future<int> getVersionCode() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + return int.parse(packageInfo.buildNumber); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/cache_util.dart b/src/main/resources/code/flutter_module/lib/utils/cache_util.dart new file mode 100644 index 0000000..7b3bcba --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/cache_util.dart @@ -0,0 +1,57 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; + +/// 缂撳瓨绠$悊绫� +/// ./lib/utils/cache_util.dart +class CacheUtil { + /// 鑾峰彇缂撳瓨澶у皬 + static Future<int> total() async { + Directory tempDir = await getTemporaryDirectory(); + if (tempDir == null) return 0; + int total = await _reduce(tempDir); + return total; + } + + /// 娓呴櫎缂撳瓨 + static Future<void> clear() async { + Directory tempDir = await getTemporaryDirectory(); + if (tempDir == null) return; + await _delete(tempDir); + } + + /// 閫掑綊缂撳瓨鐩綍锛岃绠楃紦瀛樺ぇ灏� + static Future<int> _reduce(final FileSystemEntity file) async { + /// 濡傛灉鏄竴涓枃浠讹紝鍒欑洿鎺ヨ繑鍥炴枃浠跺ぇ灏� + if (file is File) { + int length = await file.length(); + return length; + } + + /// 濡傛灉鏄洰褰曪紝鍒欓亶鍘嗙洰褰曞苟绱澶у皬 + if (file is Directory) { + final List<FileSystemEntity> children = file.listSync(); + + int total = 0; + + if (children != null && children.isNotEmpty) + for (final FileSystemEntity child in children) + total += await _reduce(child); + + return total; + } + + return 0; + } + + /// 閫掑綊鍒犻櫎缂撳瓨鐩綍鍜屾枃浠� + static Future<void> _delete(FileSystemEntity file) async { + if (file is Directory) { + final List<FileSystemEntity> children = file.listSync(); + for (final FileSystemEntity child in children) { + await _delete(child); + } + } else { + await file.delete(); + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/config_util.dart b/src/main/resources/code/flutter_module/lib/utils/config_util.dart new file mode 100644 index 0000000..ead80d0 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/config_util.dart @@ -0,0 +1,56 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import '../../api/config_api.dart'; +import '../api/http.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ConfigUtil { + ///淇濆瓨閰嶇疆淇℃伅 + static void saveConfig(Map<String, dynamic> map) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString("config_value", jsonEncode(map)); + } + + static Future<String?> getConfig(BuildContext context, String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + String? result = prefs.getString("config_value"); + if (result != null) { + Map<String, dynamic> map = jsonDecode(result); + return map[key]; + } else { + //閲嶆柊璇锋眰 + ConfigApiUtil.getConfig(context).then((value) { + if (value == null) { + return; + } + if (value["code"] == 0) { + saveConfig(value["data"]); + } + }); + } + return null; + } +} + +class ConfigKey { + //瀹㈡湇 + static const String kefu = "kefu"; + + //鏁欑▼ + static const String course = "course"; + + //娉ㄩ攢 + static const String unRegister = "unRegister"; + + //闅愮鎶曡瘔 + static const String privacyComplain = "privacyComplain"; + + //浼氬憳閾炬帴 + static const String vipLink = "vipLink"; + + //涓夋柟SDK閾炬帴 + static const String sdkList = "sdkList"; +} + +enum SharePlatform { wx, wxcircle, qq, qqzone, sina } diff --git a/src/main/resources/code/flutter_module/lib/utils/db_manager.dart b/src/main/resources/code/flutter_module/lib/utils/db_manager.dart new file mode 100644 index 0000000..038581e --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/db_manager.dart @@ -0,0 +1,99 @@ +import 'dart:convert'; + +import '../../model/video/video_model.dart'; +import '../../model/video/watch_record_model.dart'; + +import 'sqlite_utils.dart'; + +class DBManager { + ///鍒濆鍖� + static List<String> getTables() { + List<String> tables = [ + ''' + CREATE TABLE IF NOT Exists WATCH_RECORD( + _ID INTEGER NOT NULL PRIMARY KEY, + _VIDEO_ID TEXT NOT NULL UNIQUE, + _VIDEOS TEXT NOT NULL, + _VIDEO_DETAIL TEXT NOT NULL, + _POSITION INTEGER NOT NULL, + _CREATE_TIME INTEGER NOT NULL, + _UPDATE_TIME INTEGER NOT NULL + ); + ''' + ]; + return tables; + } + + ///娣诲姞瑙傜湅璁板綍 + static void addWatchRecord(VideoInfoModel videoInfo, + VideoDetailInfo detailInfo, int position) async { + VideoInfoModel temp=VideoInfoModel.fromJson(videoInfo.toJson()); + temp.videoDetailList = null; + List<Map> resultList = await SQLiteUtil.select( + "SELECT * FROM WATCH_RECORD where _VIDEO_ID = ?", [temp.id]); + if (resultList.isEmpty) { + String sql = + "INSERT INTO WATCH_RECORD(_VIDEO_ID,_VIDEOS,_VIDEO_DETAIL,_POSITION,_CREATE_TIME,_UPDATE_TIME) VALUES(?,?,?,?,?,?)"; + await SQLiteUtil.insert(sql, [ + [ + temp.id!, + jsonEncode(temp.toJson()), + jsonEncode(detailInfo.toJson()), + position, + DateTime.now().millisecondsSinceEpoch, + DateTime.now().millisecondsSinceEpoch, + ] + ]); + } else { + String sql = + "UPDATE WATCH_RECORD SET _VIDEOS=?,_VIDEO_DETAIL=?,_POSITION=?,_UPDATE_TIME=? WHERE _ID=?"; + await SQLiteUtil.executeSQLWithParams(sql, [ + jsonEncode(temp.toJson()), + jsonEncode(detailInfo.toJson()), + position, + DateTime.now().millisecondsSinceEpoch, + resultList[0]["_ID"] + ]); + } + } + + ///鍒犻櫎瑙傜湅璁板綍 + static Future deleteWatchRecord(List<String> ids) async { + String sql = "DELETE FROM WATCH_RECORD WHERE "; + for (var element in ids) { + sql += " _ID = ? OR"; + } + if (sql.endsWith("OR")) { + sql = sql.substring(0, sql.length - 2); + } + await SQLiteUtil.executeSQLWithParams(sql, ids); + } + + ///鑾峰彇瑙傜湅璁板綍鍒楄〃 + static Future<List<WatchRecordModel>> listWatchRecord( + int page, int pageSize) async { + String sql = + "select * from WATCH_RECORD ORDER BY _UPDATE_TIME DESC LIMIT ?,?"; + List<Map> result = + await SQLiteUtil.select(sql, [(page - 1) * pageSize, pageSize]); + List<WatchRecordModel> list = []; + result.forEach((element) { + list.add(WatchRecordModel( + id: element["_ID"], + videoId: element["_VIDEO_ID"], + video: VideoInfoModel.fromJson(jsonDecode(element["_VIDEOS"])), + videoDetail: + VideoDetailInfo.fromJson(jsonDecode(element["_VIDEO_DETAIL"])), + position: element["_POSITION"], + createTime: element["_CREATE_TIME"], + updateTime: element["_UPDATE_TIME"], + )); + }); + return list; + } + + static Future<int> countWatchRecord() async { + String sql = "select count(*) from WATCH_RECORD"; + return await SQLiteUtil.count(sql, []); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/des/crypto_util.dart b/src/main/resources/code/flutter_module/lib/utils/des/crypto_util.dart new file mode 100644 index 0000000..9672e32 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/des/crypto_util.dart @@ -0,0 +1,34 @@ +import 'dart:typed_data'; + +class CryptoUtil { + static List<int>? hex2List(String hexStr) { + if (hexStr == null || hexStr.length % 2 != 0) { + //鍗佸叚杩涘埗瀛楃涓查敊璇� + return null; + } + + if (hexStr.startsWith('0x')) { + hexStr = hexStr.substring(2); + } + List<int> result = List.filled(hexStr.length ~/ 2,0); + String temp = '0123456789ABCDEF'; + for (int i = 0; i < hexStr.length; i += 2) { + int h = temp.indexOf(hexStr.substring(i, i + 1)); + int l = temp.indexOf(hexStr.substring(i + 1, i + 2)); + result[i ~/ 2] = (h * 16 + l); + } + return result; + } + + static String list2Hex(List<int> list) { + List<String> results = List.filled(list.length * 2,""); + String temp = '0123456789ABCDEF'; + for (int i = 0; i < list.length; i++) { + int h = list[i] ~/ 16; + int l = list[i] % 16; + results[i * 2] = temp.substring(h, h + 1); + results[i * 2 + 1] = temp.substring(l, l + 1); + } + return results.join(); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/des/des.dart b/src/main/resources/code/flutter_module/lib/utils/des/des.dart new file mode 100644 index 0000000..6b590fc --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/des/des.dart @@ -0,0 +1,415 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'crypto_util.dart'; +import 'number_utils.dart'; +import 'padding.dart'; + +/// author: karedem +/// 鍙傝�冭嚦: https://blog.csdn.net/yxtxiaotian/article/details/52025653 +/// 浠ュ強 https://www.cnblogs.com/songwenlong/p/5944139.html +/// +class DES { + static const String _iv = '01234567'; + static const BLOCK_SIZE = 8; + List<List<int>>? dispareKeys; + static const E_box = [ + //E + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 + ]; + + static const IP = [ + //IP + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 + ]; + + static const IP_1 = [ + //IP_R + 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 + ]; + + static const PC_1 = [ + //PC_1 + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 + ]; + + static const PC_2 = [ + //PC_2 + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 + ]; + + static const S_Box = [ + [ + // S1 + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + ], + [ + //S2 + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + ], + [ + //S3 + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + ], + [ + //S4 + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + ], + [ + //S5 + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + ], + [ + //S6 + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + ], + [ + //S7 + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + ], + [ + //S8 + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + ] + ]; + + static const P_Box = [ + //P_box + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 + ]; + + static const shift_digit = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]; + + ///灏�64瀛楄妭鐨勫瘑閽ュ帇缂╂垚56瀛楄妭 瀛楄妭鏁扮粍杞负浜岃繘鍒舵暟缁� + List<int> _compressKeyTo56(List<int> key) { + List<int> bitKey = List.filled(56,0); + for (int i = 0; i < 56; i++) { + int realIndex = PC_1[i] - 1; + bitKey[i] = (key[realIndex >> 3] >> (7 - realIndex & 7)) & 1; + } + return bitKey; + } + + ///绂绘暎寰楀埌16涓瓙瀵嗛挜 + List<List<int>> dispareKey(List<int> compressKey) { + List<List<int>> dispareKeys = List.filled(16,[]); + List<int> c0 = compressKey.sublist(0, 28); + List<int> d0 = compressKey.sublist(28); + List<int> tempc = c0; + List<int> tempd = d0; + for (int i = 0; i < 16; i++) { + tempc = left_shift(i, tempc); + tempd = left_shift(i, tempd); + List<int> tempAll = []; + tempAll.addAll(tempc); + tempAll.addAll(tempd); + + List<int> dispareKey = compressDispareKey(tempAll); + dispareKeys[i] = dispareKey; + } + return dispareKeys; + } + + ///宸︾Щ鍑芥暟 闇�瑕佹牴鎹鏁� 宸︾Щ + static List<int> left_shift(int times, List<int> key) { + ///闇�绉诲姩鐨勪綅鏁� + int shift_length = shift_digit[times]; + List<int> newList = key.sublist(shift_length); + newList.addAll(key.sublist(0, shift_length)); + return newList; + } + + ///灏�56浣嶇殑瀵嗛挜鍘嬬缉涓�48浣� 浜岃繘鍒舵暟缁� 澶勭悊 + List<int> compressDispareKey(List<int> dispareKey) { + List<int> bitKey = List.filled(48,0); + for (int i = 0; i < 48; i++) { + int realIndex = PC_2[i] - 1; + bitKey[i] = dispareKey[realIndex]; + } + return bitKey; + } + + //01001 9 1111 1111 >> 6 + + ///鏄庢枃杞崲 瀛楄妭鏁扮粍杞簩杩涘埗鏁扮粍 + List<int> compressPlain(List<int> plain) { + List<int> bitKey = List.filled(64,0); + for (int i = 0; i < 64; i++) { + int realIndex = IP[i] - 1; + bitKey[i] = (plain[realIndex >> 3] >> (7 - realIndex & 7)) & 1; + } + return bitKey; + } + + ///E鐩掓墿灞� + List<int> E_transform(List<int> list) { + ///宸﹀崐閮ㄥ垎涓� L0 鍙冲崐閮ㄥ垎涓篟0 + //print("before E transform : " + list.toString()); + List<int> result = List.filled(48,0); + for (int i = 0; i < 48; i++) { + result[i] = list[E_box[i] - 1]; + } + //print("after E transform : " + result.toString()); + return result; + } + + ///P鐩掔疆鎹� + List<int> P_transform(List<int> list) { + List<int> bitKey = List.filled(32,0); + for (int i = 0; i < 32; i++) { + int realIndex = P_Box[i] - 1; + bitKey[i] = list[realIndex]; + } + return bitKey; + } + + ///S鐩掑彉鎹� 浜岃繘鍒剁粨鏋� + List<int> _S_Box_transform(List<int> list) { + ///check list length 48 + List<int> result = List.filled(32,0); + for (int i = 0; i < list.length; i += 6) { + int x = (list[i + 1] << 3 | + list[i + 2] << 2 | + list[i + 3] << 1 | + list[i + 4]); + int y = (list[i] << 1 | list[i + 5]); + int i_n = S_Box[i ~/ 6][(y << 4) + x]; + result[(i << 1) ~/ 3] = (i_n >> 3) & 1; + result[(i << 1) ~/ 3 + 1] = (i_n >> 2) & 1; + result[(i << 1) ~/ 3 + 2] = (i_n >> 1) & 1; + result[(i << 1) ~/ 3 + 3] = i_n & 1; + } + //print('before S box : ${list.toString()}'); + //print('after S box : ${result.toString()}'); + return result; + } + + ///寮傛垨 + List<int> _XOR_with_Left(List<int> left, List<int> presult) { + List<int> result = List.filled(left.length,0); + for (int i = 0; i < left.length; i++) { + result[i] = left[i] ^ presult[i]; + } + return result; + } + + ///IP_1缃崲 + List<int> _IP_1_transform(List<int> list) { + ///check list length 48 + List<int> bitKey = List.filled(64,0); + for (int i = 0; i < 64; i++) { + int realIndex = IP_1[i] - 1; + bitKey[i] = list[realIndex]; + } + return bitKey; + } + + void initKey(List<int> key) { + if (dispareKeys != null) { + return; + } + List<int> pc1_key = _compressKeyTo56(key); + dispareKeys = dispareKey(pc1_key); + } + + /// ECB鍔犲瘑 鍔犲瘑缁撴灉涓哄崄鍏繘鍒� 榛樿涓篜KCS7濉厖 + /// plain : 寰呭姞瀵嗘暟鎹� (utf-8) + /// hexKey : 鍗佸叚杩涘埗瀵嗛挜 + String encryptToHexWithECB(String plain, String hexKey) { + return CryptoUtil.list2Hex(encrypWithEcb( + Utf8Encoder().convert(plain).toList(), CryptoUtil.hex2List(hexKey)!)); + } + + /// CBC鍔犲瘑 鍔犲瘑缁撴灉涓哄崄鍏繘鍒� 榛樿涓篜KCS7濉厖 + /// plain : 寰呭姞瀵嗘暟鎹� (utf-8) + /// hexKey : 鍗佸叚杩涘埗瀵嗛挜 + /// iv : 鍚戦噺 (utf-8 榛樿鍊间负_iv) + String encryptToHexWithCBC(String plain, String hexKey, {String iv = _iv}) { + return CryptoUtil.list2Hex(encryptWithCBC( + Utf8Encoder().convert(plain).toList(), CryptoUtil.hex2List(hexKey)!, + iv: iv)); + } + + /// CBC瑙e瘑 + /// cipher : 寰呰В瀵嗘暟鎹� (鍗佸叚杩涘埗) + /// hexKey : 鍗佸叚杩涘埗瀵嗛挜 + /// iv : 鍚戦噺 (utf-8 榛樿鍊间负_iv) + String decryptFromHexWithCBC(String cipher, String hexKey, + {String iv = _iv}) { + return Utf8Decoder().convert(decryptWithCBC( + CryptoUtil.hex2List(cipher)!, CryptoUtil.hex2List(hexKey)!, + iv: iv)); + } + + /// ECB瑙e瘑 + /// cipher : 寰呰В瀵嗘暟鎹� (鍗佸叚杩涘埗) + /// hexKey : 鍗佸叚杩涘埗瀵嗛挜 + String decryptFromHexWithECB(String cipher, String hexKey, + {String iv = _iv}) { + return Utf8Decoder().convert(decryptWithEcb( + CryptoUtil.hex2List(cipher)!, CryptoUtil.hex2List(hexKey)!)); + } + + ///鍔犲瘑 鏄庢枃瀛楄妭鏁扮粍 瀵嗛挜瀛楄妭鏁扮粍 + List<int> _encryptBlock(List<int> block) { + ///dispareKey right! + //print("block " + block.toString()); + var plainCompressed = compressPlain(block); + //print("plainCompressed " + plainCompressed.toString()); + List<int> L0 = plainCompressed.sublist(0, 32); + List<int> R0 = plainCompressed.sublist(32); + List<int> L0Z = L0; + List<int> R0Z = R0; + + for (int i = 0; i < 16; i++) { + var ln = R0Z; + var pResult = P_transform( + _S_Box_transform(_XOR_with_Left(dispareKeys![i], E_transform(R0Z)))); + + ///P鐩掔疆鎹㈢殑缁撴灉鐩扡0鍋氬紓鎴� + var rn = _XOR_with_Left(pResult, L0Z); + L0Z = ln; + R0Z = rn; + } + List<int> result = []; + result.addAll(R0Z); + result.addAll(L0Z); + result = _IP_1_transform(result); + return NumberUtils.intListFromBits(result); + } + + List<int> encryptWithCBC(List<int> plain, List<int> key, {String iv = _iv}) { + int allLen = plain.length; + int blockCount = allLen >> 3; + List<int> padPlain = plain.sublist(0, blockCount << 3); + padPlain.addAll(Padding.pkcs7Padding(plain.sublist(blockCount << 3))); + //List<int> blockCipher = List(padPlain.length); + List<int> blockCipher = []; + List<int> tempIv = iv.codeUnits; + initKey(key); + for (int i = 0; i < padPlain.length; i += BLOCK_SIZE) { + List<int> xorPlain = + _XOR_with_Left(padPlain.sublist(i, i + BLOCK_SIZE), tempIv); + tempIv = _encryptBlock(xorPlain); + blockCipher.addAll(tempIv); + } + return blockCipher; + } + + List<int> decryptWithCBC(List<int> cipher, List<int> key, {String iv = _iv}) { + List<int> plain = []; + List<int> tempIv = iv.codeUnits; + initKey(key); + for (int i = 0; i < cipher.length; i += BLOCK_SIZE) { + if (i == cipher.length - BLOCK_SIZE) { + List<int> plainXor = decryptBlock(cipher.sublist(i, i + BLOCK_SIZE)); + List<int> plainBlock = + Padding.pkcs7UnPadding(_XOR_with_Left(plainXor, tempIv)); + tempIv = cipher.sublist(i, i + BLOCK_SIZE); + plain.addAll(plainBlock); + } else { + List<int> plainXor = decryptBlock(cipher.sublist(i, i + BLOCK_SIZE)); + List<int> plainBlock = _XOR_with_Left(plainXor, tempIv); + tempIv = cipher.sublist(i, i + BLOCK_SIZE); + plain.addAll(plainBlock); + } + } + return plain; + } + + ///鍔犲瘑 鏄庢枃瀛楄妭鏁扮粍 瀵嗛挜瀛楄妭鏁扮粍 + List<int> encrypWithEcb(List<int> plain, List<int> key) { + List<int> blockCipher = []; + int allLen = plain.length; + int blockCount = allLen >> 3; + List<int> padPlain = plain.sublist(0, blockCount << 3).toList(); + padPlain.addAll(Padding.pkcs7Padding(plain.sublist(blockCount << 3))); + initKey(key); + for (int i = 0; i < padPlain.length; i += BLOCK_SIZE) { + blockCipher.addAll(_encryptBlock(padPlain.sublist(i, i + BLOCK_SIZE))); + } + return blockCipher; + } + + List<int> decryptBlock(List<int> cipher) { + var plainCompressed = compressPlain(cipher); + List<int> L0 = plainCompressed.sublist(0, plainCompressed.length >> 1); + List<int> R0 = plainCompressed.sublist(plainCompressed.length >> 1); + List<int> L0Z = L0; + List<int> R0Z = R0; + for (int i = 0; i < 16; i++) { + var ln = R0Z; + var pResult = P_transform(_S_Box_transform( + _XOR_with_Left(dispareKeys![15 - i], E_transform(R0Z)))); + var rn = _XOR_with_Left(pResult, L0Z); + L0Z = ln; + R0Z = rn; + } + List<int> result = []; + result.addAll(R0Z); + result.addAll(L0Z); + result = _IP_1_transform(result); + return NumberUtils.intListFromBits(result); + } + + ///鏀寔闀挎暟鎹В瀵� + List<int> decryptWithEcb(List<int> cipher, List<int> key) { + List<int> plain = []; + initKey(key); + for (int i = 0; i < cipher.length; i += BLOCK_SIZE) { + if (i == cipher.length - BLOCK_SIZE) { + plain.addAll(Padding.pkcs7UnPadding( + decryptBlock(cipher.sublist(i, i + BLOCK_SIZE)))); + } else { + plain.addAll(decryptBlock(cipher.sublist(i, i + BLOCK_SIZE))); + } + } + return plain; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/des/number_utils.dart b/src/main/resources/code/flutter_module/lib/utils/des/number_utils.dart new file mode 100644 index 0000000..4ef5b41 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/des/number_utils.dart @@ -0,0 +1,95 @@ +import 'dart:math'; + +class NumberUtils { + ///浜岃繘鍒舵暟缁勮浆瀛楄妭鏁扮粍 + static List<int> intListFromBits(List<int> bits) { + //print("${bits.toString()} ${bits.length ~/ 8}"); + List<int> result = List.generate(bits.length >> 3, (index) => 0); + for (int i = 0; i < bits.length; i++) { + result[i >> 3] |= (bits[i] << (7 - i & 7)); + } + return result; + } + + ///瀛楄妭鏁扮粍杞簩杩涘埗鏁扮粍 + static List<int> bitsFromIntList(List<int> ints) { + List<int> result = List.filled(ints.length << 3,0); + for (int i = 0; i < (ints.length << 3); ++i) { + result[i] = (ints[i >> 3] >> (7 - i & 7)) & 1; + } + return result; +// List<int> result = []; +// ints.forEach((i) { +// result.addAll(to8Bit(i)); +// }); +// return result; + } + + ///浜岃繘鍒跺瓧绗︿覆杞暟瀛� + static int intFromBits(List<int> bits) { + //return int.parse(bits.join(), radix: 2); + int result = 0; + for (int i = 0; i < bits.length; i++) { + result |= (bits[i] << (7 - i & 7)); + } + return result; + } + + ///to8Bit 01100100 + static List<int> t8Bit(List<int> bytes) { + List<int> result = List.filled(8 * bytes.length,0); + for (int i = 0; i < 8 * bytes.length; ++i) { + result[i] = (bytes[i >> 3] >> (7 - i & 7)) & 1; + } + return result; + } + + /// 4 -> 00000100 + static List<int> to8Bit(int num) { + List<int> result = List.filled(8,0); + for (int i = 0; i < 8; i++) { + result[i] = (num >> (7 - i & 7)) & 1; + } + return result; + + String temp = num.toRadixString(2); + int zeroLen = 8 - temp.length; + for (int i = 0; i < 8; i++) { + if (i < zeroLen) { + result[i] = 0; + } else { + result[i] = (temp.codeUnitAt(i - zeroLen) - 48); + } + } + return result; + } + + /// 4 -> 0100 + static List<int> to4Bit(int num) { + List<int> result = List.filled(4,0); + for (int i = 0; i < 4; i++) { + result[i] = (num >> (3 - i & 3)) & 1; + } + return result; + + String temp = num.toRadixString(2); + temp.codeUnits.forEach((code) { + result.add(code - 48); + }); + int len = result.length; + if (len < 4) { + result.insertAll(0, List.generate(4, (index) => 0).sublist(len)); + } + return result; + } + + static String formatSeeds(int seeds) { + if (seeds > 9999) { + return '${(seeds / 10000.0).toStringAsFixed(1)}w'; + } else if (seeds > 999) { + return '${(seeds / 1000.0).toStringAsFixed(1)}k'; + } else { + return '$seeds'; + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/des/padding.dart b/src/main/resources/code/flutter_module/lib/utils/des/padding.dart new file mode 100644 index 0000000..6bb4052 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/des/padding.dart @@ -0,0 +1,19 @@ +class Padding { + static List<int> pkcs7Padding(List<int> data) { + int fillLen = 8 - data.length % 8; + List<int> padDate = data.toList(); + for (int i = 0; i < fillLen; i++) { + padDate.add(fillLen); + } + return padDate; + } + + static List<int> pkcs7UnPadding(List<int> padData) { + int fillLen = padData.last; + if (fillLen < 0 || fillLen > 8) { + return padData; + } + List<int> data = padData.sublist(0, padData.length - fillLen); + return data; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/encrypt_util.dart b/src/main/resources/code/flutter_module/lib/utils/encrypt_util.dart new file mode 100644 index 0000000..c97e1e9 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/encrypt_util.dart @@ -0,0 +1,10 @@ +import 'dart:convert'; +import 'package:crypto/crypto.dart'; + +class EncryptUtil { + static String MD5(String data) { + return md5.convert(utf8.encode(data)).toString(); + } + + +} diff --git a/src/main/resources/code/flutter_module/lib/utils/event_bus_util.dart b/src/main/resources/code/flutter_module/lib/utils/event_bus_util.dart new file mode 100644 index 0000000..cd4c691 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/event_bus_util.dart @@ -0,0 +1,10 @@ +import 'package:event_bus/event_bus.dart'; +EventBus eventBus = EventBus(); + + + +class LoginEventBus { + final bool isLogin; + LoginEventBus(this.isLogin); +} + diff --git a/src/main/resources/code/flutter_module/lib/utils/global.dart b/src/main/resources/code/flutter_module/lib/utils/global.dart new file mode 100644 index 0000000..34aea0f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/global.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +//鍏ㄥ眬璺宠浆 +final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); +class Global { + static const messageChannel = + BasicMessageChannel('DeviceUtil', StandardMessageCodec()); + + static String? utdId; + + static String? channel; + + //android鐨勭増鏈� + static int? androidSDK; + + static Future loadUtdId() async { + String? value = + await messageChannel.send({"method": "getUtdid"}) as String?; + utdId = value; + } + + static Future loadChannel() async { + String? value = + await messageChannel.send({"method": "getChannel"}) as String?; + channel = value; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/image_util.dart b/src/main/resources/code/flutter_module/lib/utils/image_util.dart new file mode 100644 index 0000000..0ca9722 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/image_util.dart @@ -0,0 +1,36 @@ +//鍥剧墖宸ュ叿 +import 'package:flutter/material.dart'; +import '../../utils/ui_constant.dart'; +import 'package:image_cropper/image_cropper.dart'; +import 'dart:io'; +import 'package:image_picker/image_picker.dart'; + +class ImageUtil{ + + //閫夋嫨骞惰鍓浘鐗� + static Future<File?> selectAndCropImage() async{ + final ImagePicker _picker = ImagePicker(); + final XFile? image = await _picker.pickImage(source: ImageSource.gallery); + ImageCropper cropper=ImageCropper(); + File? croppedFile = await cropper.cropImage( + cropStyle:CropStyle.circle, + sourcePath: image!.path, + aspectRatioPresets: [ + CropAspectRatioPreset.square + ], + androidUiSettings: const AndroidUiSettings( + toolbarTitle: 'Cropper', + toolbarColor: ColorConstant.theme, + toolbarWidgetColor: Colors.white, + initAspectRatio: CropAspectRatioPreset.square, + lockAspectRatio: true), + iosUiSettings: const IOSUiSettings( + minimumAspectRatio: 1.0, + ) + ); + return croppedFile; + } + + + +} \ No newline at end of file diff --git a/src/main/resources/code/flutter_module/lib/utils/jsinterface.dart b/src/main/resources/code/flutter_module/lib/utils/jsinterface.dart new file mode 100644 index 0000000..712a6cc --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/jsinterface.dart @@ -0,0 +1,250 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import '../api/http.dart' as http; +import '../utils/encrypt_util.dart'; +import '../utils/permission_util.dart'; +import '../utils/share_utils.dart'; +import '../utils/string_util.dart'; +import '../utils/ui_constant.dart'; +import '../utils/ui_utils.dart'; +import '../utils/user_util.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:webview_flutter/platform_interface.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:path_provider/path_provider.dart'; +import '../ui/widget/nav.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; + +import 'config_util.dart'; +import 'package:package_info/package_info.dart'; + +class JavascriptInterface { + final BuildContext context; + final WebViewController? _controller; + + JavascriptInterface( + BuildContext this.context, WebViewController? this._controller); + + Set<JavascriptChannel> getInterfaces() { + List<JavascriptChannel> list = []; + list.add(JavascriptChannel( + name: 'yestv', + onMessageReceived: (JavascriptMessage message) { + print("onMessageReceived"); + var data = jsonDecode(message.message); + String method = data["method"]; + var params = data["params"]; + String? _callback = data["callback"]; + switch (method) { + case "toast": + toast(params); + break; + case "copyText": + copyText(params); + break; + case "getUid": + getUid(params, _callback); + break; + case "getAppName": + getAppName(_callback); + break; + case "getRequestBaseParams": + getRequestBaseParams(params, _callback); + break; + case "showRewardVideoAd": + showRewardVideoAd(this.context, _callback); + break; + case "showLoading": + showLoading(); + break; + case "hideLoading": + hideLoading(); + break; + case "finishPage": + finishPage(); + break; + case "saveImg": + String url = params["url"]; + saveImg(url); + break; + case "shareImg": + String url = params["url"]; + String type = params["type"]; + shareImg(url, int.parse(type)); + break; + } + })); + return list.toSet(); + } + + callback(String method, var params, {bool finish = true}) { + _controller!.runJavascript("$method('$params')"); + if (finish) { + _controller!.runJavascript("delete $method"); + } + } + + //鑾峰彇鐢ㄦ埛ID + getUid(var params, String? callbackName) { + if (callbackName != null) { + UserUtil.getUid().then((value) { + callback(callbackName, value != null ? value.toString() : ""); + }); + } + } + + //鑾峰彇鐢ㄦ埛ID + getAppName(String? callbackName) { + // + PackageInfo.fromPlatform().then((PackageInfo packageInfo) { + if (callbackName != null) { + callback(callbackName, packageInfo.appName); + } + }); + } + + //toast + toast(params) { + if (params != null && params["msg"] != null) { + ToastUtil.toast(params["msg"], context); + } + } + + copyText(params) { + if (params != null && params["content"] != null) { + Clipboard.setData(ClipboardData(text: params["content"])); + } + } + + //鑾峰彇鍩烘湰鐨勭綉缁滆姹傚弬鏁� + getRequestBaseParams(var params, String? callbackName) { + var ps = {}; + if (params != null) { + ps.addAll(params); + } + http.HttpUtil.getBaseParams(params).then((value) { + String result = jsonEncode(value); + if (callbackName != null) { + callback(callbackName, result); + } + }); + } + + //灞曠ず婵�鍔辫棰� + showRewardVideoAd(BuildContext context, String? callbackName) { + // AdUtil.getAdInfo(context, AdPosition.vipReward).then((value) { + // AdUtil.loadReward(value, (status, msg) { + // switch (status) { + // case RewardAdStatus.verify: + // callback(callbackName!, 3, finish: false); + // break; + // case RewardAdStatus.ready: + // callback(callbackName!, 1, finish: false); + // break; + // case RewardAdStatus.close: + // callback(callbackName!, 10, finish: true); + // break; + // case RewardAdStatus.click: + // callback(callbackName!, 2, finish: false); + // break; + // case RewardAdStatus.fail: + // callback(callbackName!, 0, finish: true); + // break; + // } + // }); + // }); + } + + showLoading() { + http.showLoading(context); + } + + hideLoading() { + popPage(context); + } + + //缁撴潫椤甸潰 + finishPage() { + popPage(context); + } + + Future<bool> _dowloadImg(String url, String path) async { + PermissionStatus status = + await PermissionUtil.openPermission(Permission.storage, force: true); + if (status != PermissionStatus.granted) { + return false; + } + + //涓嬭浇鍥剧墖 + Dio dio = Dio(); + //璁剧疆杩炴帴瓒呮椂鏃堕棿 + dio.options.connectTimeout = 20000; + //璁剧疆鏁版嵁鎺ユ敹瓒呮椂鏃堕棿 + dio.options.receiveTimeout = 20000; + Response response; + try { + response = await dio.download(url, path); + if (response.statusCode == 200) { + await ImageGallerySaver.saveFile(path); + return true; + } else { + return false; + } + } catch (e) { + ToastUtil.toast("缃戠粶杩炴帴澶辫触", context); + return false; + } + } + + //淇濆瓨鍥剧墖 + saveImg(String url) { + Future<Directory?> dir; + if (Platform.isAndroid) { + dir = getExternalStorageDirectory(); + } else { + dir = getApplicationDocumentsDirectory(); + } + dir.then((value) { + if (value == null) { + ToastUtil.toast("鑾峰彇缂撳瓨鐩綍澶辫触", context); + return; + } + + String doc = value.path; + String path = doc + "/" + EncryptUtil.MD5(url) + ".png"; + _dowloadImg(url, path).then((value) { + if (value) { + ToastUtil.toast("淇濆瓨鎴愬姛", context); + } + }); + }); + } + + ///鍒嗕韩鍥剧墖 + ///type: 1-寰俊 2-qq 3-鏂版氮 + shareImg(String url, int type) { + getTemporaryDirectory().then((value) { + String path = value.path + "/" + EncryptUtil.MD5(url) + ".png"; + _dowloadImg(url, path).then((value) { + if (value) { + //寮�濮嬪垎浜� + switch (type) { + case 1: + ShareUtil.shareImg(context, File(path), SharePlatform.wx); + break; + case 2: + ShareUtil.shareImg(context, File(path), SharePlatform.qq); + break; + case 3: + ShareUtil.shareImg(context, File(path), SharePlatform.sina); + break; + } + } + }); + }); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/jump_page.dart b/src/main/resources/code/flutter_module/lib/utils/jump_page.dart new file mode 100644 index 0000000..983f2be --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/jump_page.dart @@ -0,0 +1,40 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import '../../utils/pageutils.dart'; +import '../../utils/ui_utils.dart'; +import 'package:flutter_boost/flutter_boost.dart'; + +import '../main.dart'; + +class JumpPageUtil { + + static jumpPage(String pageName, BuildContext context, + {Map<String,dynamic>? params, bool native = false, PageDataLisener? callback}) { + Map<String, dynamic> routeMap = { + "page": pageName, + }; + if (params != null) { + routeMap["params"] = params; + } + + if (native) { + print("鍘熺敓浼犻�掑弬鏁�:$params"); + //uiMethodChannel.invokeMapMethod("pushPage", jsonEncode(routeMap)); + // BoostChannel.instance.sendEventToNative("pushPage", routeMap); + BoostNavigator.instance.push( + pageName, //required + withContainer: true, //optional + arguments: params, //optional + opaque: true, //optional,default value is true + ); + } else { + NavigatorUtil.navigateToNextPage( + context, widgetForRoute(jsonEncode(routeMap)), (data) { + if (callback != null) { + callback(data); + } + }); + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/pageutils.dart b/src/main/resources/code/flutter_module/lib/utils/pageutils.dart new file mode 100644 index 0000000..f7e97b4 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/pageutils.dart @@ -0,0 +1,95 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'global.dart'; + +typedef PageDataLisener = void Function(dynamic data); + +//婊戝姩鏁堟灉 +class CustomRouteSlide extends PageRouteBuilder { + final Widget widget; + + CustomRouteSlide(this.widget) + : super( + transitionDuration: const Duration(milliseconds: 500), + pageBuilder: (BuildContext context, Animation<double> animation1, + Animation<double> animation2) { + return widget; + }, + transitionsBuilder: (BuildContext context, + Animation<double> animation1, + Animation<double> animation2, + Widget child) { + return SlideTransition( + position: Tween<Offset>( + begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0)) + .animate(CurvedAnimation( + parent: animation1, curve: Curves.fastOutSlowIn)), + child: child, + ); + }); +} + +class NavigatorUtil { + // static void navigateToNextPage(BuildContext context, PageRoute route, + // PageDataLisener? dataLisener) async { + // final result = await Navigator.of(context).push(route); + // dataLisener!(result); + // } + + static void navigateToNextPage( + BuildContext context, Widget page, PageDataLisener? dataLisener) async { + final result = + await Navigator.of(context).push(CupertinoPageRoute(builder: (context) { + return page; + })); + dataLisener!(result); + } + + static void navigateToNextPagePush(Widget page) { + + navigatorKey.currentState!.push( + CupertinoPageRoute (builder: (BuildContext context) => page)); + } + + static void navigateToNextPageWithFinish( + BuildContext context, PageRoute route) { + Navigator.of(context).pushReplacement(route); + } +} + +class KeepAliveWrapper extends StatefulWidget { + const KeepAliveWrapper({ + Key? key, + this.keepAlive = true, + required this.child, + }) : super(key: key); + final bool keepAlive; + final Widget child; + + @override + _KeepAliveWrapperState createState() => _KeepAliveWrapperState(); +} + +class _KeepAliveWrapperState extends State<KeepAliveWrapper> + with AutomaticKeepAliveClientMixin { + @override + Widget build(BuildContext context) { + super.build(context); + return widget.child; + } + + @override + void didUpdateWidget(covariant KeepAliveWrapper oldWidget) { + if (oldWidget.keepAlive != widget.keepAlive) { + // keepAlive 鐘舵�侀渶瑕佹洿鏂帮紝瀹炵幇鍦� AutomaticKeepAliveClientMixin 涓� + updateKeepAlive(); + } + super.didUpdateWidget(oldWidget); + } + + @override + bool get wantKeepAlive => widget.keepAlive; +} diff --git a/src/main/resources/code/flutter_module/lib/utils/permission_util.dart b/src/main/resources/code/flutter_module/lib/utils/permission_util.dart new file mode 100644 index 0000000..6c65858 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/permission_util.dart @@ -0,0 +1,150 @@ +import 'dart:convert'; +import 'dart:io'; + +import '../utils/global.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'event_bus_util.dart'; + +class PermissionUtil { + static var deniedSets = Set(); + + static Permission getLocationPermission() { + if (Platform.isAndroid) { + //鑾峰彇绯荤粺鐗堟湰 + if (Global.androidSDK != null && Global.androidSDK! < 29) { + return Permission.locationAlways; + } else { + return Permission.location; + } + } else { + return Permission.locationAlways; + } + } + + static Future _loadDeniedPermissions() async { + //鍔犺浇 + SharedPreferences prefs = await SharedPreferences.getInstance(); + List<String>? list = prefs.getStringList("permission_delay_list"); + deniedSets.clear(); + if (list != null) { + deniedSets.addAll(list); + } + + print(jsonEncode(list)); + } + + static Future _denyPermission(Permission permission) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List<String>? list = prefs.getStringList("permission_delay_list"); + list ??= []; + bool hasAdd = false; + list.forEach((element) { + if (element == permission.value.toString()) { + hasAdd = true; + } + }); + + if (!hasAdd) { + list.add(permission.value.toString()); + } + await prefs.setStringList("permission_delay_list", list); + } + + static Future _removeDenyPermission(Permission permission) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List<String>? list = prefs.getStringList("permission_delay_list"); + list ??= []; + + for (int i = 0; i < list.length; i++) { + if (list[i] == permission.value.toString()) { + list.removeAt(i); + i--; + } + } + await prefs.setStringList("permission_delay_list", list); + } + + ///鎵撳紑搴旂敤璁剧疆 + static openAppSetting() async { + await openAppSettings(); + } + + ///鎵撳紑鏉冮檺, + ///force锛氬己鍒舵墦寮�锛屾潈闄愯鎷掔粷鍚庤褰� + static Future<PermissionStatus> openPermission(Permission permission, + {force: false}) async { + await _loadDeniedPermissions(); + PermissionStatus status = await permission.status; + PermissionStatus? resultStatus; + switch (status) { + //Android鎺堟潈 + case PermissionStatus.granted: + resultStatus = status; + break; + //Android鎷掔粷 + case PermissionStatus.denied: + if (deniedSets.contains(permission.value.toString()) && !force) { + resultStatus = PermissionStatus.denied; + } else { + resultStatus = await permission.request(); + } + break; + //Android绂佹鍚庝笉鍐嶆彁绀� + case PermissionStatus.permanentlyDenied: + if (deniedSets.contains(permission.value.toString()) && !force) { + resultStatus = PermissionStatus.permanentlyDenied; + } else { + bool success = await openAppSettings(); + resultStatus = await permission.status; + } + break; + //IOS + case PermissionStatus.limited: + if (deniedSets.contains(permission.value.toString()) && !force) { + resultStatus = PermissionStatus.limited; + } else { + resultStatus = await permission.request(); + } + break; + //IOS + case PermissionStatus.restricted: + if (deniedSets.contains(permission.value.toString()) && !force) { + resultStatus = PermissionStatus.restricted; + } else { + resultStatus = await permission.request(); + } + break; + } + + if (resultStatus == PermissionStatus.granted) { + _removeDenyPermission(permission); + } else { + _denyPermission(permission); + } + + return resultStatus; + } + + ///鎵归噺鎵撳紑鏉冮檺 + static openPermissions(List<Permission> permissions, {force: true}) async { + await _loadDeniedPermissions(); + List<Permission> requestPS = []; + permissions.forEach((element) { + if (!deniedSets.contains(element.value.toString())) { + requestPS.add(element); + } + }); + + if (requestPS.isNotEmpty) { + await requestPS.request(); + } + } + + ///鍏抽棴鏉冮檺 + static Future<PermissionStatus> closePermission(Permission permission) async { + bool success = await openAppSettings(); + return await permission.status; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/push_util.dart b/src/main/resources/code/flutter_module/lib/utils/push_util.dart new file mode 100644 index 0000000..b886581 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/push_util.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart'; +import 'package:flutter/services.dart'; +import '../api/http.dart'; +import '../model/user/user_info.dart'; +import '../utils/event_bus_util.dart'; +import '../utils/pageutils.dart'; +import '../utils/user_util.dart'; + +import 'global.dart'; + +class PushUtil { + + static init(BuildContext context) async { + //濡傛灉鐧诲綍浜嗗氨璁剧疆鐢ㄦ埛鐨剈id + bool isLogin = await UserUtil.isLogin(); + if (isLogin) { + UserInfo? user = await UserUtil.getUserInfo(); + setAlias(user!.id!.toString()); + } + } + + ///娣诲姞alias + static Future setAlias(String alias) async { + // try { + // await _jpush.setAlias(alias); + // } catch (e) {} + } + + //鍒犻櫎alias + static Future removeAlias() async { + // await _jpush.deleteAlias(); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/setting_util.dart b/src/main/resources/code/flutter_module/lib/utils/setting_util.dart new file mode 100644 index 0000000..cf0b872 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/setting_util.dart @@ -0,0 +1,46 @@ + +import 'package:shared_preferences/shared_preferences.dart'; + +class SettingUtil { + //璁剧疆鎺ㄩ�� + static Future<bool> setPush(bool enable) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return await prefs.setBool("setting_push", enable); + } + + ///鏄惁鍏佽鎺ㄩ�� + static Future<bool> isEnablePush() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool? result = prefs.getBool("setting_push"); + result ??= true; + return result; + } + + //璁剧疆鎺ㄨ崘骞垮憡 + static Future<bool> setRecommendAd(bool enable) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return await prefs.setBool("setting_recommend_ad", enable); + } + + ///鏄惁鍏佽鎺ㄨ崘骞垮憡 + static Future<bool> isEnableRecommendAd() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool? result = prefs.getBool("setting_recommend_ad"); + result ??= true; + return result; + } + + //璁剧疆鎺ㄨ崘骞垮憡 + static Future<bool> setLocationSpan(int second) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return await prefs.setInt("setting_location_span", second); + } + + ///鏄惁鍏佽鎺ㄨ崘骞垮憡 + static Future<int> getLocationSpan() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + int? result = prefs.getInt("setting_location_span"); + result ??= 30; + return result; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/share_preference.dart b/src/main/resources/code/flutter_module/lib/utils/share_preference.dart new file mode 100644 index 0000000..d2fdba7 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/share_preference.dart @@ -0,0 +1,86 @@ +import 'package:flutter/services.dart'; + +class DataMethodChannel extends MethodChannel { + DataMethodChannel(String name) : super(name) { + setMethodCallHandler((call) { + switch (call.method) { + } + return Future.value(); + }); + } +} + +DataMethodChannel dataMethodChannel = DataMethodChannel('com.yeshi.video/data'); + +class MySharedPreferences { + static MySharedPreferences? _instance; + + static MySharedPreferences getInstance() { + _instance ??= MySharedPreferences(); + return _instance!; + } + + Future<String?> getString(String key) async { + String? value = await _getValue(key); + return value; + } + + Future<bool?> getBool(String key) async { + String? value = await _getValue(key); + if (value == null) { + return null; + } + return value as bool; + } + + Future<int?> getInt(String key) async { + String? value = await _getValue(key); + if (value == null) { + return null; + } + return int.parse(value); + } + + Future<bool> setString(String key, String value) async { + return await _setValue(key, value); + } + + Future<bool> setBool(String key, bool value) async { + return await _setValue(key, value.toString()); + } + + Future<bool> setInt(String key, bool value) async { + return await _setValue(key, value.toString()); + } + + Future<bool> remove(String key) async { + return await _removeValue(key); + } + + Future<bool> _setValue(String key, String? value) async { + String? success = await dataMethodChannel + .invokeMethod<String>("setSharedValue", {"key": key, "value": value}); + print("MySharedPreferences 璁剧疆鍊�:$success"); + if (success != null && success == "1") { + return true; + } + return false; + } + + Future<bool> _removeValue(String key) async { + String? success = + await dataMethodChannel.invokeMethod<String>("removeSharedValue", key); + print("MySharedPreferences 鍒犻櫎鍊�:$success"); + if (success != null && success == "1") { + return true; + } + return false; + } + + Future<String?> _getValue(String key) async { + String? result = + await dataMethodChannel.invokeMethod<String>("getSharedValue", key); + print("MySharedPreferences 鑾峰彇鍊�:$result"); + return result; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/share_utils.dart b/src/main/resources/code/flutter_module/lib/utils/share_utils.dart new file mode 100644 index 0000000..5ec3dc2 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/share_utils.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:share_plus/share_plus.dart'; + +import 'config_util.dart'; + +class ShareUtil { + static shareImg( + BuildContext context, File f, SharePlatform sharePlatform) async { + if (Platform.isAndroid) { + const platform = MethodChannel("com.yeshi.location/share"); //鍒嗘瀽1 + bool result = false; + try { + var params = { + "path": f.path, + "platform": sharePlatform.runtimeType.toString() + }; + switch (sharePlatform) { + case SharePlatform.qq: + params["platform"] = "qq"; + break; + case SharePlatform.wx: + params["platform"] = "wx"; + break; + case SharePlatform.sina: + params["platform"] = "sina"; + break; + } + + result = await platform.invokeMethod("shareImg", params); //鍒嗘瀽2 + } on PlatformException catch (e) { + print(e.toString()); + } + } else { + Share.shareFiles([f.path]); + } + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/sqlite_utils.dart b/src/main/resources/code/flutter_module/lib/utils/sqlite_utils.dart new file mode 100644 index 0000000..8bcf79f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/sqlite_utils.dart @@ -0,0 +1,69 @@ +import '../../utils/db_manager.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:path_provider/path_provider.dart'; +import 'dart:io'; + +class SQLiteUtil { + static const String DB_NAME = "hanju.db"; + + static Future<Database> _getDB() async { + var path = await getDatabasesPath() + "/$DB_NAME"; + print("databasepath:$path"); + Database database = await openDatabase(path, version: 1, + onCreate: (Database db, int version) async { + // When creating the db, create the table + List<String> tables = DBManager.getTables(); + for (var i = 0; i < tables.length; i++) { + await db.execute(tables[i]); + } + }); + return database; + } + + static Future executeSQL(String sql) async { + var db = await openDatabase(""); + await db.execute(sql); + await db.close(); + } + + static Future executeSQLWithParams(String sql, List params) async { + var db = await _getDB(); + await db.execute(sql, params); + await db.close(); + } + + static Future executeSQLs(List<String> sqls) async { + var db = await _getDB(); + for (var i = 0; i < sqls.length; i++) { + await db.execute(sqls[i]); + } + await db.close(); + } + + ///鎵归噺鎻掑叆 + ///sql绀轰緥锛欼NSERT INTO artists (name) VALUES (?) + static Future insert(String sql, List<List> list) async { + var db = await _getDB(); + await db.transaction((txn) async { + for (var i = 0; i < list.length; i++) { + await txn.rawInsert(sql, list[i]); + } + }); + await db.close(); + } + + ///鏌ヨ + static Future<List<Map>> select(String sql, List params) async { + var db = await _getDB(); + List<Map> list = await db.rawQuery(sql, params); + await db.close(); + return list; + } + + static Future<int> count(String sql, List params) async { + var db = await _getDB(); + int? count = Sqflite.firstIntValue(await db.rawQuery(sql, params)); + await db.close(); + return count!; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/string_util.dart b/src/main/resources/code/flutter_module/lib/utils/string_util.dart new file mode 100644 index 0000000..0c1560f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/string_util.dart @@ -0,0 +1,19 @@ +class StringUtil { + //鏄惁涓虹數璇濆彿鐮� + static bool isMobile(String str) { + return RegExp( + '^((13[0-9])|(15[^4])|(166)|(17[0-8])|(18[0-9])|(19[8-9])|(147,145))\\d{8}\$') + .hasMatch(str); + } + + static bool isEmail(String str) { + return RegExp( + '^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w+)+)\$') + .hasMatch(str); + } + + static bool isNullOrEmpty(String? str) { + return str==null|| str.isEmpty || str.trim().isEmpty; + } + +} diff --git a/src/main/resources/code/flutter_module/lib/utils/ui_constant.dart b/src/main/resources/code/flutter_module/lib/utils/ui_constant.dart new file mode 100644 index 0000000..3efd879 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/ui_constant.dart @@ -0,0 +1,40 @@ +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; + +//婊戝姩鏁堟灉 +class ColorConstant { + static const Color theme = Color(0xFFFF4D88);//Color(0xFF0E95FE); + static const Color title = Color(0xFF333333); +} + +class Constant { + + static const String IOS_APP_ID = + "1135798414"; + + //闃块噷浜戞巿鏉冪櫥褰� + static const String ALIYUN_AUTH_SECRETINFO = + "v+dQBemz/CZaLI0YP/A5AxxG2YgF7IvP+RNzRkmOGln1bbd7tcMt7uvqt8dvb9ekawjGbvSZ6Y/N9WT0kpUb6lrkS11BxfhCpXtuxmOxei97xP3l5Hd8PVqPv2jPC8uVDRhl6kaAqf/6I1gwL1d7am+8w1sGkYnZ3UEgd9ljDBKjeRbbpp+KEzLiqnKWYDNqMLSRdU0BmzTSGqtkM5c1TYOZgx68NxwE2oM9VzcjQEeFP0yiQatMyNIQ5mJjbyU3zi9qiyMQaeTLHeACvqZ2XCYQBbAeqJh6DPrhIHGlfGc="; + + //闅愮鏀跨瓥閾炬帴 + static const String PRIVACY_URL = + "http://h5.hanju.goxcw.com/hanju/privacy_ios.html"; + + //鐢ㄦ埛鍗忚閾炬帴 + static const String PROTOCOL_URL = + "http://h5.hanju.goxcw.com/hanju/user_protocol_ios.html"; + + //寰俊 + static const String WX_APPID = "wxd930ea5d5a228f5f"; + static const String WX_UNIVERSAL_LINK = "https://your.univerallink.com/link/"; + + static const String APP_DOWNLOAD_LINK = + "https://a.app.qq.com/o/simple.jsp?pkgname=com.dw.zzql"; + + + static const String MAP_LINK = + "http://web.location.izzql.com/map/map.html"; + + +} diff --git a/src/main/resources/code/flutter_module/lib/utils/ui_utils.dart b/src/main/resources/code/flutter_module/lib/utils/ui_utils.dart new file mode 100644 index 0000000..8f148ed --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/ui_utils.dart @@ -0,0 +1,107 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; +import 'package:flutter/services.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class UIMethodChannel extends MethodChannel { + UIMethodChannel(String name) : super(name) { + setMethodCallHandler((call) { + switch (call.method) { + case "resumePage": + {} + } + return Future.value(); + }); + } +} + +MethodChannel uiMethodChannel = UIMethodChannel('com.yeshi.video/ui'); + +///鍖呰9鏁翠釜椤甸潰锛屼笅鎷夊埛鏂帮紝锛屼笂鎷夊姞杞界殑榛樿閰嶇疆 +Widget getBasePage(Widget widget) { + return RefreshConfiguration( + headerBuilder: () => const WaterDropHeader(), + // 閰嶇疆榛樿澶撮儴鎸囩ず鍣�,鍋囧浣犳瘡涓〉闈㈢殑澶撮儴鎸囩ず鍣ㄩ兘涓�鏍风殑璇�,浣犻渶瑕佽缃繖涓� + footerBuilder: () => const ClassicFooter(), + // 閰嶇疆榛樿搴曢儴鎸囩ず鍣� + headerTriggerDistance: 80.0, + // 澶撮儴瑙﹀彂鍒锋柊鐨勮秺鐣岃窛绂� + springDescription: + const SpringDescription(stiffness: 170, damping: 16, mass: 1.9), + // 鑷畾涔夊洖寮瑰姩鐢�,涓変釜灞炴�у�兼剰涔夎鏌ヨflutter api + maxOverScrollExtent: 100, + //澶撮儴鏈�澶у彲浠ユ嫋鍔ㄧ殑鑼冨洿,濡傛灉鍙戠敓鍐插嚭瑙嗗浘鑼冨洿鍖哄煙,璇疯缃繖涓睘鎬� + maxUnderScrollExtent: 0, + // 搴曢儴鏈�澶у彲浠ユ嫋鍔ㄧ殑鑼冨洿 + enableScrollWhenRefreshCompleted: true, + //杩欎釜灞炴�т笉鍏煎PageView鍜孴abBarView,濡傛灉浣犵壒鍒渶瑕乀abBarView宸﹀彸婊戝姩,浣犻渶瑕佹妸瀹冭缃负true + enableLoadingWhenFailed: true, + //鍦ㄥ姞杞藉け璐ョ殑鐘舵�佷笅,鐢ㄦ埛浠嶇劧鍙互閫氳繃鎵嬪娍涓婃媺鏉ヨЕ鍙戝姞杞芥洿澶� + hideFooterWhenNotFull: false, + // Viewport涓嶆弧涓�灞忔椂,绂佺敤涓婃媺鍔犺浇鏇村鍔熻兘 + enableBallisticLoad: true, + // 鍙互閫氳繃鎯�ф粦鍔ㄨЕ鍙戝姞杞芥洿澶� + child: MaterialApp( + //鍥介檯鍖栧鐞� + // localizationsDelegates: const [ + // // 杩欒鏄叧閿� + // RefreshLocalizations.delegate, + // ], + // supportedLocales: const [ + // Locale('en'), + // Locale('zh'), + // ], + // localeResolutionCallback: + // (Locale? locale, Iterable<Locale> supportedLocales) { + // //print("change language"); + // return locale; + // }, + home: widget)); +} + +class ToastUtil { + static toast(String text, BuildContext context) { + // Toast.show(text, context); + // showToast(text,position: ToastPosition.bottom); + print("toast:$text"); + uiMethodChannel.invokeMethod("toast", text); + } +} + +class DialogUtil { + static Future<dynamic> showDialog(BuildContext context, Dialog dialog) async { + return await showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation<double> animation, + Animation<double> secondaryAnimation) { + return dialog; + }); + } + + static Future<dynamic> showDialogBottom(BuildContext context, Widget widget, + {BorderRadius? borderRadius, Color? backgroundColor}) async { + return await showModalBottomSheet<void>( + backgroundColor: backgroundColor ?? Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: borderRadius ?? BorderRadius.zero, + ), + context: context, + builder: (BuildContext context) { + return widget; + }); + } +} + +class DimenUtil { + //鑾峰彇鍍忕礌姣� + static double getPixelRatio(BuildContext context) { + MediaQueryData mediaQuery = MediaQuery.of(context); + return mediaQuery.devicePixelRatio; + } + + //鑾峰彇1鍍忕礌鐨勫彇鍊� + static double getOnePixel(BuildContext context) { + return 1 / MediaQuery.of(context).devicePixelRatio; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/user_util.dart b/src/main/resources/code/flutter_module/lib/utils/user_util.dart new file mode 100644 index 0000000..cc20bfc --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/user_util.dart @@ -0,0 +1,114 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/services.dart'; +import 'package:fluwx_no_pay/fluwx_no_pay.dart' as fluwx; +import '../../api/user_api.dart'; +import '../../utils/share_preference.dart'; +import '../api/http.dart'; +import '../model/user/user_info.dart'; +import '../utils/event_bus_util.dart'; +import '../utils/string_util.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Timer? locationTimer; + +class UserUtil { + static const _loginMessageChannel = + BasicMessageChannel('ThirdLogin', StandardMessageCodec()); + + //鏄惁鍚屾剰浜嗙敤鎴峰崗璁� + static Future<bool> isAgreeProtocol() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool? agree = prefs.getBool("agree_protocol"); + if (agree == null) { + return false; + } + return agree; + } + + //璁剧疆宸插悓鎰忕敤鎴峰崗璁� + static Future<bool> setAgreeProtocol() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.setBool("agree_protocol", true); + } + + ///寰俊鐧诲綍 + static void loginWX() async { + fluwx + .sendWeChatAuth(scope: "snsapi_userinfo", state: "wechat_sdk_demo_test") + .then((value) {}); + } + + ///QQ鐧诲綍 + static Future<Map> loginQQ() async { + Map value = await _loginMessageChannel.send({"method": "loginQQ"}) as Map; + return value; + } + + //鏄惁宸茬粡鐧诲綍 + static Future<bool> isLogin() async { + UserInfo? user = await getUserInfo(); + return user != null; + } + + //鐢ㄦ埛淇℃伅 + static Future<UserInfo?> getUserInfo() async { + + // return null; + + MySharedPreferences prefs = MySharedPreferences.getInstance(); + String? result =await prefs.getString("user_info"); + if (StringUtil.isNullOrEmpty(result)) { + return null; + } else { + return UserInfo.fromJson(jsonDecode(result!)); + } + } + + static Future<UserInfo?> updateUserInfo(BuildContext context) async { + String? uid = await getUid(); + if (uid == null) { + return null; + } + + Map<String, dynamic>? result = await UserApiUtil.getUserInfo(context, uid); + var code = result!["IsPost"]; + if (code == "true") { + UserInfo user = UserInfo.fromJson(result["Data"]); + //淇濆瓨鐢ㄦ埛淇℃伅 + UserUtil.setUserInfo(user); + + return user; + } else if (code == 80001 || code == 80002) { + await logout(); + } + return null; + } + + static Future<String?> getUid() async { + UserInfo? user = await getUserInfo(); + if (user != null) { + return user.id; + } + return null; + } + + static Future setUserInfo(UserInfo user) async { + MySharedPreferences prefs = MySharedPreferences.getInstance(); + await prefs.setString("user_info", jsonEncode(user)); + } + + //閫�鍑虹櫥褰� + static Future logout() async { + await _logout(); + eventBus.fire(LoginEventBus(false)); + } + + static Future _logout() async { + MySharedPreferences prefs = MySharedPreferences.getInstance(); + await prefs.remove("user_info"); + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/video/search_record_util.dart b/src/main/resources/code/flutter_module/lib/utils/video/search_record_util.dart new file mode 100644 index 0000000..fe5576f --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/video/search_record_util.dart @@ -0,0 +1,38 @@ +///鎼滅储璁板綍甯姪绫� +import 'package:shared_preferences/shared_preferences.dart'; + +class SearchRecordUtil { + static void addRecord(String st) async { + if (st.isEmpty || st.trim().isEmpty) { + return; + } + final prefs = await SharedPreferences.getInstance(); + List<String> list = await listRecord(); + + //鍒犻櫎閲嶅鐨勬暟鎹� + for (var i = 0; i < list.length; i++) { + if (list[i] == st.trim()) { + list.removeAt(i); + i--; + } + } + + list.insert(0, st.trim()); + if (list.length > 10) { + list = list.sublist(0, 10); + } + await prefs.setStringList("searchRecord", list); + } + + static void clearRecord() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setStringList("searchRecord", []); + } + + static Future<List<String>> listRecord() async { + final prefs = await SharedPreferences.getInstance(); + List<String>? list = prefs.getStringList("searchRecord"); + list ??= []; + return list; + } +} diff --git a/src/main/resources/code/flutter_module/lib/utils/video/video_util.dart b/src/main/resources/code/flutter_module/lib/utils/video/video_util.dart new file mode 100644 index 0000000..a0a0c22 --- /dev/null +++ b/src/main/resources/code/flutter_module/lib/utils/video/video_util.dart @@ -0,0 +1,19 @@ +import '../../model/video/video_model.dart'; + +class VideoUtil { + static String getVPicture(VideoInfoModel video) { + if (video.vpicture != null && video.vpicture!.isNotEmpty) { + return video.vpicture!; + } + return video.picture!; + } + + static String getHPicture(VideoInfoModel video) { + if (video.hpicture != null && video.hpicture!.isNotEmpty) { + return video.hpicture!; + } + return video.picture!; + } + + +} -- Gitblit v1.8.0