feat(mobile-app): 授权申请弹窗支持省市选择器

- 市团队和省团队申请弹窗使用 city_pickers 选择省市
- 默认选中本地存储的省市(来自认种时的选择)
- 允许用户重新选择省市
- 社区申请保持只输入社区名称(无省市选择)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-22 06:34:44 -08:00
parent 4daee6a650
commit 3f34b11181
1 changed files with 238 additions and 135 deletions

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:city_pickers/city_pickers.dart';
import '../../../../core/di/injection_container.dart'; import '../../../../core/di/injection_container.dart';
import '../../../../core/services/authorization_service.dart'; import '../../../../core/services/authorization_service.dart';
@ -207,39 +208,62 @@ class _AuthorizationApplyPageState
} }
/// ///
Future<bool?> _showAuthorizationConfirmDialog(AuthorizationType type) { /// Map
Future<Map<String, dynamic>?> _showAuthorizationConfirmDialog(AuthorizationType type) {
String title; String title;
String locationInfo;
String? additionalField;
switch (type) { switch (type) {
case AuthorizationType.community: case AuthorizationType.community:
title = '申请社区授权'; title = '申请社区授权';
locationInfo = '请输入您的社区名称';
additionalField = 'community';
break; break;
case AuthorizationType.cityTeam: case AuthorizationType.cityTeam:
title = '申请市团队授权'; title = '申请市团队授权';
if (_savedCityName == null || _savedCityCode == null) {
_showErrorSnackBar('未找到认种时保存的城市信息,请先完成认种');
return Future.value(false);
}
locationInfo = '城市: $_savedCityName';
break; break;
case AuthorizationType.provinceTeam: case AuthorizationType.provinceTeam:
title = '申请省团队授权'; title = '申请省团队授权';
if (_savedProvinceName == null || _savedProvinceCode == null) {
_showErrorSnackBar('未找到认种时保存的省份信息,请先完成认种');
return Future.value(false);
}
locationInfo = '省份: $_savedProvinceName';
break; break;
} }
return showDialog<bool>( // 使
String? tempProvinceName = _savedProvinceName;
String? tempProvinceCode = _savedProvinceCode;
String? tempCityName = _savedCityName;
String? tempCityCode = _savedCityCode;
return showDialog<Map<String, dynamic>>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,
builder: (context) => AlertDialog( builder: (dialogContext) => StatefulBuilder(
builder: (context, setDialogState) {
//
Future<void> showLocationPicker() async {
final result = await CityPickers.showCityPicker(
context: context,
cancelWidget: const Text(
'取消',
style: TextStyle(color: Color(0xFF745D43), fontSize: 16),
),
confirmWidget: const Text(
'确定',
style: TextStyle(color: Color(0xFFD4AF37), fontSize: 16, fontWeight: FontWeight.w600),
),
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( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
@ -255,7 +279,7 @@ class _AuthorizationApplyPageState
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (additionalField == 'community') ...[ if (type == AuthorizationType.community) ...[
const Text( const Text(
'社区名称', '社区名称',
style: TextStyle( style: TextStyle(
@ -284,34 +308,83 @@ class _AuthorizationApplyPageState
), ),
), ),
] else ...[ ] else ...[
Container( //
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, width: double.infinity,
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFFFF5E6), color: const Color(0xFFFFF5E6),
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0x33D4AF37)), border: Border.all(color: const Color(0x4DD4AF37)),
), ),
child: Row(
children: [
Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (type == AuthorizationType.cityTeam) ...[
Text( Text(
locationInfo, tempCityName ?? '请选择城市',
style: const TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF5D4037), color: tempCityName != null
? const Color(0xFF5D4037)
: const Color(0xFF5D4037).withOpacity(0.5),
), ),
), ),
const SizedBox(height: 8), if (tempProvinceName != null) ...[
const SizedBox(height: 4),
Text( Text(
'此信息来自您认种时的选择,不可修改', tempProvinceName!,
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: const Color(0xFF745D43).withOpacity(0.7), 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.6),
), ),
), ),
], ],
@ -327,7 +400,7 @@ class _AuthorizationApplyPageState
), ),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.of(context).pop(false), onPressed: () => Navigator.of(dialogContext).pop(null),
child: const Text( child: const Text(
'取消', '取消',
style: TextStyle(color: Color(0xFF745D43)), style: TextStyle(color: Color(0xFF745D43)),
@ -335,7 +408,7 @@ class _AuthorizationApplyPageState
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
if (additionalField == 'community') { if (type == AuthorizationType.community) {
if (_communityNameController.text.trim().isEmpty) { if (_communityNameController.text.trim().isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
@ -345,8 +418,35 @@ class _AuthorizationApplyPageState
); );
return; return;
} }
} else if (type == AuthorizationType.cityTeam) {
if (tempCityCode == null || tempCityName == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('请选择城市'),
backgroundColor: Colors.red,
),
);
return;
} }
Navigator.of(context).pop(true); } 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( style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4AF37), backgroundColor: const Color(0xFFD4AF37),
@ -360,6 +460,8 @@ class _AuthorizationApplyPageState
), ),
), ),
], ],
);
},
), ),
); );
} }
@ -395,8 +497,8 @@ class _AuthorizationApplyPageState
} }
// //
final confirmed = await _showAuthorizationConfirmDialog(_selectedType!); final dialogResult = await _showAuthorizationConfirmDialog(_selectedType!);
if (confirmed != true) { if (dialogResult == null || dialogResult['confirmed'] != true) {
return; return;
} }
@ -419,6 +521,7 @@ class _AuthorizationApplyPageState
} }
// 2. AuthorizationType SelfApplyAuthorizationType // 2. AuthorizationType SelfApplyAuthorizationType
//
SelfApplyAuthorizationType selfApplyType; SelfApplyAuthorizationType selfApplyType;
String? communityName; String? communityName;
String? provinceCode; String? provinceCode;
@ -433,13 +536,13 @@ class _AuthorizationApplyPageState
break; break;
case AuthorizationType.cityTeam: case AuthorizationType.cityTeam:
selfApplyType = SelfApplyAuthorizationType.cityTeam; selfApplyType = SelfApplyAuthorizationType.cityTeam;
cityCode = _savedCityCode; cityCode = dialogResult['cityCode'] as String?;
cityName = _savedCityName; cityName = dialogResult['cityName'] as String?;
break; break;
case AuthorizationType.provinceTeam: case AuthorizationType.provinceTeam:
selfApplyType = SelfApplyAuthorizationType.provinceTeam; selfApplyType = SelfApplyAuthorizationType.provinceTeam;
provinceCode = _savedProvinceCode; provinceCode = dialogResult['provinceCode'] as String?;
provinceName = _savedProvinceName; provinceName = dialogResult['provinceName'] as String?;
break; break;
} }