From 70a752b6e5e5fa99bae05ffb7e2726bc4cbcd19c Mon Sep 17 00:00:00 2001 From: userGyl Date: Wed, 21 Jan 2026 14:29:03 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E5=90=8C=E6=84=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/pages/common/webview/controller.dart | 26 ++++ ln_jq_app/lib/pages/common/webview/view.dart | 50 ++++++++ ln_jq_app/lib/pages/login/view.dart | 118 +++++++++++++++++- 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 ln_jq_app/lib/pages/common/webview/controller.dart create mode 100644 ln_jq_app/lib/pages/common/webview/view.dart diff --git a/ln_jq_app/lib/pages/common/webview/controller.dart b/ln_jq_app/lib/pages/common/webview/controller.dart new file mode 100644 index 0000000..7094cec --- /dev/null +++ b/ln_jq_app/lib/pages/common/webview/controller.dart @@ -0,0 +1,26 @@ +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:get/get.dart'; + +class WebController extends GetxController { + late String title; + late String url; + + final RxDouble progress = 0.0.obs; + InAppWebViewController? webViewController; + + @override + void onInit() { + super.onInit(); + // 从参数中获取标题和URL + title = Get.arguments['title'] ?? '详情'; + url = Get.arguments['url'] ?? ''; + } + + void onWebViewCreated(InAppWebViewController controller) { + webViewController = controller; + } + + void onProgressChanged(InAppWebViewController controller, int progressValue) { + progress.value = progressValue / 100; + } +} diff --git a/ln_jq_app/lib/pages/common/webview/view.dart b/ln_jq_app/lib/pages/common/webview/view.dart new file mode 100644 index 0000000..910dcc2 --- /dev/null +++ b/ln_jq_app/lib/pages/common/webview/view.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class WebViewPage extends GetView { + const WebViewPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Get.put(WebController()); + + return Scaffold( + appBar: AppBar( + title: Text(controller.title), + centerTitle: true, + bottom: PreferredSize( + preferredSize: const Size.fromHeight(2.0), + child: Obx( + () => controller.progress.value < 1.0 + ? LinearProgressIndicator( + value: controller.progress.value, + backgroundColor: Colors.transparent, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor, + ), + minHeight: 2.0, + ) + : const SizedBox(height: 2.0), + ), + ), + ), + body: InAppWebView( + initialUrlRequest: URLRequest(url: WebUri(controller.url)), + initialSettings: InAppWebViewSettings( + isInspectable: true, + javaScriptEnabled: true, + javaScriptCanOpenWindowsAutomatically: true, + useShouldOverrideUrlLoading: true, + mixedContentMode: MixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW, + mediaPlaybackRequiresUserGesture: false, + allowsInlineMediaPlayback: true, + ), + onWebViewCreated: controller.onWebViewCreated, + onProgressChanged: controller.onProgressChanged, + ), + ); + } +} diff --git a/ln_jq_app/lib/pages/login/view.dart b/ln_jq_app/lib/pages/login/view.dart index 30ba347..32a05d7 100644 --- a/ln_jq_app/lib/pages/login/view.dart +++ b/ln_jq_app/lib/pages/login/view.dart @@ -1,4 +1,7 @@ +import 'dart:io'; + import 'package:aliyun_push_flutter/aliyun_push_flutter.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; @@ -9,6 +12,7 @@ import 'package:ln_jq_app/common/model/vehicle_info.dart'; import 'package:ln_jq_app/common/styles/theme.dart'; import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart'; import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart'; +import 'package:ln_jq_app/pages/common/webview/view.dart'; import 'package:ln_jq_app/pages/login/controller.dart'; import 'package:ln_jq_app/pages/url_host/view.dart'; import 'package:ln_jq_app/storage_service.dart'; @@ -85,7 +89,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix margin: EdgeInsets.all(15), elevation: 4, child: Container( - height: cLogin ? 285.h : 360.h, + height: cLogin ? 290.h : 365.h, padding: EdgeInsets.all(15), child: Column( children: [ @@ -165,6 +169,20 @@ class _LoginPageState extends State with SingleTickerProviderStateMix SizedBox(height: 20.h), ElevatedButton( onPressed: () async { + if (!_isAgreed) { + DialogX.to.showConfirmDialog( + icon: DialogIcon.warn, + content: _buildDialogContent(), + confirmText: '同意', + cancelText: '拒绝', + onConfirm: () { + _isAgreed = true; + controller.updateUi(); + }, + ); + return; + } + String password = controller.driverIdentityController.text; if (password.isEmpty) { showToast("请输入密码"); @@ -241,6 +259,7 @@ class _LoginPageState extends State with SingleTickerProviderStateMix ), child: Text('登录'), ), + buildAgreement(), ], ); } @@ -323,6 +342,20 @@ class _LoginPageState extends State with SingleTickerProviderStateMix shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), onPressed: () async { + if (!_isAgreed) { + DialogX.to.showConfirmDialog( + icon: DialogIcon.warn, + content: _buildDialogContent(), + confirmText: '同意', + cancelText: '拒绝', + onConfirm: () { + _isAgreed = true; + controller.updateUi(); + }, + ); + return; + } + String account = controller.stationIdController.text; String password = controller.passwordController.text; @@ -393,10 +426,93 @@ class _LoginPageState extends State with SingleTickerProviderStateMix }, child: Text('登录'), ), + buildAgreement(), ], ); } + bool _isAgreed = false; + + Widget buildAgreement() { + return Padding( + padding: EdgeInsets.only(top: 13.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // 勾选框 + SizedBox( + width: 22.w, + height: 22.h, + child: Checkbox( + value: _isAgreed, + activeColor: AppTheme.themeColor, + // 简单的圆角样式 + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), + onChanged: (bool? value) { + setState(() { + _isAgreed = value ?? false; + }); + }, + ), + ), + const SizedBox(width: 4), + // 富文本协议部分 + Text.rich( + TextSpan( + text: '我已阅读并同意', + style: const TextStyle(color: Colors.grey, fontSize: 13), + children: [ + TextSpan( + text: '《用户协议》', + style: TextStyle(color: AppTheme.themeColor, fontSize: 13), + recognizer: TapGestureRecognizer() + ..onTap = () { + openPage("用户协议", "https://lnh2e.com/user_agreement.html"); + }, + ), + const TextSpan(text: ' 和 '), + TextSpan( + text: '《隐私政策》', + style: TextStyle(color: AppTheme.themeColor, fontSize: 13), + recognizer: TapGestureRecognizer() + ..onTap = () { + openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html"); + }, + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildDialogContent() { + return RichTextX( + children: [ + TextSpanItem('请阅读并同意'), + TextSpanItem( + '《隐私协议》', + onTap: () => openPage("隐私政策", "https://lnh2e.com/privacy_agreement.html"), + ), + TextSpanItem('和'), + TextSpanItem( + '《用户政策》', + onTap: () => openPage("用户协议", "https://lnh2e.com/user_agreement.html"), + ), + TextSpanItem(',我们将在协议框架内为您提供更优质的服务。'), + ], + ); + } + + void openPage(String title, String url) { + if (Platform.isIOS) { + openWebPage(url); + return; + } + Get.to(() => const WebViewPage(), arguments: {'title': title, 'url': url}); + } + final _aliyunPush = AliyunPushFlutter(); void addAlias(String alias) async {