You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
556 lines
18 KiB
556 lines
18 KiB
import 'package:get/get.dart';
|
|
import 'package:flutter/material.dart';
|
|
import '../../../routes/app_routes.dart';
|
|
import 'dart:convert';
|
|
import 'dart:ffi';
|
|
import 'facebookLogin.dart';
|
|
import 'googleLogin.dart';
|
|
import 'weixinLogin.dart';
|
|
import '../../../data/services/network/api.dart';
|
|
import '../../../../data/models/user_Info.dart';
|
|
//import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:get_storage/get_storage.dart';
|
|
|
|
enum LoginViewState {
|
|
welcome, // 欢迎页面
|
|
verification // 验证码页面
|
|
}
|
|
|
|
class LoginController extends GetxController {
|
|
static LoginController get to => Get.find();
|
|
final GetStorage _storage = GetStorage();
|
|
|
|
// 添加国家编码相关状态
|
|
final _selectedCountryCode = '+86'.obs;
|
|
final _selectedCountryFlag = '🇨🇳'.obs;
|
|
// 国家编码 Getter
|
|
String get selectedCountryCode => _selectedCountryCode.value;
|
|
String get selectedCountryFlag => _selectedCountryFlag.value;
|
|
// 预设国家编码列表
|
|
final List<Map<String, String>> countryCodes = [
|
|
{'name': '邮箱', 'code': '', 'flag': '✉️'}, // 保持首位
|
|
{'name': '中国', 'code': '+86', 'flag': '🇨🇳'}, // 保持第二位
|
|
|
|
// 按国家代码数字升序排列(去除+号后排序)
|
|
{'name': '俄罗斯', 'code': '+7', 'flag': '🇷🇺'},
|
|
{'name': '英国', 'code': '+44', 'flag': '🇬🇧'},
|
|
{'name': '德国', 'code': '+49', 'flag': '🇩🇪'},
|
|
{'name': '意大利', 'code': '+39', 'flag': '🇮🇹'},
|
|
{'name': '法国', 'code': '+33', 'flag': '🇫🇷'},
|
|
{'name': '西班牙', 'code': '+34', 'flag': '🇪🇸'},
|
|
{'name': '南非', 'code': '+27', 'flag': '🇿🇦'},
|
|
{'name': '沙特阿拉伯', 'code': '+966', 'flag': '🇸🇦'},
|
|
{'name': '土耳其', 'code': '+90', 'flag': '🇹🇷'},
|
|
{'name': '日本', 'code': '+81', 'flag': '🇯🇵'},
|
|
{'name': '韩国', 'code': '+82', 'flag': '🇰🇷'},
|
|
{'name': '新加坡', 'code': '+65', 'flag': '🇸🇬'},
|
|
{'name': '马来西亚', 'code': '+60', 'flag': '🇲🇾'},
|
|
{'name': '泰国', 'code': '+66', 'flag': '🇹🇭'},
|
|
{'name': '越南', 'code': '+84', 'flag': '🇻🇳'},
|
|
{'name': '印度', 'code': '+91', 'flag': '🇮🇳'},
|
|
{'name': '巴基斯坦', 'code': '+92', 'flag': '🇵🇰'},
|
|
{'name': '菲律宾', 'code': '+63', 'flag': '🇵🇭'},
|
|
{'name': '印度尼西亚', 'code': '+62', 'flag': '🇮🇩'},
|
|
{'name': '阿联酋', 'code': '+971', 'flag': '🇦🇪'},
|
|
{'name': '美国', 'code': '+1', 'flag': '🇺🇸'},
|
|
{'name': '加拿大', 'code': '+1', 'flag': '🇨🇦'},
|
|
{'name': '墨西哥', 'code': '+52', 'flag': '🇲🇽'},
|
|
{'name': '巴西', 'code': '+55', 'flag': '🇧🇷'},
|
|
{'name': '阿根廷', 'code': '+54', 'flag': '🇦🇷'},
|
|
{'name': '澳大利亚', 'code': '+61', 'flag': '🇦🇺'},
|
|
{'name': '新西兰', 'code': '+64', 'flag': '🇳🇿'},
|
|
|
|
// 中国地区(保持最后)
|
|
{'name': '中国香港', 'code': '+852', 'flag': '🇭🇰'},
|
|
{'name': '中国澳门', 'code': '+853', 'flag': '🇲🇴'},
|
|
{'name': '中国台湾', 'code': '+886', 'flag': '🇹🇼'},
|
|
];
|
|
|
|
// 状态管理
|
|
final _viewState = LoginViewState.welcome.obs;
|
|
final _isLoading = false.obs;
|
|
final _countdownSeconds = 0.obs;
|
|
final _contactValue = ''.obs;
|
|
final _displayContact = ''.obs;
|
|
final _verificationCode = ['', '', '', ''].obs;
|
|
int loginType = -1;
|
|
// Getters
|
|
LoginViewState get viewState => _viewState.value;
|
|
bool get isLoading => _isLoading.value;
|
|
int get countdownSeconds => _countdownSeconds.value;
|
|
String get contactValue => _contactValue.value;
|
|
String get displayContact => _displayContact.value;
|
|
List<String> get verificationCode => _verificationCode;
|
|
|
|
// 文本控制器
|
|
final contactController = TextEditingController();
|
|
final verificationCodeController = TextEditingController();
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
_viewState.value = LoginViewState.welcome;
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
contactController.dispose();
|
|
verificationCodeController.dispose();
|
|
super.onClose();
|
|
}
|
|
|
|
// 更新选中的国家编码
|
|
void updateCountryCode(String code, String flag) {
|
|
_selectedCountryCode.value = code;
|
|
_selectedCountryFlag.value = flag;
|
|
// 根据选择更新输入框类型
|
|
if (code == '@') {
|
|
contactController.clear(); // 清空当前输入
|
|
loginType = 0; // 设置为邮箱登录类型
|
|
} else {
|
|
loginType = 1; // 设置为手机号登录类型
|
|
}
|
|
}
|
|
|
|
// 获取验证码
|
|
void getVerificationCode() {
|
|
if (contactController.text.isEmpty) {
|
|
Get.snackbar(
|
|
'提示',
|
|
'请输入手机号码或邮箱',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
return;
|
|
}
|
|
|
|
_contactValue.value = contactController.text;
|
|
_formatDisplayContact();
|
|
_sendVerificationCode();
|
|
}
|
|
|
|
// 使用验证码直接登录
|
|
void loginWithVerificationCode() {
|
|
//测试期间暂时关闭验证码登录
|
|
// if (contactController.text.isEmpty) {
|
|
// Get.snackbar(
|
|
// '提示',
|
|
// '请输入手机号码或邮箱',
|
|
// snackPosition: SnackPosition.TOP,
|
|
// backgroundColor: Colors.red.withOpacity(0.1),
|
|
// colorText: Colors.red,
|
|
// );
|
|
// return;
|
|
// }
|
|
|
|
// if (verificationCodeController.text.isEmpty) {
|
|
// Get.snackbar(
|
|
// '提示',
|
|
// '请输入验证码',
|
|
// snackPosition: SnackPosition.TOP,
|
|
// backgroundColor: Colors.red.withOpacity(0.1),
|
|
// colorText: Colors.red,
|
|
// );
|
|
// return;
|
|
// }
|
|
|
|
// if (verificationCodeController.text.length < 6) {
|
|
// Get.snackbar(
|
|
// '提示',
|
|
// '请输入完整的验证码',
|
|
// snackPosition: SnackPosition.TOP,
|
|
// backgroundColor: Colors.red.withOpacity(0.1),
|
|
// colorText: Colors.red,
|
|
// );
|
|
// return;
|
|
// }
|
|
|
|
_isLoading.value = true;
|
|
|
|
// 模拟验证过程
|
|
Future.delayed(const Duration(seconds: 2), () {
|
|
_isLoading.value = false;
|
|
// 验证成功,跳转到配对页面
|
|
Get.offAllNamed(Routes.pairing);
|
|
});
|
|
}
|
|
|
|
// 切换到验证码页面
|
|
void showVerificationView() {
|
|
if (contactController.text.isEmpty) {
|
|
Get.snackbar(
|
|
'提示',
|
|
'请输入手机号码或邮箱',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
return;
|
|
}
|
|
|
|
_contactValue.value = contactController.text;
|
|
_formatDisplayContact();
|
|
_viewState.value = LoginViewState.verification;
|
|
|
|
_sendVerificationCode();
|
|
}
|
|
|
|
// 返回欢迎页面
|
|
void showWelcomeView() {
|
|
_viewState.value = LoginViewState.welcome;
|
|
_resetVerificationCode();
|
|
}
|
|
|
|
// 格式化显示的联系方式(隐藏部分字符)
|
|
void _formatDisplayContact() {
|
|
String contact = _contactValue.value;
|
|
if (contact.contains('@')) {
|
|
// 邮箱逻辑保持不变
|
|
final parts = contact.split('@');
|
|
if (parts[0].length > 3) {
|
|
_displayContact.value = parts[0].substring(0, 3) + '****@' + parts[1];
|
|
} else {
|
|
_displayContact.value = contact;
|
|
}
|
|
loginType = 0;
|
|
} else if (RegExp(r'^\d+$').hasMatch(contact)) {
|
|
// 手机号格式化时包含国家编码
|
|
if (contact.length > 7) {
|
|
_displayContact.value = '$selectedCountryCode ' +
|
|
contact.substring(0, 3) +
|
|
'****' +
|
|
contact.substring(contact.length - 4);
|
|
} else {
|
|
_displayContact.value = '$selectedCountryCode $contact';
|
|
}
|
|
loginType = 1;
|
|
} else {
|
|
_displayContact.value = contact;
|
|
}
|
|
}
|
|
|
|
// 开始倒计时
|
|
void _startCountdown() {
|
|
_countdownSeconds.value = 60;
|
|
Future.doWhile(() async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
_countdownSeconds.value--;
|
|
return _countdownSeconds.value > 0;
|
|
});
|
|
}
|
|
|
|
// 发送验证码
|
|
Future<void> _sendVerificationCode() async {
|
|
// 这里应该是实际发送验证码的逻辑
|
|
// 为了演示,我们只是模拟发送
|
|
if (isEmialContactController() || isPhoneContactController()) {
|
|
_startCountdown();
|
|
var response = await Api.getCode({
|
|
"addr": selectedCountryCode + contactController.text,
|
|
"vtype": loginType,
|
|
});
|
|
if (response != null) {
|
|
print("请求后台数据验证码params=${response}");
|
|
//短信已发送
|
|
//EasyLoading.showInfo('codeSend'.tr);
|
|
}
|
|
} else {
|
|
Get.snackbar(
|
|
'提示',
|
|
'请正确的手机号码或邮箱',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 重新发送验证码
|
|
void resendVerificationCode() {
|
|
if (_countdownSeconds.value > 0) return;
|
|
_startCountdown();
|
|
_sendVerificationCode();
|
|
}
|
|
|
|
// 更新验证码输入
|
|
void updateVerificationCode(int index, String value) {
|
|
print("updateVerificationCode=${index}${value}");
|
|
if (index >= 0 && index < 4) {
|
|
var newList = [..._verificationCode];
|
|
newList[index] = value;
|
|
_verificationCode.value = newList;
|
|
}
|
|
}
|
|
|
|
// 重置验证码
|
|
void _resetVerificationCode() {
|
|
_verificationCode.value = ['', '', '', ''];
|
|
}
|
|
|
|
// 检查邮箱格式
|
|
bool isEmialContactController() {
|
|
// 检查每个元素是否为单个字母或数字字符
|
|
final text = contactController.text;
|
|
return (text.length > 5 &&
|
|
RegExp(r'^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
|
|
.hasMatch(text));
|
|
}
|
|
|
|
// 检查手机号码格式
|
|
bool isPhoneContactController() {
|
|
// 检查每个元素是否为单个字母或数字字符
|
|
final text = contactController.text;
|
|
return RegExp(r'^[0-9]+$').hasMatch(text);
|
|
}
|
|
|
|
// 检查验证码是否完整
|
|
bool isVerificationCodeComplete() {
|
|
// 检查每个元素是否为单个字母或数字字符
|
|
final text = verificationCodeController.text;
|
|
print("数据获取Facebook${text.length}");
|
|
return text.length == 4 && RegExp(r'^[0-9]+$').hasMatch(text);
|
|
}
|
|
|
|
// 登录
|
|
Future<void> login() async {
|
|
_isLoading.value = true;
|
|
|
|
try {
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
// 模拟验证过程
|
|
|
|
switch (loginType) {
|
|
case 0:
|
|
{
|
|
if (!isVerificationCodeComplete()) {
|
|
Get.snackbar(
|
|
'提示',
|
|
'请输入完整的验证码',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
break;
|
|
}
|
|
requestLogin({
|
|
"mail": contactController.text,
|
|
"phone": selectedCountryCode + contactController.text,
|
|
"stype": loginType,
|
|
"vcode": verificationCodeController.text,
|
|
"ttoken": "",
|
|
"phonemodel": "",
|
|
"phonemac": "",
|
|
"name": "",
|
|
"avatar": "",
|
|
});
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
if (!isVerificationCodeComplete()) {
|
|
Get.snackbar(
|
|
'提示',
|
|
'请输入完整的验证码',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
break;
|
|
}
|
|
requestLogin({
|
|
"mail": "",
|
|
"phone": selectedCountryCode + contactController.text,
|
|
"stype": loginType,
|
|
"vcode": verificationCodeController.text,
|
|
"ttoken": "",
|
|
"phonemodel": "",
|
|
"phonemac": "",
|
|
"name": "",
|
|
"avatar": "",
|
|
});
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
print("数据获取Facebook");
|
|
final facebook = Facebook();
|
|
bool authResult = await facebook.signInWithFacebook();
|
|
print("数据获取Facebook().token${authResult}");
|
|
if (authResult) {
|
|
print("数据获取_Facebook.token${facebook.token}");
|
|
print("数据获取_Facebook.pictureurl${facebook.pictureurl}");
|
|
print("数据获取_Facebook.pictureurl${facebook.email}");
|
|
// SettingsController().userName = Facebook().name;
|
|
// SettingsController().avatar = Facebook().pictureurl;
|
|
// SettingsController().userId = Facebook().id;
|
|
//SettingsController().avatar = Google().email;
|
|
|
|
// 验证成功,跳转到配对页面
|
|
requestLogin({
|
|
"mail": facebook.email,
|
|
"phone": "",
|
|
"stype": loginType,
|
|
"vcode": "",
|
|
"ttoken": facebook.token,
|
|
"phonemodel": "",
|
|
"phonemac": "",
|
|
"name": facebook.name,
|
|
"avatar": facebook.pictureurl,
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
print("数据获取_Google()");
|
|
final google = Google();
|
|
bool authResult = await google.signInWithGoogle();
|
|
print("数据获取_Google().token${authResult}");
|
|
if (authResult) {
|
|
print("数据获取_Google.token${google.token}");
|
|
print("数据获取_Google.pictureurl${google.pictureurl}");
|
|
print("数据获取_Google.email${google.email}");
|
|
print("数据获取_Google.email${google.displayName}");
|
|
// 验证成功,跳转到配对页面
|
|
// SettingsController().userName = Google().displayName!;
|
|
// //SettingsController().avatar = Google().email;
|
|
// SettingsController().userId = Google().id!;
|
|
requestLogin({
|
|
"mail": google.email,
|
|
"phone": "",
|
|
"stype": loginType,
|
|
"vcode": "",
|
|
"ttoken": google.token,
|
|
"phonemodel": "",
|
|
"phonemac": "",
|
|
"name": google.displayName,
|
|
"avatar": google.pictureurl,
|
|
});
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
print("数据获取_FlWX()");
|
|
|
|
bool authResult = await FlWX().authBy(
|
|
NormalAuth(
|
|
scope: "snsapi_userinfo",
|
|
state: "wechat_sdk_demo_test",
|
|
),
|
|
getUserInfo: true, // 按需获取用户信息
|
|
);
|
|
|
|
if (authResult) {
|
|
// 确保 FlWX() 返回的是同一个实例,并且 token 已经被设置
|
|
// 这里假设 FlWX().token 在 authBy 成功后被设置
|
|
print("数据获取_FlWX().token${FlWX().token}");
|
|
// SettingsController().userName = FlWX().nickname;
|
|
// SettingsController().avatar = FlWX().headimgurl;
|
|
// SettingsController().userId = FlWX().openid;
|
|
requestLogin({
|
|
"mail": "",
|
|
"phone": "",
|
|
"stype": loginType,
|
|
"vcode": "",
|
|
"ttoken": FlWX().code,
|
|
"phonemodel": "",
|
|
"phonemac": "",
|
|
"name": FlWX().nickname,
|
|
"avatar": FlWX().headimgurl,
|
|
});
|
|
// 验证成功,跳转到配对页面
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Get.snackbar(
|
|
'错误',
|
|
'登录失败,请重试',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
Get.snackbar(
|
|
'错误',
|
|
'登录失败,请重试',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.red.withOpacity(0.1),
|
|
colorText: Colors.red,
|
|
);
|
|
} finally {
|
|
_isLoading.value = false;
|
|
}
|
|
}
|
|
|
|
// 使用登陆请求服务器
|
|
Future<void> requestLogin(Map params) async {
|
|
var response = await Api.login({
|
|
"mail": params["mail"],
|
|
"phone": params["phone"],
|
|
"stype": params["stype"],
|
|
"vcode": params["vcode"],
|
|
"ttoken": params["ttoken"],
|
|
"phonemodel": params["phonemodel"],
|
|
"phonemac": params["phonemac"],
|
|
"name": params["name"],
|
|
"avatar": params["avatar"],
|
|
});
|
|
if (response != null) {
|
|
print("请求后台数据login回复数据=${response}");
|
|
|
|
// final instance = await GetStorage();
|
|
await _storage.write('loginInfo', jsonEncode(response));
|
|
// 2. 将响应数据转换为User对象
|
|
try {
|
|
print('响应数据: ${response.toString()}'); // 调试输出原始数据
|
|
|
|
if (response['token'].toString() != '') {
|
|
// 新增:检查 user 字段是否存在且类型正确
|
|
// if (response.containsKey('data') && response['data'] is Map) {
|
|
final user = User.fromJson(response);
|
|
print('用户初始化成功: ${response['token'].toString()}');
|
|
await _storage.write("logintoken", response['token'].toString());
|
|
// 存储到 SharedPreferences (仅存储用户数据)
|
|
//final prefs = await GetStorage();
|
|
await _storage.write(
|
|
'user', jsonEncode(user.toJson())); // 使用 user.toJson()
|
|
|
|
Get.offAllNamed(Routes.pairing);
|
|
// } else {
|
|
// throw FormatException('API 响应缺少 user 字段');
|
|
// }
|
|
} else {
|
|
throw Exception('接口返回错误: ${response['msg']}');
|
|
}
|
|
} catch (e, stack) {
|
|
// 新增:打印堆栈跟踪
|
|
print('用户数据处理失败: $e');
|
|
print('堆栈跟踪: $stack');
|
|
Get.snackbar('错误', '用户数据解析失败,请检查网络');
|
|
}
|
|
|
|
// Get.offAll(() => IndexWidget());
|
|
}
|
|
}
|
|
|
|
// 使用密码登录
|
|
void usePasswordLogin() {
|
|
// 这里可以实现密码登录的逻辑
|
|
Get.snackbar(
|
|
'提示',
|
|
'密码登录功能尚未实现',
|
|
snackPosition: SnackPosition.TOP,
|
|
backgroundColor: Colors.blue.withOpacity(0.1),
|
|
colorText: Colors.blue,
|
|
);
|
|
}
|
|
}
|
|
|