12 changed files with 399 additions and 62 deletions
Split 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