import 'dart:io'; import 'package:aliyun_push_flutter/aliyun_push_flutter.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:getx_scaffold/getx_scaffold.dart'; import 'package:ln_jq_app/common/login_util.dart'; import 'package:ln_jq_app/common/model/base_model.dart'; 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'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { @override Widget build(BuildContext context) { return GetBuilder( init: LoginController(), id: 'login', builder: (controller) { return Scaffold( backgroundColor: Colors.white, body: Stack( children: [ // 1. 顶部背景与装饰 Positioned( top: 0, left: 0, right: 0, child: LoginUtil.getAssImg("bg_login"), ), Positioned( top: 0, left: 0, child: SizedBox( width: 180.w, height: 218.h, child: LoginUtil.getAssImg("ic_login_bg@2x"), ), ), _buildBackground(), // 2. 登录表单主体 Positioned( top: 280.h, left: 0, right: 0, bottom: 0, child: Container( height: MediaQuery.of(context).size.height * 2 / 3, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), ), ), child: SingleChildScrollView( child: Padding( padding: EdgeInsets.symmetric(horizontal: 30.w), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 80.h), // 登录输入区域 _buildLoginInputFields(controller), SizedBox(height: 40.h), // 协议 buildAgreement(), SizedBox(height: 80.h), // 底部 Slogan Center( child: Column( children: [ Text( "H Y P A I", style: TextStyle( fontSize: 16, fontWeight: FontWeight.w400, color: Color.fromRGBO(51, 51, 51, 1), letterSpacing: 8, ), ), Text( "HYDROGEN MOBILITY", style: TextStyle( fontSize: 9, color: Colors.grey.shade400, letterSpacing: 1, ), ), ], ), ), ], ), ), ), ), ), if (AppTheme.is_show_host) Positioned( top: 40.h, right: 20.w, child: TextButton( onPressed: () { Get.to(() => UrlHostPage()); }, child: const Text( "域名配置", style: TextStyle( color: Colors.black, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ); }, ); } /// 构建背景装饰 Widget _buildBackground() { return Positioned( top: 0, left: 32.w, right: 0, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 100.h), // Logo SizedBox(height: 60.h, child: LoginUtil.getAssImg('ic_logo_unbg@2x')), SizedBox(height: 30.h), const Text( "HELLO,", style: TextStyle( fontSize: 32, fontWeight: FontWeight.w500, color: Color.fromRGBO(51, 51, 51, 1), ), ), const SizedBox(height: 8), Row( children: [ const Text( "欢迎使用 ", style: TextStyle( fontSize: 24, fontWeight: FontWeight.w500, color: Color.fromRGBO(51, 51, 51, 1), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: Color.fromRGBO(56, 198, 151, 1), borderRadius: BorderRadius.circular(20), ), child: const Text( "“羚牛氢能智慧服务平台”", style: TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), ); } /// 构建登录输入框区域 Widget _buildLoginInputFields(LoginController controller) { return Column( children: [ // 手机号输入 Container( height: 55.h, decoration: BoxDecoration( color: const Color(0xFFF7F9FB), borderRadius: BorderRadius.circular(28), ), child: TextField( controller: controller.phoneController, keyboardType: TextInputType.phone, style: const TextStyle(fontSize: 15), decoration: const InputDecoration( hintText: '请输入手机号', hintStyle: TextStyle(color: Colors.grey, fontSize: 14), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 24), ), ), ), const SizedBox(height: 20), // 验证码输入 Container( height: 55.h, decoration: BoxDecoration( color: const Color(0xFFF7F9FB), borderRadius: BorderRadius.circular(28), ), child: Row( children: [ Expanded( child: TextField( inputFormatters: [ LengthLimitingTextInputFormatter(6), // 最多6位 ], controller: controller.codeController, keyboardType: TextInputType.number, style: const TextStyle(fontSize: 15), decoration: const InputDecoration( hintText: '请输入验证码', hintStyle: TextStyle(color: Colors.grey, fontSize: 14), border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 24), ), ), ), Obx( () => GestureDetector( onTap: controller.countdown.value == 0 ? controller.startCountdown : null, child: Padding( padding: const EdgeInsets.only(right: 24.0), child: Text( controller.countdown.value == 0 ? "获取验证码" : "${controller.countdown.value}s后重新获取", style: TextStyle( color: controller.countdown.value == 0 ? const Color(0xFF006633) : Colors.grey, fontSize: 13, fontWeight: FontWeight.bold, ), ), ), ), ), ], ), ), const SizedBox(height: 40), // 登录按钮 ElevatedButton( onPressed: () => _handleLogin(controller), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF006633), foregroundColor: Colors.white, minimumSize: const Size(double.infinity, 55), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(28)), elevation: 0, ), child: const Text( "立即登录", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ], ); } /// 处理登录逻辑 void _handleLogin(LoginController controller) async { if (!_isAgreed) { DialogX.to.showConfirmDialog( icon: DialogIcon.warn, content: _buildDialogContent(), confirmText: '同意', cancelText: '拒绝', onConfirm: () { _isAgreed = true; controller.updateUi(); }, ); return; } String phone = controller.phoneController.text; String code = controller.codeController.text; if (phone.isEmpty || !phone.isPhoneNumber) { showToast("请输入正确的手机号"); return; } if (code.isEmpty) { showToast("请输入验证码"); return; } showLoading('登录中...'); try { var responseData = await HttpService.to.post( 'appointment/login/login', data: {'mobile': phone, 'code': code}, ); if (responseData == null && responseData!.data == null) { dismissLoading(); showToast('登录失败:无法获取凭证'); return; } //登录信息处理 try { var result = BaseModel.fromJson(responseData.data); if (result.code != 0) { showToast(result.error); dismissLoading(); return; } //类型2是司机 1是站点 String loginType = result.data['loginType'].toString() ?? ''; String token = result.data['token'] ?? ''; if (loginType == "2") { String idCard = result.data['idCard'] ?? ''; String name = result.data['name'] ?? ''; String phone = result.data['phone'] ?? ''; await StorageService.to.saveLoginInfo( token: token, userId: "", channel: "driver", idCard: idCard, name: name, phone: phone, ); //司机登录后查询已绑定车辆信息 var carInfo = await HttpService.to.get( "appointment/driver/getTruckInfoByDriver?phone=$phone", ); if (carInfo != null) { var carInforesult = BaseModel.fromJson(carInfo.data); if (carInforesult.data != null) { final vehicle = VehicleInfo.fromJson( carInforesult.data as Map, ); //保存使用 await StorageService.to.saveVehicleInfo(vehicle); } } } if (loginType == "1") { String userId = result.data['userId'] ?? ''; String mobile = result.data['mobile'] ?? ''; await StorageService.to.saveLoginInfo( token: token, userId: userId, phone: mobile, channel: "station", ); } //注册推送别名 addAlias(phone); //页面操作 dismissLoading(); showToast('登录成功,欢迎您'); if (loginType == "2") { Get.offAll(() => BaseWidgetsPage()); } else { Get.offAll(() => B_BaseWidgetsPage()); } } catch (e) { dismissLoading(); showToast('登录失败:数据异常'); } } catch (e) { dismissLoading(); } } 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 { var result = await _aliyunPush.addAlias(alias); var code = result['code']; if (code == kAliyunPushSuccessCode) { Logger.d('添加别名$alias成功'); } else { var errorCode = result['code']; var errorMsg = result['errorMsg']; Logger.d('添加别名$alias失败: $errorCode - $errorMsg'); } } }