12 changed files with 399 additions and 62 deletions
Unified View
Diff Options
-
2lib/controller/mine/login_controller.dart
-
114lib/controller/mine/phone_controller.dart
-
108lib/controller/mine/user_controller.dart
-
2lib/extension/router_service.dart
-
22lib/model/mine/user_data.dart
-
1lib/network/api_urls.dart
-
5lib/network/user_api.dart
-
34lib/network/user_api.g.dart
-
5lib/pages/main/main_page.dart
-
13lib/pages/mine/auth_center_page.dart
-
151lib/pages/mine/phone_page.dart
-
4lib/pages/mine/real_name_page.dart
@ -0,0 +1,114 @@ |
|||||
|
import 'dart:async'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; |
||||
|
import '../../network/user_api.dart'; |
||||
|
import '../global.dart'; |
||||
|
|
||||
|
class PhoneController extends GetxController { |
||||
|
late UserApi _userApi; |
||||
|
final phone = ''.obs; |
||||
|
// 手机号输入 |
||||
|
final phoneNumber = ''.obs; |
||||
|
// 验证码输入 |
||||
|
final verificationCode = ''.obs; |
||||
|
// 是否正在发送验证码 |
||||
|
final isSendingCode = false.obs; |
||||
|
// 倒计时秒数 |
||||
|
final countdownSeconds = 0.obs; |
||||
|
// 是否正在登录中 |
||||
|
final isLoggingIn = false.obs; |
||||
|
final tabIndex = 0.obs; |
||||
|
|
||||
|
@override |
||||
|
void onInit() { |
||||
|
super.onInit(); |
||||
|
// 从全局依赖中获取UserApi |
||||
|
_userApi = Get.find<UserApi>(); |
||||
|
phone.value = GlobalData().userData!.phone!; |
||||
|
} |
||||
|
|
||||
|
// 获取验证码 |
||||
|
Future<void> getVerificationCode() async { |
||||
|
if (countdownSeconds.value > 0) { |
||||
|
return; |
||||
|
} |
||||
|
// 验证手机号格式 |
||||
|
if (phoneNumber.value.isEmpty || phoneNumber.value.length != 11) { |
||||
|
SmartDialog.showToast('请输入正确的手机号'); |
||||
|
return; |
||||
|
} |
||||
|
isSendingCode.value = true; |
||||
|
try { |
||||
|
// 构建请求参数 |
||||
|
final params = { |
||||
|
'purpose': 3, // 认证 |
||||
|
'verifiableAccount': phoneNumber.value, |
||||
|
'verifiableAccountType': 1, // 手机 |
||||
|
}; |
||||
|
// 调用UserApi中的验证码接口 |
||||
|
final response = await _userApi.getVerificationCode(params); |
||||
|
// 处理响应 |
||||
|
if (response.data.isSuccess) { |
||||
|
// 生产环境移除打印,可考虑使用正式的日志框架 |
||||
|
SmartDialog.showToast('验证码发送成功'); |
||||
|
// 开始倒计时 |
||||
|
countdownSeconds.value = 60; |
||||
|
startCountdown(); |
||||
|
} else { |
||||
|
SmartDialog.showToast(response.data.message); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
SmartDialog.showToast('网络请求失败,请重试'); |
||||
|
} finally { |
||||
|
isSendingCode.value = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 开始倒计时 |
||||
|
void startCountdown() { |
||||
|
Future.delayed(const Duration(seconds: 1), () { |
||||
|
if (countdownSeconds.value > 0) { |
||||
|
countdownSeconds.value--; |
||||
|
startCountdown(); |
||||
|
} else { |
||||
|
isSendingCode.value = false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
Future<void> changePhone() async { |
||||
|
if(isSendingCode.value){ |
||||
|
return; |
||||
|
} |
||||
|
if (phoneNumber.value.isEmpty) { |
||||
|
SmartDialog.showToast('请输入手机号'); |
||||
|
return; |
||||
|
} |
||||
|
if (verificationCode.value.isEmpty) { |
||||
|
SmartDialog.showToast('请输入验证码'); |
||||
|
return; |
||||
|
} |
||||
|
isSendingCode.value = true; |
||||
|
try { |
||||
|
// 调用登录接口 |
||||
|
SmartDialog.showLoading(msg: '处理中'); |
||||
|
final param = { 'phone': phoneNumber.value, 'captcha': verificationCode.value}; |
||||
|
final response = await _userApi.updatePhone(param); |
||||
|
// 处理响应 |
||||
|
if (response.data.isSuccess) { |
||||
|
GlobalData().userData!.phone = phoneNumber.value; |
||||
|
SmartDialog.showToast('更换成功'); |
||||
|
Get.back(result: 1); |
||||
|
} else { |
||||
|
SmartDialog.showToast(response.data.message); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
SmartDialog.showToast('网络请求失败,请检查网络连接'); |
||||
|
} finally { |
||||
|
SmartDialog.dismiss(); |
||||
|
isSendingCode.value = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
@ -0,0 +1,151 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:get/get.dart'; |
||||
|
import 'package:tdesign_flutter/tdesign_flutter.dart'; |
||||
|
|
||||
|
import '../../controller/mine/phone_controller.dart'; |
||||
|
|
||||
|
class PhonePage extends StatelessWidget { |
||||
|
PhonePage({super.key}); |
||||
|
|
||||
|
final PhoneController controller = Get.put(PhoneController()); |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
backgroundColor: Color(0xffFFFFFF), |
||||
|
appBar: AppBar( |
||||
|
title: Text('手机认证', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), |
||||
|
centerTitle: true, |
||||
|
), |
||||
|
body: Obx(() { |
||||
|
return IndexedStack( |
||||
|
index: controller.tabIndex.value, |
||||
|
children: [ |
||||
|
// 推荐列表 |
||||
|
Center( |
||||
|
child: Column( |
||||
|
mainAxisAlignment: MainAxisAlignment.center, // 垂直居中 |
||||
|
crossAxisAlignment: CrossAxisAlignment.center, // 水平居中 |
||||
|
children: [ |
||||
|
const Text('已经认证的手机号码', style: TextStyle(fontSize: 18, color: Color(0xFF999999))), |
||||
|
const SizedBox(height: 12), |
||||
|
Text(_encryptPhone(controller.phone.value), style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold)), |
||||
|
SizedBox(height: 64), |
||||
|
TDButton( |
||||
|
text: '更换手机号码', |
||||
|
width: MediaQuery.of(context).size.width - 50, |
||||
|
size: TDButtonSize.large, |
||||
|
type: TDButtonType.fill, |
||||
|
shape: TDButtonShape.round, |
||||
|
style: TDButtonStyle( |
||||
|
textColor: Colors.white, |
||||
|
backgroundColor: Color(0xFF7562F9), |
||||
|
), |
||||
|
activeStyle: TDButtonStyle( |
||||
|
textColor: Colors.white, |
||||
|
backgroundColor: Color(0xC37562F9), |
||||
|
), |
||||
|
onTap: (){ |
||||
|
controller.tabIndex.value = 1; |
||||
|
}, |
||||
|
), |
||||
|
SizedBox(height: 200), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
// 同城列表 |
||||
|
Center( |
||||
|
child: Column( |
||||
|
children: [ |
||||
|
const SizedBox(height:32), |
||||
|
const Text('请输入新的手机号码', style: TextStyle(fontSize: 16, color: Color(0xFF333333))), |
||||
|
const SizedBox(height: 8), |
||||
|
const Text('手机号码仅用于登录和账号保护', style: TextStyle(fontSize: 14, color: Color(0xFF999999))), |
||||
|
const SizedBox(height: 56), |
||||
|
TDInput( |
||||
|
type: TDInputType.cardStyle, |
||||
|
inputType: TextInputType.phone, |
||||
|
maxLength: 11, |
||||
|
cardStyle: TDCardStyle.topText, |
||||
|
width: MediaQuery.of(context).size.width - 50, |
||||
|
hintText: '请输入你的手机号', |
||||
|
onChanged: (text) { |
||||
|
controller.phoneNumber.value = text; |
||||
|
} |
||||
|
), |
||||
|
const SizedBox(height: 24), |
||||
|
TDInput( |
||||
|
type: TDInputType.cardStyle, |
||||
|
inputType: TextInputType.number, |
||||
|
maxLength: 6, |
||||
|
cardStyle: TDCardStyle.topText, |
||||
|
width: MediaQuery.of(context).size.width - 50, |
||||
|
hintText: '请输入验证码', |
||||
|
onChanged: (text) { |
||||
|
controller.verificationCode.value = text; |
||||
|
}, |
||||
|
rightBtn: SizedBox( |
||||
|
width: 100, |
||||
|
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
|
children: [ |
||||
|
Padding( |
||||
|
padding: const EdgeInsets.only(right: 10), |
||||
|
child: Container( |
||||
|
width: 0.5, |
||||
|
height: 24, |
||||
|
color: TDTheme.of(context).grayColor3, |
||||
|
), |
||||
|
), |
||||
|
controller.countdownSeconds.value > 0 ? |
||||
|
TDText('${controller.countdownSeconds.value}秒后重试', textColor: TDTheme.of(context).fontGyColor4) |
||||
|
: TDText('获取验证码', textColor: Color(0xFF7562F9)), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
needClear: false, |
||||
|
onBtnTap: () { |
||||
|
controller.getVerificationCode(); |
||||
|
} |
||||
|
), |
||||
|
const SizedBox(height: 48), |
||||
|
TDButton( |
||||
|
text: '确定更换', |
||||
|
width: MediaQuery.of(context).size.width - 50, |
||||
|
size: TDButtonSize.large, |
||||
|
type: TDButtonType.fill, |
||||
|
shape: TDButtonShape.round, |
||||
|
style: TDButtonStyle( |
||||
|
textColor: Colors.white, |
||||
|
backgroundColor: Color(0xFF7562F9), |
||||
|
), |
||||
|
activeStyle: TDButtonStyle( |
||||
|
textColor: Colors.white, |
||||
|
backgroundColor: Color(0xC37562F9), |
||||
|
), |
||||
|
onTap: (){ |
||||
|
controller.changePhone(); |
||||
|
}, |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
) |
||||
|
], |
||||
|
); |
||||
|
}) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
String _encryptPhone(String phone) { |
||||
|
if (phone.isEmpty) return ''; |
||||
|
if (phone.length < 7) return phone; |
||||
|
|
||||
|
// 保留前3位和后4位,中间用*代替 |
||||
|
String start = phone.substring(0, 3); |
||||
|
String end = phone.substring(phone.length - 4); |
||||
|
String middle = '*' * (phone.length - 7); |
||||
|
|
||||
|
return '$start$middle$end'; |
||||
|
} |
||||
|
|
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save