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: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<bool?> _showAuthorizationConfirmDialog(AuthorizationType type) {
/// Map
Future<Map<String, dynamic>?> _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<bool>(
// 使
String? tempProvinceName = _savedProvinceName;
String? tempProvinceCode = _savedProvinceCode;
String? tempCityName = _savedCityName;
String? tempCityCode = _savedCityCode;
return showDialog<Map<String, dynamic>>(
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<void> 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;
}