完善游客模式登录 并且新增设备管理
This commit is contained in:
@@ -65,7 +65,7 @@ class KRCrispController extends GetxController {
|
||||
try {
|
||||
final appData = KRAppRunData();
|
||||
final currentLanguage = KRLanguageUtils.getCurrentLanguageCode();
|
||||
final userEmail = appData.kr_account ?? '';
|
||||
final userEmail = appData.kr_account.value ?? '';
|
||||
|
||||
// 获取设备 ID
|
||||
final deviceId = await KRDeviceUtil().kr_getDeviceId();
|
||||
|
||||
@@ -43,23 +43,17 @@ class KRDeleteAccountController extends GetxController {
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
// 发送验证码(仅支持邮箱)
|
||||
Future<void> kr_sendCode() async {
|
||||
final account = KRAppRunData.getInstance().kr_account;
|
||||
final account = KRAppRunData.getInstance().kr_account.value;
|
||||
if (account == null || account.isEmpty) {
|
||||
KRCommonUtil.kr_showToast('账号不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 判断账号类型
|
||||
final isEmail = KRAppRunData.getInstance().kr_loginType == KRLoginType.kr_email;
|
||||
final type = isEmail ? KRLoginType.kr_email : KRLoginType.kr_telephone;
|
||||
|
||||
// 发送验证码
|
||||
// 发送验证码(简化后的 API 只需要 email 和 type)
|
||||
final result = await _authApi.kr_sendCode(
|
||||
type,
|
||||
account,
|
||||
KRAppRunData.getInstance().kr_areaCode, // 手机号不需要区号
|
||||
account, // 邮箱地址
|
||||
2, // 删除账号的验证码类型
|
||||
);
|
||||
|
||||
@@ -70,7 +64,7 @@ class KRDeleteAccountController extends GetxController {
|
||||
(success) {
|
||||
kr_canSendCode.value = false;
|
||||
kr_countdown.value = 60;
|
||||
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (kr_countdown.value > 0) {
|
||||
kr_countdown.value--;
|
||||
@@ -89,11 +83,12 @@ class KRDeleteAccountController extends GetxController {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.sendCode);
|
||||
return;
|
||||
}
|
||||
final result = await _authApi.kr_deleteAccount(
|
||||
KRAppRunData.getInstance().kr_loginType ?? KRLoginType.kr_telephone,
|
||||
|
||||
// 删除账号(简化后的 API 只需要 code)
|
||||
final result = await _authApi.kr_deleteAccount(
|
||||
kr_codeController.text,
|
||||
);
|
||||
|
||||
result.fold(
|
||||
(error) {
|
||||
KRCommonUtil.kr_showToast(error.msg);
|
||||
@@ -103,7 +98,5 @@ class KRDeleteAccountController extends GetxController {
|
||||
KRAppRunData.getInstance().kr_loginOut();
|
||||
},
|
||||
);
|
||||
// TODO: 实现删除账号的逻辑
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../controllers/kr_device_management_controller.dart';
|
||||
|
||||
class KRDeviceManagementBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<KRDeviceManagementController>(
|
||||
() => KRDeviceManagementController(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_device_util.dart';
|
||||
import 'package:kaer_with_panels/app/services/api_service/kr_api.user.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/dialogs/kr_dialog.dart';
|
||||
import 'package:kaer_with_panels/app/common/app_run_data.dart';
|
||||
import 'package:kaer_with_panels/app/services/api_service/kr_auth_api.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_device_info_service.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_subscribe_service.dart';
|
||||
import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
class KRDeviceManagementController extends GetxController {
|
||||
// 设备列表
|
||||
final RxList<Map<String, dynamic>> devices = <Map<String, dynamic>>[].obs;
|
||||
|
||||
// 加载状态
|
||||
final RxBool isLoading = true.obs;
|
||||
|
||||
// 当前设备ID
|
||||
String? currentDeviceId;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_initDeviceId();
|
||||
loadDeviceList();
|
||||
}
|
||||
|
||||
/// 初始化当前设备ID
|
||||
Future<void> _initDeviceId() async {
|
||||
try {
|
||||
currentDeviceId = await KRDeviceUtil().kr_getDeviceId();
|
||||
KRLogUtil.kr_i('当前设备ID: $currentDeviceId', tag: 'DeviceManagement');
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('获取设备ID失败: $e', tag: 'DeviceManagement');
|
||||
}
|
||||
}
|
||||
|
||||
/// 加载设备列表
|
||||
Future<void> loadDeviceList() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
KRLogUtil.kr_i('开始加载设备列表', tag: 'DeviceManagement');
|
||||
|
||||
// 调用API获取设备列表
|
||||
final result = await KRUserApi().kr_getUserDevices();
|
||||
|
||||
result.fold(
|
||||
(error) {
|
||||
KRLogUtil.kr_e('加载设备列表失败: ${error.msg}', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', error.msg);
|
||||
},
|
||||
(deviceList) {
|
||||
KRLogUtil.kr_i('获取到 ${deviceList.length} 个设备', tag: 'DeviceManagement');
|
||||
|
||||
// 转换设备数据格式
|
||||
devices.value = deviceList.map((device) {
|
||||
final identifier = device['identifier']?.toString() ?? '';
|
||||
final isCurrent = identifier == currentDeviceId;
|
||||
|
||||
return {
|
||||
'id': device['id']?.toString() ?? '',
|
||||
'identifier': identifier,
|
||||
'device_name': device['user_agent'] ?? '未知设备',
|
||||
'ip': device['ip'] ?? '',
|
||||
'last_login': device['updated_at'] ?? device['created_at'] ?? '',
|
||||
'is_current': isCurrent,
|
||||
'enabled': device['enabled'] ?? true,
|
||||
'online': device['online'] ?? false,
|
||||
};
|
||||
}).toList();
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
KRLogUtil.kr_e('加载设备列表异常: $e', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', '加载设备列表失败');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 删除设备
|
||||
Future<void> deleteDevice(String id) async {
|
||||
try {
|
||||
// 检查是否是本机设备
|
||||
final device = devices.firstWhere(
|
||||
(d) => d['id'] == id,
|
||||
orElse: () => {},
|
||||
);
|
||||
|
||||
if (device.isEmpty) return;
|
||||
|
||||
final isCurrent = device['is_current'] ?? false;
|
||||
|
||||
// 使用响应式变量来接收确认结果
|
||||
bool? confirmed;
|
||||
|
||||
// 显示确认对话框
|
||||
await KRDialog.show(
|
||||
title: '确认删除',
|
||||
message: isCurrent
|
||||
? '确定要删除本机设备吗?删除后将使用设备登录自动重新登录。'
|
||||
: '确定要删除此设备吗?删除后该设备将被强制下线。',
|
||||
icon: Container(
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.warning_rounded,
|
||||
color: Colors.red,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
confirmText: '删除',
|
||||
cancelText: '取消',
|
||||
onConfirm: () {
|
||||
confirmed = true;
|
||||
},
|
||||
onCancel: () {
|
||||
confirmed = false;
|
||||
},
|
||||
);
|
||||
|
||||
if (confirmed != true) return;
|
||||
|
||||
KRLogUtil.kr_i('开始解绑设备 - id: $id, isCurrent: $isCurrent', tag: 'DeviceManagement');
|
||||
|
||||
// 调用API解绑设备
|
||||
final result = await KRUserApi().kr_unbindUserDevice(id);
|
||||
|
||||
result.fold(
|
||||
(error) {
|
||||
KRLogUtil.kr_e('删除设备失败: ${error.msg}', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', '删除失败:${error.msg}');
|
||||
},
|
||||
(_) async {
|
||||
KRLogUtil.kr_i('设备删除成功', tag: 'DeviceManagement');
|
||||
|
||||
if (isCurrent) {
|
||||
// 如果删除的是本机设备,重新进行设备登录
|
||||
KRLogUtil.kr_i('本机设备已删除,准备重新登录', tag: 'DeviceManagement');
|
||||
|
||||
// 先关闭当前设备管理页面
|
||||
Get.back();
|
||||
|
||||
// 执行重新登录
|
||||
await _reloginWithDevice();
|
||||
} else {
|
||||
// 删除其他设备,从列表中移除
|
||||
devices.removeWhere((device) => device['id'] == id);
|
||||
Get.snackbar('成功', '设备已删除');
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
KRLogUtil.kr_e('删除设备异常: $e', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', '删除失败:$e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 重新使用设备登录
|
||||
Future<void> _reloginWithDevice() async {
|
||||
try {
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_i('开始重新进行设备登录', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'DeviceManagement');
|
||||
|
||||
// 先清除当前的用户信息(但不调用 kr_loginOut,避免显示登录界面)
|
||||
final appRunData = KRAppRunData.getInstance();
|
||||
appRunData.kr_isLogin.value = false;
|
||||
appRunData.kr_token = null;
|
||||
appRunData.kr_account.value = null;
|
||||
appRunData.kr_userId.value = null;
|
||||
|
||||
// 检查是否启用设备登录
|
||||
final siteConfigService = KRSiteConfigService();
|
||||
final isDeviceLoginEnabled = siteConfigService.isDeviceLoginEnabled();
|
||||
|
||||
if (!isDeviceLoginEnabled) {
|
||||
KRLogUtil.kr_w('设备登录未启用,执行完整退出登录', tag: 'DeviceManagement');
|
||||
Get.snackbar('提示', '设备登录未启用,请手动登录');
|
||||
await appRunData.kr_loginOut();
|
||||
return;
|
||||
}
|
||||
|
||||
KRLogUtil.kr_i('设备登录已启用,开始调用设备登录接口', tag: 'DeviceManagement');
|
||||
|
||||
// 初始化设备信息服务(如果还没初始化)
|
||||
await KRDeviceInfoService().initialize();
|
||||
|
||||
// 调用设备登录接口
|
||||
final authApi = KRAuthApi();
|
||||
final result = await authApi.kr_deviceLogin();
|
||||
|
||||
result.fold(
|
||||
(error) {
|
||||
// 设备登录失败
|
||||
KRLogUtil.kr_e('设备登录失败: ${error.msg}', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', '自动登录失败:${error.msg},请手动登录');
|
||||
|
||||
// 执行完整退出登录,显示登录界面
|
||||
appRunData.kr_loginOut();
|
||||
},
|
||||
(token) async {
|
||||
// 设备登录成功
|
||||
KRLogUtil.kr_i('✅ 设备登录成功!', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_i('🎫 Token: ${token.substring(0, min(20, token.length))}...', tag: 'DeviceManagement');
|
||||
|
||||
// 保存新的用户信息
|
||||
final deviceId = KRDeviceInfoService().deviceId ?? 'unknown';
|
||||
await appRunData.kr_saveUserInfo(
|
||||
token,
|
||||
'device_$deviceId',
|
||||
KRLoginType.kr_email,
|
||||
null,
|
||||
);
|
||||
|
||||
KRLogUtil.kr_i('✅ 设备重新登录成功,已更新用户信息', tag: 'DeviceManagement');
|
||||
|
||||
// 等待一小段时间,确保登录状态已经更新
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
// 刷新订阅信息
|
||||
KRLogUtil.kr_i('🔄 开始刷新订阅信息...', tag: 'DeviceManagement');
|
||||
try {
|
||||
await KRSubscribeService().kr_refreshAll();
|
||||
KRLogUtil.kr_i('✅ 订阅信息刷新成功', tag: 'DeviceManagement');
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('订阅信息刷新失败: $e', tag: 'DeviceManagement');
|
||||
}
|
||||
|
||||
Get.snackbar('成功', '已自动重新登录');
|
||||
},
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
KRLogUtil.kr_e('设备重新登录异常: $e', tag: 'DeviceManagement');
|
||||
KRLogUtil.kr_e('堆栈跟踪: $stackTrace', tag: 'DeviceManagement');
|
||||
Get.snackbar('错误', '自动登录失败,请手动登录');
|
||||
|
||||
// 发生异常,执行完整退出登录
|
||||
await KRAppRunData.getInstance().kr_loginOut();
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取设备类型和图标
|
||||
Map<String, dynamic> getDeviceTypeInfo(String userAgent) {
|
||||
String deviceType = '未知设备';
|
||||
String iconName = 'devices';
|
||||
|
||||
if (userAgent.contains('Android') || userAgent.toLowerCase().contains('android')) {
|
||||
deviceType = '安卓设备';
|
||||
iconName = 'phone_android';
|
||||
} else if (userAgent.contains('iOS') || userAgent.contains('iPhone') || userAgent.toLowerCase().contains('ios')) {
|
||||
deviceType = 'iOS 设备';
|
||||
iconName = 'phone_iphone';
|
||||
} else if (userAgent.contains('iPad')) {
|
||||
deviceType = 'iPad';
|
||||
iconName = 'tablet';
|
||||
} else if (userAgent.contains('macOS') || userAgent.contains('Mac') || userAgent.toLowerCase().contains('mac')) {
|
||||
deviceType = 'macOS';
|
||||
iconName = 'desktop_mac';
|
||||
} else if (userAgent.contains('Windows') || userAgent.toLowerCase().contains('windows')) {
|
||||
deviceType = 'Windows';
|
||||
iconName = 'computer';
|
||||
} else if (userAgent.contains('Linux') || userAgent.toLowerCase().contains('linux')) {
|
||||
deviceType = 'Linux';
|
||||
iconName = 'computer';
|
||||
}
|
||||
|
||||
return {
|
||||
'type': deviceType,
|
||||
'icon': iconName,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
|
||||
|
||||
import '../controllers/kr_device_management_controller.dart';
|
||||
|
||||
class KRDeviceManagementView extends GetView<KRDeviceManagementController> {
|
||||
const KRDeviceManagementView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
extendBodyBehindAppBar: true,
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color.fromRGBO(23, 151, 255, 0.15),
|
||||
Color.fromRGBO(23, 151, 255, 0.05),
|
||||
],
|
||||
stops: [0.0, 0.28],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 顶部导航栏
|
||||
AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
size: 20.w,
|
||||
),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
title: Text(
|
||||
'设备管理',
|
||||
style: KrAppTextStyle(
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
// 内容区域
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
|
||||
if (controller.devices.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.devices_other,
|
||||
size: 64.w,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
SizedBox(height: 16.w),
|
||||
Text(
|
||||
'暂无登录设备',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => controller.loadDeviceList(),
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.all(16.w),
|
||||
itemCount: controller.devices.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildDeviceItem(
|
||||
context,
|
||||
controller.devices[index],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建设备项
|
||||
Widget _buildDeviceItem(
|
||||
BuildContext context, Map<String, dynamic> device) {
|
||||
final id = device['id'] ?? '';
|
||||
final identifier = device['identifier'] ?? '';
|
||||
final userAgent = device['device_name'] ?? '未知设备';
|
||||
final isCurrent = device['is_current'] ?? false;
|
||||
final ip = device['ip'] ?? '';
|
||||
final lastLoginRaw = device['last_login'];
|
||||
final String lastLogin = lastLoginRaw?.toString() ?? '';
|
||||
|
||||
// 获取设备类型信息
|
||||
final deviceInfo = controller.getDeviceTypeInfo(userAgent);
|
||||
final deviceType = deviceInfo['type'] as String;
|
||||
final iconName = deviceInfo['icon'] as String;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.only(bottom: 12.w),
|
||||
padding: EdgeInsets.all(16.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(12.w),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.03),
|
||||
blurRadius: 10.w,
|
||||
offset: Offset(0, 2.w),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 设备类型和操作按钮
|
||||
Row(
|
||||
children: [
|
||||
// 设备图标
|
||||
Container(
|
||||
width: 48.w,
|
||||
height: 48.w,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1797FF).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8.w),
|
||||
),
|
||||
child: Icon(
|
||||
_getIconData(iconName),
|
||||
color: const Color(0xFF1797FF),
|
||||
size: 24.w,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
// 设备信息
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
deviceType,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
if (isCurrent) ...[
|
||||
SizedBox(width: 8.w),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8.w,
|
||||
vertical: 2.w,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4.w),
|
||||
),
|
||||
child: Text(
|
||||
'本机',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4.w),
|
||||
Text(
|
||||
'ID: ${identifier.substring(0, identifier.length > 12 ? 12 : identifier.length)}...',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 删除按钮
|
||||
TextButton(
|
||||
onPressed: () => controller.deleteDevice(id),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.w),
|
||||
),
|
||||
child: Text(
|
||||
'删除',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// 分隔线
|
||||
if (ip.isNotEmpty || lastLogin.isNotEmpty) ...[
|
||||
SizedBox(height: 12.w),
|
||||
Divider(height: 1, color: Theme.of(context).dividerColor),
|
||||
SizedBox(height: 12.w),
|
||||
],
|
||||
// 详细信息
|
||||
if (ip.isNotEmpty)
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'IP地址',
|
||||
ip,
|
||||
),
|
||||
if (ip.isNotEmpty && lastLogin.isNotEmpty) SizedBox(height: 8.w),
|
||||
if (lastLogin.isNotEmpty)
|
||||
_buildInfoRow(
|
||||
context,
|
||||
'最后登录',
|
||||
_formatDateTime(lastLoginRaw),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建信息行
|
||||
Widget _buildInfoRow(BuildContext context, String label, String value) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
'$label: ',
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 格式化时间
|
||||
String _formatDateTime(dynamic timestamp) {
|
||||
if (timestamp == null) return '未知';
|
||||
|
||||
try {
|
||||
DateTime dateTime;
|
||||
if (timestamp is int) {
|
||||
// 判断是秒级时间戳(10位)还是毫秒级时间戳(13位)
|
||||
if (timestamp > 9999999999) {
|
||||
// 毫秒级时间戳
|
||||
dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
} else {
|
||||
// 秒级时间戳
|
||||
dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
|
||||
}
|
||||
} else if (timestamp is String) {
|
||||
dateTime = DateTime.parse(timestamp);
|
||||
} else {
|
||||
return '未知';
|
||||
}
|
||||
return '${dateTime.year}-${dateTime.month.toString().padLeft(2, '0')}-${dateTime.day.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
} catch (e) {
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取图标数据
|
||||
IconData _getIconData(String iconName) {
|
||||
switch (iconName) {
|
||||
case 'phone_android':
|
||||
return Icons.phone_android;
|
||||
case 'phone_iphone':
|
||||
return Icons.phone_iphone;
|
||||
case 'tablet':
|
||||
return Icons.tablet_mac;
|
||||
case 'desktop_mac':
|
||||
return Icons.desktop_mac;
|
||||
case 'computer':
|
||||
return Icons.computer;
|
||||
default:
|
||||
return Icons.devices;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,10 +183,7 @@ class KRHomeController extends GetxController {
|
||||
if (isValidLogin) {
|
||||
kr_currentViewStatus.value = KRHomeViewsStatus.kr_loggedIn;
|
||||
KRLogUtil.kr_i('设置为已登录状态', tag: 'HomeController');
|
||||
|
||||
// 检查公告服务
|
||||
KRAnnouncementService().kr_checkAnnouncement();
|
||||
|
||||
|
||||
// 确保订阅服务初始化
|
||||
_kr_ensureSubscribeServiceInitialized();
|
||||
} else {
|
||||
@@ -255,7 +252,15 @@ class KRHomeController extends GetxController {
|
||||
} else {
|
||||
kr_currentViewStatus.value = KRHomeViewsStatus.kr_notLoggedIn;
|
||||
KRLogUtil.kr_i('登录状态变化:设置为未登录', tag: 'HomeController');
|
||||
|
||||
// 重置列表状态,防止出现无限高度
|
||||
kr_currentListStatus.value = KRHomeViewsListStatus.kr_none;
|
||||
|
||||
// 退出登录清理订阅服务
|
||||
kr_subscribeService.kr_logout();
|
||||
|
||||
// 显式更新底部面板高度,确保未登录状态下高度正确
|
||||
kr_updateBottomPanelHeight();
|
||||
}
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('处理登录状态变化失败: $e', tag: 'HomeController');
|
||||
|
||||
@@ -26,30 +26,13 @@ class KRHomeConnectionOptionsView extends GetView<KRHomeController> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.w),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: _buildConnectionOption(
|
||||
"home_server",
|
||||
AppTranslations.kr_home.dedicatedServers,
|
||||
context,
|
||||
onTap: () {
|
||||
controller.kr_switchListStatus(KRHomeViewsListStatus.kr_serverList);
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Flexible(
|
||||
child: _buildConnectionOption(
|
||||
"home_ct",
|
||||
AppTranslations.kr_home.countryRegion,
|
||||
context,
|
||||
onTap: () {
|
||||
controller.kr_switchListStatus(KRHomeViewsListStatus.kr_countrySubscribeList);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
_buildConnectionOption(
|
||||
"home_ct",
|
||||
AppTranslations.kr_home.countryRegion,
|
||||
context,
|
||||
onTap: () {
|
||||
controller.kr_switchListStatus(KRHomeViewsListStatus.kr_countrySubscribeList);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -31,28 +31,32 @@ class KRHomeView extends GetView<KRHomeController> {
|
||||
// 地图视图
|
||||
const KRHomeMapView(),
|
||||
// 登录视图
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
spreadRadius: 0,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height * 0.8,
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
spreadRadius: 0,
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, -2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const KRLoginView(),
|
||||
),
|
||||
child: const KRLoginView(),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -71,16 +71,15 @@ class KRInviteController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// ⚠️ 已废弃:新版本后端不再提供 kr_getUserInfo 接口
|
||||
// 邀请码现在使用 AppConfig 中的固定值,等待新接口实现
|
||||
Future<void> _kr_fetchUserInfo() async {
|
||||
try {
|
||||
kr_isLoading.value = true;
|
||||
final either = await KRUserApi().kr_getUserInfo();
|
||||
either.fold(
|
||||
(error) => KRCommonUtil.kr_showToast(error.msg),
|
||||
(userInfo) {
|
||||
kr_referCode.value = userInfo.referCode;
|
||||
},
|
||||
);
|
||||
|
||||
// 使用 AppConfig 中的固定邀请码
|
||||
kr_referCode.value = AppConfig.kr_userReferCode;
|
||||
|
||||
} catch (e) {
|
||||
KRCommonUtil.kr_showToast(e.toString());
|
||||
} finally {
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:kaer_with_panels/app/model/kr_area_code.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_common_util.dart';
|
||||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_event_bus.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
|
||||
|
||||
import '../../../localization/kr_language_utils.dart';
|
||||
|
||||
@@ -125,9 +126,9 @@ class KRLoginController extends GetxController
|
||||
String kr_getNextBtnText() {
|
||||
switch (kr_loginStatus.value) {
|
||||
case KRLoginProgressStatus.kr_check:
|
||||
return AppTranslations.kr_login.next;
|
||||
return AppTranslations.kr_login.passwordLogin; // 显示"登录"
|
||||
case KRLoginProgressStatus.kr_loginByCode:
|
||||
return AppTranslations.kr_login.codeLogin;
|
||||
return AppTranslations.kr_login.passwordLogin; // 已废弃,保留以防兼容性问题
|
||||
case KRLoginProgressStatus.kr_loginByPsd:
|
||||
return AppTranslations.kr_login.passwordLogin;
|
||||
case KRLoginProgressStatus.kr_registerSendCode:
|
||||
@@ -198,25 +199,17 @@ class KRLoginController extends GetxController
|
||||
}
|
||||
});
|
||||
|
||||
// 修改 accountController 的监听器
|
||||
// accountController 监听器(仅支持邮箱)
|
||||
accountController.addListener(() {
|
||||
String input = accountController.text.trim();
|
||||
|
||||
// 延迟执行状态更新,避免在输入过程中频繁切换
|
||||
// 延迟执行状态更新
|
||||
Future.microtask(() {
|
||||
final isNumeric = _isNumeric(input);
|
||||
if (isNumeric && kr_loginType.value != KRLoginType.kr_telephone) {
|
||||
kr_loginType.value = KRLoginType.kr_telephone;
|
||||
kr_emailList.clear();
|
||||
kr_removeOverlay();
|
||||
} else if (!isNumeric && kr_loginType.value != KRLoginType.kr_email) {
|
||||
kr_loginType.value = KRLoginType.kr_email;
|
||||
}
|
||||
// 始终保持邮箱类型
|
||||
kr_loginType.value = KRLoginType.kr_email;
|
||||
|
||||
// 只在邮箱模式下更新邮箱列表
|
||||
if (!isNumeric) {
|
||||
kr_emailList.value = kr_generateAndSortEmailList(input);
|
||||
}
|
||||
// 更新邮箱建议列表
|
||||
kr_emailList.value = kr_generateAndSortEmailList(input);
|
||||
|
||||
kr_accountHasText.value = input.isNotEmpty;
|
||||
});
|
||||
@@ -259,40 +252,29 @@ class KRLoginController extends GetxController
|
||||
return numericRegex.hasMatch(input);
|
||||
}
|
||||
|
||||
/// 检查是否注册
|
||||
/// 直接登录(不再检查是否注册)
|
||||
void kr_check() async {
|
||||
if (accountController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterAccount);
|
||||
return;
|
||||
}
|
||||
|
||||
final either = await KRAuthApi().kr_isRegister(
|
||||
kr_loginType.value,
|
||||
accountController.text,
|
||||
kr_loginType == KRLoginType.kr_telephone
|
||||
? kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode
|
||||
: null);
|
||||
either.fold((l) {
|
||||
KRCommonUtil.kr_showToast(l.msg);
|
||||
}, (r) async {
|
||||
kr_isRegistered.value = r;
|
||||
kr_loginStatus.value = r
|
||||
? KRLoginProgressStatus.kr_loginByPsd
|
||||
: KRLoginProgressStatus.kr_registerSendCode;
|
||||
});
|
||||
if (psdController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword);
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接调用登录
|
||||
kr_login();
|
||||
}
|
||||
|
||||
/// 发送验证码
|
||||
/// 发送验证码(仅支持邮箱)
|
||||
void kr_sendCode() async {
|
||||
final either = await KRAuthApi().kr_sendCode(
|
||||
kr_loginType.value,
|
||||
accountController.text,
|
||||
kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode,
|
||||
kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode
|
||||
? 1
|
||||
: kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSendCode
|
||||
? 2
|
||||
: 2);
|
||||
? 2 // 注册验证码类型为2
|
||||
: 3); // 重置密码验证码类型为3
|
||||
either.fold((l) {
|
||||
KRCommonUtil.kr_showToast(l.msg);
|
||||
}, (r) async {
|
||||
@@ -301,28 +283,15 @@ class KRLoginController extends GetxController
|
||||
});
|
||||
}
|
||||
|
||||
/// 开始登录
|
||||
/// 开始登录(仅支持邮箱+密码)
|
||||
void kr_login() async {
|
||||
if (kr_loginStatus == KRLoginProgressStatus.kr_loginByCode &&
|
||||
codeController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kr_loginStatus == KRLoginProgressStatus.kr_loginByPsd &&
|
||||
psdController.text.isEmpty) {
|
||||
if (psdController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword);
|
||||
return;
|
||||
}
|
||||
|
||||
final either = await KRAuthApi().kr_login(
|
||||
kr_loginType.value,
|
||||
kr_loginStatus.value == KRLoginProgressStatus.kr_loginByPsd,
|
||||
accountController.text,
|
||||
kr_loginType == KRLoginType.kr_telephone
|
||||
? kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode
|
||||
: null,
|
||||
codeController.text,
|
||||
psdController.text);
|
||||
either.fold((l) {
|
||||
KRCommonUtil.kr_showToast(l.msg);
|
||||
@@ -331,8 +300,14 @@ class KRLoginController extends GetxController
|
||||
});
|
||||
}
|
||||
|
||||
/// 开始注册
|
||||
/// 开始注册(仅支持邮箱,验证码和邀请码可选)
|
||||
void kr_register() async {
|
||||
// 验证邮箱
|
||||
if (accountController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterAccount);
|
||||
return;
|
||||
}
|
||||
|
||||
if (psdController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword);
|
||||
return;
|
||||
@@ -346,20 +321,26 @@ class KRLoginController extends GetxController
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否需要验证码(基于站点配置)
|
||||
final siteConfig = KRSiteConfigService();
|
||||
final needVerification = siteConfig.isEmailVerificationEnabled() ||
|
||||
siteConfig.isRegisterVerificationEnabled();
|
||||
|
||||
if (needVerification && codeController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterCode);
|
||||
return;
|
||||
}
|
||||
|
||||
final either = await KRAuthApi().kr_register(
|
||||
kr_loginType.value,
|
||||
accountController.text,
|
||||
kr_loginType == KRLoginType.kr_telephone
|
||||
? kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode
|
||||
: null,
|
||||
codeController.text,
|
||||
psdController.text,
|
||||
inviteCode: inviteCodeController.text);
|
||||
code: codeController.text.isEmpty ? null : codeController.text,
|
||||
inviteCode: inviteCodeController.text.isEmpty ? null : inviteCodeController.text);
|
||||
either.fold((l) {
|
||||
KRCommonUtil.kr_showToast(l.msg);
|
||||
}, (r) async {
|
||||
_saveLoginData(r);
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.registerSuccess);
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.registerSuccess);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -390,20 +371,17 @@ class KRLoginController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证验证码
|
||||
/// 验证验证码(仅支持邮箱)
|
||||
void kr_checkVerificationCode(KRLoginProgressStatus status) async {
|
||||
final either = await KRAuthApi().kr_checkVerificationCode(
|
||||
kr_loginType.value,
|
||||
accountController.text,
|
||||
kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode,
|
||||
codeController.text,
|
||||
kr_loginStatus.value == KRLoginProgressStatus.kr_registerSendCode
|
||||
? 1
|
||||
: 2);
|
||||
? 2 // 注册验证码类型为2
|
||||
: 3); // 重置密码验证码类型为3
|
||||
either.fold((l) {
|
||||
KRCommonUtil.kr_showToast(l.msg);
|
||||
}, (r) async {
|
||||
|
||||
if (status == KRLoginProgressStatus.kr_registerSendCode) {
|
||||
kr_loginStatus.value = KRLoginProgressStatus.kr_registerSetPsd;
|
||||
} else if (status == KRLoginProgressStatus.kr_forgetPsdSendCode) {
|
||||
@@ -412,7 +390,7 @@ class KRLoginController extends GetxController
|
||||
});
|
||||
}
|
||||
|
||||
/// 忘记密码--- 设置新密码
|
||||
/// 忘记密码-设置新密码(仅支持邮箱)
|
||||
void kr_setNewPsdByForgetPsd() async {
|
||||
if (psdController.text.isEmpty) {
|
||||
KRCommonUtil.kr_showToast(AppTranslations.kr_login.enterPassword);
|
||||
@@ -428,11 +406,7 @@ class KRLoginController extends GetxController
|
||||
}
|
||||
|
||||
final either = await KRAuthApi().kr_setNewPsdByForgetPsd(
|
||||
kr_loginType.value,
|
||||
accountController.text,
|
||||
kr_loginType == KRLoginType.kr_telephone
|
||||
? kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode
|
||||
: null,
|
||||
codeController.text,
|
||||
psdController.text);
|
||||
either.fold((l) {
|
||||
@@ -464,15 +438,13 @@ class KRLoginController extends GetxController
|
||||
});
|
||||
}
|
||||
|
||||
/// 设置登录数据
|
||||
/// 设置登录数据(仅支持邮箱)
|
||||
void _saveLoginData(String token) {
|
||||
KRAppRunData.getInstance().kr_saveUserInfo(
|
||||
token,
|
||||
accountController.text,
|
||||
kr_loginType.value,
|
||||
kr_loginType == KRLoginType.kr_telephone
|
||||
? kr_areaCodeList[kr_cutSeleteCodeIndex.value].kr_dialCode
|
||||
: null);
|
||||
KRLoginType.kr_email,
|
||||
null);
|
||||
kr_loginStatus.value = KRLoginProgressStatus.kr_check;
|
||||
|
||||
// 登录/注册成功后,发送消息触发订阅服务刷新
|
||||
@@ -480,6 +452,9 @@ class KRLoginController extends GetxController
|
||||
Future.delayed(Duration(milliseconds: 100), () {
|
||||
KREventBus().kr_sendMessage(KRMessageType.kr_payment);
|
||||
});
|
||||
|
||||
// 登录成功后返回到上一页
|
||||
Get.back();
|
||||
}
|
||||
|
||||
/// 根据输入内容匹配邮箱
|
||||
@@ -611,6 +586,14 @@ class KRLoginController extends GetxController
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
// 每次打开登录页面时,重置为登录状态(而不是注册状态)
|
||||
// 这样确保点击"登录/注册"按钮时始终显示登录页面
|
||||
kr_loginStatus.value = KRLoginProgressStatus.kr_check;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
kr_removeOverlay();
|
||||
@@ -640,13 +623,9 @@ class KRLoginController extends GetxController
|
||||
|
||||
void _updateInputState() {
|
||||
String input = accountController.text.trim();
|
||||
if (_isNumeric(input)) {
|
||||
kr_loginType.value = KRLoginType.kr_telephone;
|
||||
kr_emailList.clear();
|
||||
} else {
|
||||
kr_loginType.value = KRLoginType.kr_email;
|
||||
kr_emailList.value = kr_generateAndSortEmailList(input);
|
||||
}
|
||||
// 始终保持邮箱类型
|
||||
kr_loginType.value = KRLoginType.kr_email;
|
||||
kr_emailList.value = kr_generateAndSortEmailList(input);
|
||||
}
|
||||
|
||||
// 添加移除悬浮框的方法
|
||||
|
||||
@@ -14,6 +14,7 @@ import '../controllers/kr_login_controller.dart';
|
||||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
import 'package:kaer_with_panels/app/services/api_service/api.dart';
|
||||
import 'package:kaer_with_panels/app/common/app_config.dart';
|
||||
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
|
||||
|
||||
class KRLoginView extends GetView<KRLoginController> {
|
||||
const KRLoginView({super.key});
|
||||
@@ -21,15 +22,16 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (!FocusScope.of(context).hasPrimaryFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
}
|
||||
_hideDropdown();
|
||||
},
|
||||
child: Container(
|
||||
color: theme.scaffoldBackgroundColor,
|
||||
return Scaffold(
|
||||
backgroundColor: theme.scaffoldBackgroundColor,
|
||||
body: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () {
|
||||
if (!FocusScope.of(context).hasPrimaryFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
}
|
||||
_hideDropdown();
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.w),
|
||||
child: Obx(() {
|
||||
@@ -58,16 +60,18 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
}
|
||||
|
||||
Widget _buildCheckView(BuildContext context) {
|
||||
// 构建检查视图的代码
|
||||
// 构建登录视图 - 直接显示邮箱和密码输入框
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
// 总是显示返回按钮,包括 kr_check 状态
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
// 邮箱输入框
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
SizedBox(height: 8.w),
|
||||
// 密码输入框
|
||||
_buildPasswordInput(Theme.of(context)),
|
||||
_buildDynamicContent(Theme.of(context)),
|
||||
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
|
||||
SizedBox(height: _getBottomPadding()),
|
||||
@@ -75,6 +79,63 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建密码输入框(专用于 kr_check 状态)
|
||||
Widget _buildPasswordInput(ThemeData theme) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 52.w,
|
||||
decoration: ShapeDecoration(
|
||||
color: theme.cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.w),
|
||||
child: _buildIcon("login_psd"),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Obx(() => TextField(
|
||||
controller: controller.psdController,
|
||||
obscureText: controller.kr_obscureText.value,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
hintText: '请输入密码',
|
||||
hintStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
|
||||
suffixIcon: controller.kr_psdHasText.value
|
||||
? IconButton(
|
||||
icon: Icon(
|
||||
controller.kr_obscureText.value
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
color: const Color(0xFF999999),
|
||||
),
|
||||
onPressed: () {
|
||||
controller.kr_obscureText.value = !controller.kr_obscureText.value;
|
||||
},
|
||||
)
|
||||
: null,
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 获取底部间距
|
||||
double _getBottomPadding() {
|
||||
switch (controller.kr_loginStatus.value) {
|
||||
@@ -98,10 +159,7 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
_buildDynamicContent(Theme.of(context)),
|
||||
@@ -116,10 +174,7 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
_buildDynamicContent(Theme.of(context)),
|
||||
@@ -130,18 +185,29 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
}
|
||||
|
||||
Widget _buildRegisterSendCodeView(BuildContext context) {
|
||||
// 构建注册发送验证码视图的代码
|
||||
// 构建单页注册视图 - 所有字段显示在一个页面
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
_buildDynamicContent(Theme.of(context)),
|
||||
_buildNextButton(controller.kr_getNextBtnText(), Theme.of(context)),
|
||||
_buildBackButton(theme),
|
||||
_buildHeaderSection(theme),
|
||||
// 邮箱输入框
|
||||
_buildRegistrationEmailInput(theme),
|
||||
SizedBox(height: 8.w),
|
||||
// 验证码输入框(根据站点配置决定是否显示,包括底部间距)
|
||||
_buildRegistrationCodeInputWithSpacing(theme),
|
||||
// 密码输入框
|
||||
_buildRegistrationPasswordInput(theme),
|
||||
SizedBox(height: 8.w),
|
||||
// 确认密码输入框
|
||||
_buildRegistrationConfirmPasswordInput(theme),
|
||||
SizedBox(height: 8.w),
|
||||
// 邀请码输入框(可选)
|
||||
_buildInviteCodeInput(theme),
|
||||
SizedBox(height: 17.w),
|
||||
// 注册按钮
|
||||
_buildNextButton('注册账号', theme),
|
||||
SizedBox(height: _getBottomPadding()),
|
||||
],
|
||||
);
|
||||
@@ -152,10 +218,7 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
Column(
|
||||
@@ -178,10 +241,7 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
_buildDynamicContent(Theme.of(context)),
|
||||
@@ -196,10 +256,7 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_loginStatus.value != KRLoginProgressStatus.kr_check,
|
||||
child: _buildBackButton(Theme.of(context)),
|
||||
)),
|
||||
_buildBackButton(Theme.of(context)),
|
||||
_buildHeaderSection(Theme.of(context)),
|
||||
_buildInputSection(context, Theme.of(context)),
|
||||
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_forgetPsdSetPsd)
|
||||
@@ -670,7 +727,8 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
Widget? content;
|
||||
switch (controller.kr_loginStatus.value) {
|
||||
case KRLoginProgressStatus.kr_check:
|
||||
content = _buildAgreementText(theme);
|
||||
// 在登录状态显示"忘记密码"和切换按钮
|
||||
content = _buildLoginBtns(theme);
|
||||
case KRLoginProgressStatus.kr_loginByCode:
|
||||
case KRLoginProgressStatus.kr_loginByPsd:
|
||||
content = _buildLoginBtns(theme);
|
||||
@@ -744,6 +802,34 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
|
||||
/// 构建登录按钮行
|
||||
Widget _buildLoginBtns(ThemeData theme) {
|
||||
// 在 kr_check 状态下显示不同的按钮
|
||||
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
|
||||
return SizedBox(
|
||||
height: 24.w,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
_buildHoverableTextButton(
|
||||
text: 'login.forgotPassword'.tr,
|
||||
onTap: () => controller.kr_loginStatus.value =
|
||||
KRLoginProgressStatus.kr_forgetPsdSendCode,
|
||||
theme: theme,
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
_buildHoverableTextButton(
|
||||
text: '点我注册',
|
||||
onTap: () {
|
||||
// 跳转到注册页面
|
||||
controller.kr_loginStatus.value = KRLoginProgressStatus.kr_registerSendCode;
|
||||
},
|
||||
theme: theme,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 其他状态保持原有逻辑
|
||||
return SizedBox(
|
||||
height: 24.w,
|
||||
child: Row(
|
||||
@@ -802,7 +888,8 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
controller.kr_login();
|
||||
break;
|
||||
case KRLoginProgressStatus.kr_registerSendCode:
|
||||
controller.kr_checkCode();
|
||||
// 直接调用注册,不再需要多步骤
|
||||
controller.kr_register();
|
||||
break;
|
||||
case KRLoginProgressStatus.kr_registerSetPsd:
|
||||
controller.kr_register();
|
||||
@@ -981,8 +1068,21 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
|
||||
Widget _buildBackButton(ThemeData theme) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
controller.kr_back();
|
||||
print('👆 返回按钮被点击了!');
|
||||
print('👆 当前登录状态: ${controller.kr_loginStatus.value}');
|
||||
// 只有在初始登录状态(kr_check)时,返回按钮才返回主页
|
||||
// 其他所有状态(包括注册页面)都返回到上一步
|
||||
if (controller.kr_loginStatus.value == KRLoginProgressStatus.kr_check) {
|
||||
print('👆 调用 Get.back() 返回主页');
|
||||
Get.back();
|
||||
print('👆 Get.back() 调用完成');
|
||||
} else {
|
||||
// 其他状态:返回到上一步(例如从注册页返回登录页)
|
||||
print('👆 调用 controller.kr_back() 返回上一步');
|
||||
controller.kr_back();
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(0, 20.w, 0, 0),
|
||||
@@ -1095,4 +1195,293 @@ class KRLoginView extends GetView<KRLoginController> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建可悬停的文本按钮
|
||||
Widget _buildHoverableTextButton({
|
||||
required String text,
|
||||
required VoidCallback onTap,
|
||||
required ThemeData theme,
|
||||
}) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
bool isHovering = false;
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => isHovering = true),
|
||||
onExit: (_) => setState(() => isHovering = false),
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Text(
|
||||
text,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 13.sp,
|
||||
color: isHovering ? const Color(0xFF1796FF) : const Color(0xFF666666),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// ========== 注册页面相关组件 ==========
|
||||
|
||||
/// 构建注册页面的邮箱输入框
|
||||
Widget _buildRegistrationEmailInput(ThemeData theme) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 52.w,
|
||||
decoration: ShapeDecoration(
|
||||
color: theme.cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.w),
|
||||
child: _buildIcon("login_account"),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.accountController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'login.enterEmail'.tr,
|
||||
hintStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(() => Visibility(
|
||||
visible: controller.kr_accountHasText.value,
|
||||
child: GestureDetector(
|
||||
onTap: () => controller.accountController.clear(),
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w),
|
||||
child: _buildIcon("login_close"),
|
||||
),
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建注册页面的验证码输入框(包含间距)
|
||||
Widget _buildRegistrationCodeInputWithSpacing(ThemeData theme) {
|
||||
// 从站点配置服务获取验证配置(站点配置不是响应式的,不需要 Obx)
|
||||
final siteConfig = KRSiteConfigService();
|
||||
final needVerification = siteConfig.isEmailVerificationEnabled() ||
|
||||
siteConfig.isRegisterVerificationEnabled();
|
||||
|
||||
// 如果不需要验证码,返回空容器
|
||||
if (!needVerification) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
// 显示验证码输入框和底部间距
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildRegistrationCodeInput(theme),
|
||||
SizedBox(height: 8.w),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建注册页面的验证码输入框(根据站点配置显示)
|
||||
Widget _buildRegistrationCodeInput(ThemeData theme) {
|
||||
// 从站点配置服务获取验证配置
|
||||
final siteConfig = KRSiteConfigService();
|
||||
final needVerification = siteConfig.isEmailVerificationEnabled() ||
|
||||
siteConfig.isRegisterVerificationEnabled();
|
||||
|
||||
// 如果不需要验证码,返回空容器
|
||||
if (!needVerification) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
// 显示验证码输入框
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 52.w,
|
||||
decoration: ShapeDecoration(
|
||||
color: theme.cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.w),
|
||||
child: _buildIcon("login_code"),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.codeController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'login.enterCode'.tr,
|
||||
hintStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.kr_codeHasText.value)
|
||||
GestureDetector(
|
||||
onTap: () => controller.codeController.clear(),
|
||||
child: Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w),
|
||||
child: _buildIcon("login_close"),
|
||||
),
|
||||
),
|
||||
_buildSendCodeButton(theme),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建注册页面的密码输入框
|
||||
Widget _buildRegistrationPasswordInput(ThemeData theme) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 52.w,
|
||||
decoration: ShapeDecoration(
|
||||
color: theme.cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.w),
|
||||
child: _buildIcon("login_psd"),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Obx(() => TextField(
|
||||
controller: controller.psdController,
|
||||
obscureText: controller.kr_obscureText.value,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'login.enterPassword'.tr,
|
||||
hintStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
|
||||
suffixIcon: controller.kr_psdHasText.value
|
||||
? IconButton(
|
||||
icon: Icon(
|
||||
controller.kr_obscureText.value
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
color: const Color(0xFF999999),
|
||||
),
|
||||
onPressed: () {
|
||||
controller.kr_obscureText.value = !controller.kr_obscureText.value;
|
||||
},
|
||||
)
|
||||
: null,
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建注册页面的确认密码输入框
|
||||
Widget _buildRegistrationConfirmPasswordInput(ThemeData theme) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: 52.w,
|
||||
decoration: ShapeDecoration(
|
||||
color: theme.cardColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 0.5, color: const Color(0xFFD2D2D2)),
|
||||
borderRadius: BorderRadius.circular(10.r),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12.w),
|
||||
child: _buildIcon("login_psd"),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Obx(() => TextField(
|
||||
controller: controller.agPsdController,
|
||||
obscureText: controller.kr_obscureText.value,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'login.reenterPassword'.tr,
|
||||
hintStyle: theme.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16.w),
|
||||
suffixIcon: controller.kr_agPsdHasText.value
|
||||
? IconButton(
|
||||
icon: Icon(
|
||||
controller.kr_obscureText.value
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
color: const Color(0xFF999999),
|
||||
),
|
||||
onPressed: () {
|
||||
controller.kr_obscureText.value = !controller.kr_obscureText.value;
|
||||
},
|
||||
)
|
||||
: null,
|
||||
),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontSize: 14.sp,
|
||||
fontFamily: 'AlibabaPuHuiTi-Regular',
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-9
@@ -6,6 +6,7 @@ import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||||
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
|
||||
|
||||
import '../../../common/app_run_data.dart';
|
||||
import '../../../common/app_config.dart';
|
||||
import '../../../model/response/kr_already_subscribe.dart';
|
||||
import '../../../model/response/kr_payment_methods.dart';
|
||||
import '../../../routes/app_pages.dart';
|
||||
@@ -109,15 +110,20 @@ class KRPurchaseMembershipController extends GetxController {
|
||||
|
||||
/// 初始化用户信息
|
||||
Future<void> _iniUserInfo() async {
|
||||
final either0 = await KRUserApi().kr_getUserInfo();
|
||||
either0.fold(
|
||||
(error) {
|
||||
KRLogUtil.kr_e(error.msg, tag: 'AppRunData');
|
||||
},
|
||||
(userInfo) async {
|
||||
_kr_balance = userInfo.balance;
|
||||
},
|
||||
);
|
||||
// ⚠️ 已废弃:新版本后端不再提供 kr_getUserInfo 接口
|
||||
// 余额现在使用 AppConfig 中的固定值,等待新接口实现
|
||||
// final either0 = await KRUserApi().kr_getUserInfo();
|
||||
// either0.fold(
|
||||
// (error) {
|
||||
// KRLogUtil.kr_e(error.msg, tag: 'AppRunData');
|
||||
// },
|
||||
// (userInfo) async {
|
||||
// _kr_balance = userInfo.balance;
|
||||
// },
|
||||
// );
|
||||
|
||||
// 使用 AppConfig 中的固定余额
|
||||
_kr_balance = AppConfig.kr_userBalance;
|
||||
}
|
||||
|
||||
/// 获取用户已订阅套餐
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../controllers/kr_setting_controller.dart';
|
||||
import '../../../themes/kr_theme_service.dart';
|
||||
import '../../../localization/app_translations.dart';
|
||||
import '../../../routes/app_pages.dart';
|
||||
import '../../../common/app_run_data.dart';
|
||||
|
||||
class KRSettingView extends GetView<KRSettingController> {
|
||||
const KRSettingView({Key? key}) : super(key: key);
|
||||
@@ -235,12 +236,33 @@ class KRSettingView extends GetView<KRSettingController> {
|
||||
onChanged: (value) => controller.kr_helpImprove.value = value,
|
||||
),
|
||||
_kr_buildDivider(),
|
||||
_kr_buildActionTile(
|
||||
context,
|
||||
title: AppTranslations.kr_userInfo.myAccount,
|
||||
trailing: AppTranslations.kr_setting.goToDelete,
|
||||
onTap: controller.kr_deleteAccount,
|
||||
),
|
||||
Obx(() {
|
||||
final appRunData = KRAppRunData.getInstance();
|
||||
final isLoggedIn = appRunData.kr_isLogin.value;
|
||||
final isDeviceLogin = appRunData.isDeviceLogin();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
// 未登录,不显示此项
|
||||
return SizedBox.shrink();
|
||||
} else if (isDeviceLogin) {
|
||||
// 设备登录(游客模式),显示"点击这里登录/注册"
|
||||
return _kr_buildActionTile(
|
||||
context,
|
||||
title: "登录/注册",
|
||||
trailing: "",
|
||||
onTap: () => Get.toNamed(Routes.MR_LOGIN),
|
||||
);
|
||||
} else {
|
||||
// 正常登录,显示用户邮箱
|
||||
final userEmail = appRunData.kr_account.value ?? AppTranslations.kr_userInfo.myAccount;
|
||||
return _kr_buildActionTile(
|
||||
context,
|
||||
title: userEmail,
|
||||
trailing: AppTranslations.kr_setting.goToDelete,
|
||||
onTap: controller.kr_deleteAccount,
|
||||
);
|
||||
}
|
||||
}),
|
||||
_kr_buildDivider(),
|
||||
// _kr_buildTitleTile(
|
||||
// context,
|
||||
@@ -407,7 +429,7 @@ class KRSettingView extends GetView<KRSettingController> {
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
color: Theme.of(context).textTheme.bodySmall?.color,
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
|
||||
@@ -241,13 +241,29 @@ class KRSplashController extends GetxController {
|
||||
}
|
||||
|
||||
// 等待一小段时间确保所有初始化完成
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// 验证登录状态是否已正确设置
|
||||
final loginStatus = KRAppRunData.getInstance().kr_isLogin.value;
|
||||
KRLogUtil.kr_i('启动完成,最终登录状态: $loginStatus', tag: 'SplashController');
|
||||
final token = KRAppRunData.getInstance().kr_token;
|
||||
final hasToken = token != null && token.isNotEmpty;
|
||||
|
||||
// 直接导航到主页
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
print('🎯 准备进入主页');
|
||||
print('📊 最终登录状态: $loginStatus');
|
||||
print('🎫 Token存在: $hasToken');
|
||||
if (hasToken) {
|
||||
print('🎫 Token前缀: ${token.substring(0, min(20, token.length))}...');
|
||||
}
|
||||
print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'SplashController');
|
||||
KRLogUtil.kr_i('🎯 准备进入主页', tag: 'SplashController');
|
||||
KRLogUtil.kr_i('📊 最终登录状态: $loginStatus', tag: 'SplashController');
|
||||
KRLogUtil.kr_i('🎫 Token存在: $hasToken', tag: 'SplashController');
|
||||
KRLogUtil.kr_i('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', tag: 'SplashController');
|
||||
|
||||
// 直接导航到主页(无论是否登录,主页会根据登录状态显示不同内容)
|
||||
Get.offAllNamed(Routes.KR_MAIN);
|
||||
} catch (e) {
|
||||
// 后续步骤失败,显示错误信息
|
||||
|
||||
@@ -102,13 +102,15 @@ class KRStatisticsController extends GetxController {
|
||||
return;
|
||||
}
|
||||
|
||||
final either0 = await KRUserApi().kr_getUserInfo();
|
||||
either0.fold(
|
||||
(error) => KRCommonUtil.kr_showToast(error.msg),
|
||||
(userInfo) {
|
||||
// kr_homeController.kr_userId.value = userInfo.id.toString();
|
||||
},
|
||||
);
|
||||
// ⚠️ 已废弃:新版本后端不再提供 kr_getUserInfo 接口
|
||||
// 用户ID 现在从其他途径获取,此处注释掉
|
||||
// final either0 = await KRUserApi().kr_getUserInfo();
|
||||
// either0.fold(
|
||||
// (error) => KRCommonUtil.kr_showToast(error.msg),
|
||||
// (userInfo) {
|
||||
// // kr_homeController.kr_userId.value = userInfo.id.toString();
|
||||
// },
|
||||
// );
|
||||
|
||||
// 获取本周的开始和结束时间戳
|
||||
final DateTime now = DateTime.now();
|
||||
|
||||
@@ -199,15 +199,21 @@ class KRUserInfoController extends GetxController with KRAppBarOpacityMixin {
|
||||
if (!KRAppRunData.getInstance().kr_isLogin.value) {
|
||||
return;
|
||||
}
|
||||
final either0 = await KRUserApi().kr_getUserInfo();
|
||||
either0.fold(
|
||||
(error) {
|
||||
KRLogUtil.kr_e(error.msg, tag: 'AppRunData');
|
||||
},
|
||||
(userInfo) async {
|
||||
kr_balance.value = userInfo.balance.toDouble() / 100;
|
||||
},
|
||||
);
|
||||
|
||||
// ⚠️ 已废弃:新版本后端不再提供 kr_getUserInfo 接口
|
||||
// 余额现在使用 AppConfig 中的固定值,等待新接口实现
|
||||
// final either0 = await KRUserApi().kr_getUserInfo();
|
||||
// either0.fold(
|
||||
// (error) {
|
||||
// KRLogUtil.kr_e(error.msg, tag: 'AppRunData');
|
||||
// },
|
||||
// (userInfo) async {
|
||||
// kr_balance.value = userInfo.balance.toDouble() / 100;
|
||||
// },
|
||||
// );
|
||||
|
||||
// 使用 AppConfig 中的固定余额
|
||||
kr_balance.value = AppConfig.kr_userBalance.toDouble() / 100;
|
||||
}
|
||||
|
||||
/// 处理用户退出登录
|
||||
|
||||
@@ -99,64 +99,115 @@ class KRUserInfoView extends GetView<KRUserInfoController> {
|
||||
|
||||
// 构建绑定提示
|
||||
Widget _kr_buildBindingTip(BuildContext context) {
|
||||
return Obx(() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
return Obx(() {
|
||||
final appRunData = KRAppRunData.getInstance();
|
||||
final isLoggedIn = appRunData.kr_isLogin.value;
|
||||
final isDeviceLogin = appRunData.isDeviceLogin();
|
||||
|
||||
// 判断显示文字和颜色
|
||||
String displayText;
|
||||
Color displayColor;
|
||||
IconData displayIcon;
|
||||
bool shouldShowLoginPrompt = false;
|
||||
|
||||
if (!isLoggedIn) {
|
||||
// 未登录
|
||||
displayText = AppTranslations.kr_userInfo.bindingTip;
|
||||
displayColor = Theme.of(context).colorScheme.error;
|
||||
displayIcon = Icons.info_outline;
|
||||
shouldShowLoginPrompt = false;
|
||||
} else if (isDeviceLogin) {
|
||||
// 设备登录(游客模式)
|
||||
displayText = "登录/注册";
|
||||
displayColor = const Color(0xFF1797FF); // 使用蓝色提示可以点击
|
||||
displayIcon = Icons.touch_app;
|
||||
shouldShowLoginPrompt = true;
|
||||
} else {
|
||||
// 正常登录
|
||||
displayText = "${AppTranslations.kr_userInfo.myAccount} ${appRunData.kr_account.value}";
|
||||
displayColor = Theme.of(context).textTheme.bodyMedium?.color ?? Colors.black;
|
||||
displayIcon = Icons.info;
|
||||
shouldShowLoginPrompt = false;
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: shouldShowLoginPrompt
|
||||
? () {
|
||||
// 跳转到登录页面
|
||||
Get.toNamed(Routes.MR_LOGIN);
|
||||
}
|
||||
: null,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.w),
|
||||
decoration: shouldShowLoginPrompt
|
||||
? BoxDecoration(
|
||||
color: const Color(0xFF1797FF).withOpacity(0.05),
|
||||
borderRadius: BorderRadius.circular(8.w),
|
||||
)
|
||||
: null,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
KRAppRunData.getInstance().kr_isLogin.value
|
||||
? Icons.info
|
||||
: Icons.info_outline,
|
||||
color: !KRAppRunData.getInstance().kr_isLogin.value
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
displayIcon,
|
||||
color: displayColor,
|
||||
size: 16.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
displayText,
|
||||
style: KrAppTextStyle(
|
||||
color: displayColor,
|
||||
fontSize: 12,
|
||||
fontWeight: shouldShowLoginPrompt ? FontWeight.w600 : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (shouldShowLoginPrompt)
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14.w,
|
||||
color: displayColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 余额信息或游客ID
|
||||
Visibility(
|
||||
visible: KRAppRunData.getInstance().kr_isLogin.value &&
|
||||
AppConfig.getInstance().kr_is_daytime,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, bottom: 16.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
appRunData.isDeviceLogin()
|
||||
? Icons.person_outline
|
||||
: Icons.account_balance_wallet_outlined,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
size: 16.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
KRAppRunData.getInstance().kr_isLogin.value
|
||||
? "${AppTranslations.kr_userInfo.myAccount} ${KRAppRunData().kr_account}"
|
||||
: AppTranslations.kr_userInfo.bindingTip,
|
||||
appRunData.isDeviceLogin()
|
||||
? "游客ID:${(appRunData.kr_userId.value ?? 0) + 10000}"
|
||||
: "${AppTranslations.kr_userInfo.balance} ${controller.kr_balance.value.toString()}",
|
||||
style: KrAppTextStyle(
|
||||
color: !KRAppRunData.getInstance().kr_isLogin.value
|
||||
? Theme.of(context).colorScheme.error
|
||||
: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 余额信息(写死预览)
|
||||
Visibility(
|
||||
visible: KRAppRunData.getInstance().kr_isLogin.value &&
|
||||
AppConfig.getInstance().kr_is_daytime,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16.w, bottom: 16.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.account_balance_wallet_outlined,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
size: 16.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
"${AppTranslations.kr_userInfo.balance} ${controller.kr_balance.value.toString()}",
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
));
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 构建订阅卡片
|
||||
@@ -574,49 +625,67 @@ class KRUserInfoView extends GetView<KRUserInfoController> {
|
||||
|
||||
// 构建快捷键区域
|
||||
Widget _kr_buildShortcutSection(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.fromLTRB(16.w, 24.w, 16.w, 16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppTranslations.kr_userInfo.shortcuts,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
return Obx(() {
|
||||
final appRunData = KRAppRunData.getInstance();
|
||||
final isLoggedIn = appRunData.kr_isLogin.value;
|
||||
final isDeviceLogin = appRunData.isDeviceLogin();
|
||||
|
||||
// 只有正常登录用户(非游客)才显示设备管理
|
||||
final showDeviceManagement = isLoggedIn && !isDeviceLogin;
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.fromLTRB(16.w, 24.w, 16.w, 16.w),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
AppTranslations.kr_userInfo.shortcuts,
|
||||
style: KrAppTextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).textTheme.bodyMedium?.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.w),
|
||||
Column(
|
||||
children: [
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_ads",
|
||||
title: AppTranslations.kr_userInfo.adBlock,
|
||||
value: controller.kr_isAdBlockEnabled,
|
||||
onChanged: controller.kr_toggleAdBlock,
|
||||
context: context,
|
||||
),
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_dns",
|
||||
title: AppTranslations.kr_userInfo.ndsUnlock,
|
||||
value: controller.kr_isNDSUnlockEnabled,
|
||||
onChanged: controller.kr_toggleNDSUnlock,
|
||||
context: context,
|
||||
),
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_cn_us",
|
||||
title: AppTranslations.kr_userInfo.contactUs,
|
||||
onTap: () {
|
||||
Get.toNamed(Routes.KR_CRISP);
|
||||
},
|
||||
context: context,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
SizedBox(height: 12.w),
|
||||
Column(
|
||||
children: [
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_ads",
|
||||
title: AppTranslations.kr_userInfo.adBlock,
|
||||
value: controller.kr_isAdBlockEnabled,
|
||||
onChanged: controller.kr_toggleAdBlock,
|
||||
context: context,
|
||||
),
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_dns",
|
||||
title: AppTranslations.kr_userInfo.ndsUnlock,
|
||||
value: controller.kr_isNDSUnlockEnabled,
|
||||
onChanged: controller.kr_toggleNDSUnlock,
|
||||
context: context,
|
||||
),
|
||||
if (showDeviceManagement)
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_dns",
|
||||
title: "设备管理",
|
||||
onTap: () {
|
||||
Get.toNamed(Routes.KR_DEVICE_MANAGEMENT);
|
||||
},
|
||||
context: context,
|
||||
),
|
||||
_kr_buildShortcutContainer(
|
||||
icon: "my_cn_us",
|
||||
title: AppTranslations.kr_userInfo.contactUs,
|
||||
onTap: () {
|
||||
Get.toNamed(Routes.KR_CRISP);
|
||||
},
|
||||
context: context,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 构建快捷键容器
|
||||
|
||||
Reference in New Issue
Block a user