From 3f34b11181cfd0c0b18f98edf1265683e9a9650c Mon Sep 17 00:00:00 2001 From: hailin Date: Mon, 22 Dec 2025 06:34:44 -0800 Subject: [PATCH] =?UTF-8?q?feat(mobile-app):=20=E6=8E=88=E6=9D=83=E7=94=B3?= =?UTF-8?q?=E8=AF=B7=E5=BC=B9=E7=AA=97=E6=94=AF=E6=8C=81=E7=9C=81=E5=B8=82?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 市团队和省团队申请弹窗使用 city_pickers 选择省市 - 默认选中本地存储的省市(来自认种时的选择) - 允许用户重新选择省市 - 社区申请保持只输入社区名称(无省市选择) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../pages/authorization_apply_page.dart | 373 +++++++++++------- 1 file changed, 238 insertions(+), 135 deletions(-) diff --git a/frontend/mobile-app/lib/features/authorization/presentation/pages/authorization_apply_page.dart b/frontend/mobile-app/lib/features/authorization/presentation/pages/authorization_apply_page.dart index a50d33de..8dbd2987 100644 --- a/frontend/mobile-app/lib/features/authorization/presentation/pages/authorization_apply_page.dart +++ b/frontend/mobile-app/lib/features/authorization/presentation/pages/authorization_apply_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:city_pickers/city_pickers.dart'; import '../../../../core/di/injection_container.dart'; import '../../../../core/services/authorization_service.dart'; @@ -207,159 +208,260 @@ class _AuthorizationApplyPageState } /// 显示授权确认弹窗 - Future _showAuthorizationConfirmDialog(AuthorizationType type) { + /// 返回 Map 包含确认状态和选择的省市信息 + Future?> _showAuthorizationConfirmDialog(AuthorizationType type) { String title; - String locationInfo; - String? additionalField; switch (type) { case AuthorizationType.community: title = '申请社区授权'; - locationInfo = '请输入您的社区名称'; - additionalField = 'community'; break; case AuthorizationType.cityTeam: title = '申请市团队授权'; - if (_savedCityName == null || _savedCityCode == null) { - _showErrorSnackBar('未找到认种时保存的城市信息,请先完成认种'); - return Future.value(false); - } - locationInfo = '城市: $_savedCityName'; break; case AuthorizationType.provinceTeam: title = '申请省团队授权'; - if (_savedProvinceName == null || _savedProvinceCode == null) { - _showErrorSnackBar('未找到认种时保存的省份信息,请先完成认种'); - return Future.value(false); - } - locationInfo = '省份: $_savedProvinceName'; break; } - return showDialog( + // 初始化弹窗内的临时变量(使用本地存储的值作为默认值) + String? tempProvinceName = _savedProvinceName; + String? tempProvinceCode = _savedProvinceCode; + String? tempCityName = _savedCityName; + String? tempCityCode = _savedCityCode; + + return showDialog>( context: context, barrierDismissible: true, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - title: Text( - title, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: Color(0xFF5D4037), - ), - ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (additionalField == 'community') ...[ - const Text( - '社区名称', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Color(0xFF745D43), - ), + builder: (dialogContext) => StatefulBuilder( + builder: (context, setDialogState) { + // 显示省市选择器 + Future showLocationPicker() async { + final result = await CityPickers.showCityPicker( + context: context, + cancelWidget: const Text( + '取消', + style: TextStyle(color: Color(0xFF745D43), fontSize: 16), ), - const SizedBox(height: 8), - TextField( - controller: _communityNameController, - decoration: InputDecoration( - hintText: '请输入社区名称', - hintStyle: TextStyle( - color: const Color(0xFF745D43).withOpacity(0.5), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Color(0x4D8B5A2B)), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Color(0xFFD4AF37), width: 2), - ), - contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - ), + confirmWidget: const Text( + '确定', + style: TextStyle(color: Color(0xFFD4AF37), fontSize: 16, fontWeight: FontWeight.w600), ), - ] else ...[ - Container( - width: double.infinity, - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFFFFF5E6), - borderRadius: BorderRadius.circular(8), - border: Border.all(color: const Color(0x33D4AF37)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - locationInfo, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Color(0xFF5D4037), + height: 300, + showType: ShowType.pc, // 只显示省市两级 + barrierDismissible: true, + locationCode: tempCityCode ?? tempProvinceCode, // 默认选中保存的位置 + ); + + if (result != null) { + setDialogState(() { + tempProvinceName = result.provinceName; + tempProvinceCode = result.provinceId; + tempCityName = result.cityName; + tempCityCode = result.cityId; + }); + } + } + + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + title: Text( + title, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Color(0xFF5D4037), + ), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (type == AuthorizationType.community) ...[ + const Text( + '社区名称', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Color(0xFF745D43), + ), + ), + const SizedBox(height: 8), + TextField( + controller: _communityNameController, + decoration: InputDecoration( + hintText: '请输入社区名称', + hintStyle: TextStyle( + color: const Color(0xFF745D43).withOpacity(0.5), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Color(0x4D8B5A2B)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: Color(0xFFD4AF37), width: 2), + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + ), + ), + ] else ...[ + // 省市选择区域 + Text( + type == AuthorizationType.cityTeam ? '选择城市' : '选择省份', + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Color(0xFF745D43), + ), + ), + const SizedBox(height: 8), + GestureDetector( + onTap: showLocationPicker, + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFFFFF5E6), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: const Color(0x4DD4AF37)), + ), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (type == AuthorizationType.cityTeam) ...[ + Text( + tempCityName ?? '请选择城市', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: tempCityName != null + ? const Color(0xFF5D4037) + : const Color(0xFF5D4037).withOpacity(0.5), + ), + ), + if (tempProvinceName != null) ...[ + const SizedBox(height: 4), + Text( + tempProvinceName!, + style: TextStyle( + fontSize: 12, + color: const Color(0xFF745D43).withOpacity(0.7), + ), + ), + ], + ] else ...[ + Text( + tempProvinceName ?? '请选择省份', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: tempProvinceName != null + ? const Color(0xFF5D4037) + : const Color(0xFF5D4037).withOpacity(0.5), + ), + ), + ], + ], + ), + ), + const Icon( + Icons.keyboard_arrow_down, + color: Color(0xFFD4AF37), + size: 24, + ), + ], ), ), - const SizedBox(height: 8), - Text( - '此信息来自您认种时的选择,不可修改', - style: TextStyle( - fontSize: 12, - color: const Color(0xFF745D43).withOpacity(0.7), - ), + ), + const SizedBox(height: 8), + Text( + '点击可重新选择省市', + style: TextStyle( + fontSize: 12, + color: const Color(0xFF745D43).withOpacity(0.6), ), - ], + ), + ], + const SizedBox(height: 16), + Text( + '确认申请后将使用以上信息提交授权申请', + style: TextStyle( + fontSize: 13, + color: const Color(0xFF745D43).withOpacity(0.8), + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(dialogContext).pop(null), + child: const Text( + '取消', + style: TextStyle(color: Color(0xFF745D43)), + ), + ), + ElevatedButton( + onPressed: () { + if (type == AuthorizationType.community) { + if (_communityNameController.text.trim().isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('请输入社区名称'), + backgroundColor: Colors.red, + ), + ); + return; + } + } else if (type == AuthorizationType.cityTeam) { + if (tempCityCode == null || tempCityName == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('请选择城市'), + backgroundColor: Colors.red, + ), + ); + return; + } + } else if (type == AuthorizationType.provinceTeam) { + if (tempProvinceCode == null || tempProvinceName == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('请选择省份'), + backgroundColor: Colors.red, + ), + ); + return; + } + } + + Navigator.of(dialogContext).pop({ + 'confirmed': true, + 'provinceName': tempProvinceName, + 'provinceCode': tempProvinceCode, + 'cityName': tempCityName, + 'cityCode': tempCityCode, + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFD4AF37), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text( + '确认申请', + style: TextStyle(color: Colors.white), ), ), ], - const SizedBox(height: 16), - Text( - '确认申请后将使用以上信息提交授权申请', - style: TextStyle( - fontSize: 13, - color: const Color(0xFF745D43).withOpacity(0.8), - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text( - '取消', - style: TextStyle(color: Color(0xFF745D43)), - ), - ), - ElevatedButton( - onPressed: () { - if (additionalField == 'community') { - if (_communityNameController.text.trim().isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('请输入社区名称'), - backgroundColor: Colors.red, - ), - ); - return; - } - } - Navigator.of(context).pop(true); - }, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4AF37), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text( - '确认申请', - style: TextStyle(color: Colors.white), - ), - ), - ], + ); + }, ), ); } @@ -395,8 +497,8 @@ class _AuthorizationApplyPageState } // 显示确认弹窗 - final confirmed = await _showAuthorizationConfirmDialog(_selectedType!); - if (confirmed != true) { + final dialogResult = await _showAuthorizationConfirmDialog(_selectedType!); + if (dialogResult == null || dialogResult['confirmed'] != true) { return; } @@ -419,6 +521,7 @@ class _AuthorizationApplyPageState } // 2. 将 AuthorizationType 转换为 SelfApplyAuthorizationType + // 从弹窗返回结果中获取省市信息 SelfApplyAuthorizationType selfApplyType; String? communityName; String? provinceCode; @@ -433,13 +536,13 @@ class _AuthorizationApplyPageState break; case AuthorizationType.cityTeam: selfApplyType = SelfApplyAuthorizationType.cityTeam; - cityCode = _savedCityCode; - cityName = _savedCityName; + cityCode = dialogResult['cityCode'] as String?; + cityName = dialogResult['cityName'] as String?; break; case AuthorizationType.provinceTeam: selfApplyType = SelfApplyAuthorizationType.provinceTeam; - provinceCode = _savedProvinceCode; - provinceName = _savedProvinceName; + provinceCode = dialogResult['provinceCode'] as String?; + provinceName = dialogResult['provinceName'] as String?; break; }