feat: 尚未购买的弹窗

This commit is contained in:
speakeloudest 2025-11-27 22:58:42 -08:00
parent adcde623c7
commit 55d7508807
7 changed files with 403 additions and 451 deletions

View File

@ -6,6 +6,7 @@ import 'package:get/get.dart';
import 'package:kaer_with_panels/app/modules/hi_menu/controllers/hi_menu_controller.dart'; import 'package:kaer_with_panels/app/modules/hi_menu/controllers/hi_menu_controller.dart';
import 'package:kaer_with_panels/app/routes/app_pages.dart'; import 'package:kaer_with_panels/app/routes/app_pages.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart'; import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:kaer_with_panels/app/widgets/kr_subscription_expiry_text.dart';
/// Widget /// Widget
/// ///
@ -67,51 +68,10 @@ class UserInfoCard extends StatelessWidget {
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
Obx(() { KRSubscriptionExpiryText(
final currentSubscribe = expireTimeProvider: () => controller.kr_subscribeService
controller.kr_subscribeService.kr_currentSubscribe.value; .kr_currentSubscribe.value?.expireTime,
String expiryText; ),
if (currentSubscribe == null) {
expiryText = '尚未购买套餐';
} else {
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime =
DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
expireDateTime = null;
}
if (expireDateTime == null) {
expiryText = '套餐信息无效';
} else if (expireDateTime.isBefore(now)) {
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
expiryText = '已于 $formattedExpireDate 到期';
} else {
final year = expireDateTime.year;
final month = expireDateTime.month.toString().padLeft(2, '0');
final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
// 2.
final formattedDateTime = '$year/$month/$day $hour:$minute:$second';
expiryText = '到期时间:$formattedDateTime';
}
}
return Text(
expiryText,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
);
}),
], ],
), ),
), ),

View File

@ -19,13 +19,14 @@ import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart'; import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart';
import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart'; import 'package:kaer_with_panels/app/widgets/dialogs/hi_dialog.dart';
import 'package:kaer_with_panels/app/common/app_config.dart'; import 'package:kaer_with_panels/app/common/app_config.dart';
import 'package:kaer_with_panels/app/widgets/kr_subscription_expiry_text.dart';
class HIUserInfoView extends GetView<HIUserInfoController> { class HIUserInfoView extends GetView<HIUserInfoController> {
const HIUserInfoView({super.key}); const HIUserInfoView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDeviceLogin = KRAppRunData.getInstance().isDeviceLogin(); final isDeviceLogin = KRAppRunData.getInstance().isDeviceLogin();
return HIBaseScaffold( return HIBaseScaffold(
child: Stack( child: Stack(
children: [ children: [
@ -69,9 +70,14 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Obx(() { Obx(() {
final account = KRAppRunData.getInstance().kr_account.value; final account = KRAppRunData.getInstance()
final isDeviceLogin = account != null && account.startsWith('9000'); .kr_account
final accountText = isDeviceLogin ? '待绑定' : 'ID: ${KRAppRunData.getInstance().kr_account.value.toString()}'; .value;
final isDeviceLogin = account != null &&
account.startsWith('9000');
final accountText = isDeviceLogin
? '待绑定'
: 'ID: ${KRAppRunData.getInstance().kr_account.value.toString()}';
return Text( return Text(
accountText, accountText,
style: TextStyle( style: TextStyle(
@ -84,51 +90,13 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
}), }),
Obx(() { KRSubscriptionExpiryText(
final currentSubscribe = expireTimeProvider: () => controller
controller.kr_subscribeService.kr_currentSubscribe.value; .kr_subscribeService
String expiryText; .kr_currentSubscribe
.value
if (currentSubscribe == null) { ?.expireTime,
expiryText = '尚未购买套餐'; ),
} else {
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime =
DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
expireDateTime = null;
}
if (expireDateTime == null) {
expiryText = '套餐信息无效';
} else if (expireDateTime.isBefore(now)) {
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
expiryText = '已于 $formattedExpireDate 到期';
} else {
final year = expireDateTime.year;
final month = expireDateTime.month.toString().padLeft(2, '0');
final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
// 2.
final formattedDateTime = '$year/$month/$day $hour:$minute:$second';
expiryText = '到期时间:$formattedDateTime';
}
}
return Text(
expiryText,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
);
}),
], ],
), ),
), ),
@ -138,7 +106,8 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
SizedBox(height: 12.w), SizedBox(height: 12.w),
// //
Obx(() { Obx(() {
final isDeviceLogin = KRAppRunData.getInstance().isDeviceLogin(); final isDeviceLogin =
KRAppRunData.getInstance().isDeviceLogin();
if (!isDeviceLogin) return SizedBox.shrink(); if (!isDeviceLogin) return SizedBox.shrink();
return InkWell( return InkWell(
onTap: () { onTap: () {
@ -155,14 +124,18 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
} }
}, },
child: Container( child: Container(
margin: EdgeInsets.symmetric(horizontal: 0).copyWith(bottom: 10), // margin: EdgeInsets.symmetric(horizontal: 0)
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10), .copyWith(bottom: 10), //
padding: EdgeInsets.symmetric(
horizontal: 16, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22), borderRadius: BorderRadius.circular(22),
border: Border.all(color: Colors.white, width: 2), border:
Border.all(color: Colors.white, width: 2),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
isDeviceLogin ? '绑定邮箱' : '修改密码', isDeviceLogin ? '绑定邮箱' : '修改密码',
@ -203,7 +176,8 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
// 2. GridView // 2. GridView
shrinkWrap: true, shrinkWrap: true,
// 3. // 3.
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // crossAxisCount: 2, //
crossAxisSpacing: 10, // crossAxisSpacing: 10, //
mainAxisSpacing: 10, // mainAxisSpacing: 10, //
@ -226,8 +200,10 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
child: Text( child: Text(
'请确认是否移除此设备?', '请确认是否移除此设备?',
style: KrAppTextStyle( style: KrAppTextStyle(
color: color: Theme.of(context)
Theme.of(context).textTheme.bodyMedium?.color, .textTheme
.bodyMedium
?.color,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@ -240,8 +216,7 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
}, },
onCancel: () { onCancel: () {
controller.deleteDevice(id); controller.deleteDevice(id);
} });
);
}, },
); );
} else { } else {
@ -261,9 +236,11 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
), ),
// 👇 3: Expanded // 👇 3: Expanded
Obx(() { Obx(() {
if((KRAppRunData.getInstance().kr_account.value != null && if ((KRAppRunData.getInstance().kr_account.value != null &&
KRAppRunData.getInstance().kr_account.value!.startsWith('9000'))) KRAppRunData.getInstance()
return SizedBox.shrink(); .kr_account
.value!
.startsWith('9000'))) return SizedBox.shrink();
return Padding( return Padding(
padding: EdgeInsets.symmetric(horizontal: 40.w), padding: EdgeInsets.symmetric(horizontal: 40.w),
child: Column( child: Column(
@ -278,7 +255,10 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
'注销账号后,所有此账号内的剩余套餐和账户数据将被清空,无法找回。', // 1. '注销账号后,所有此账号内的剩余套餐和账户数据将被清空,无法找回。', // 1.
textAlign: TextAlign.left, // textAlign: TextAlign.left, //
style: KrAppTextStyle( style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color, color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@ -292,8 +272,7 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
Get.toNamed(Routes.KR_DELETE_ACCOUNT); Get.toNamed(Routes.KR_DELETE_ACCOUNT);
}, },
// 4. onConfirm // 4. onConfirm
onConfirm: () { onConfirm: () {},
},
); );
}, },
child: Container( child: Container(
@ -301,7 +280,8 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
padding: EdgeInsets.symmetric(vertical: 12.w), padding: EdgeInsets.symmetric(vertical: 12.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.w), borderRadius: BorderRadius.circular(24.w),
border: Border.all(color: const Color(0xFFFF2ED1), width: 2), border: Border.all(
color: const Color(0xFFFF2ED1), width: 2),
), ),
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
@ -324,7 +304,10 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
'确认要退出您的账号?', // 1. '确认要退出您的账号?', // 1.
textAlign: TextAlign.center, // textAlign: TextAlign.center, //
style: KrAppTextStyle( style: KrAppTextStyle(
color: Theme.of(context).textTheme.bodyMedium?.color, color: Theme.of(context)
.textTheme
.bodyMedium
?.color,
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
@ -333,15 +316,14 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
cancelText: '确认', cancelText: '确认',
confirmText: '返回', confirmText: '返回',
onCancel: () async { onCancel: () async {
final currentDevice = controller.devices.firstWhere( final currentDevice =
(device) => device['is_current'] == true, controller.devices.firstWhere(
(device) => device['is_current'] == true,
); );
final deviceId = currentDevice['id'] as String; final deviceId = currentDevice['id'] as String;
await controller.deleteDevice(deviceId); await controller.deleteDevice(deviceId);
}, },
onConfirm: () { onConfirm: () {},
},
); );
}, },
child: Container( child: Container(
@ -376,6 +358,7 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
), ),
); );
} }
/// ///
Widget _buildDeviceCard({ Widget _buildDeviceCard({
required BuildContext context, required BuildContext context,
@ -396,7 +379,8 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
final deviceType = deviceInfo['type'] as String; final deviceType = deviceInfo['type'] as String;
final iconName = deviceInfo['icon'] as String; final iconName = deviceInfo['icon'] as String;
return Stack( // 使 Stack return Stack(
// 使 Stack
children: [ children: [
// //
Container( Container(
@ -486,31 +470,31 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
// //
final device_limit = AppConfig.getInstance().device_limit; final device_limit = AppConfig.getInstance().device_limit;
HIDialog.show( HIDialog.show(
customMessageWidget: Padding( customMessageWidget: Padding(
padding: EdgeInsets.only(top: 16.w, bottom: 16.w), padding: EdgeInsets.only(top: 16.w, bottom: 16.w),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, // Column mainAxisSize: MainAxisSize.min, // Column
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// 2. Text // 2. Text
Text( Text(
'请使用绑定邮箱登录新设备', '请使用绑定邮箱登录新设备',
style: KrAppTextStyle( style: KrAppTextStyle(
color: Colors.black, // 使 color: Colors.black, // 使
fontSize: 14.sp, fontSize: 14.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
),
), ),
SizedBox(height: 20.w), ),
Text( SizedBox(height: 20.w),
"每个账号最多允许同时使用${device_limit}台设备同时在线", Text(
style: KrAppTextStyle( "每个账号最多允许同时使用${device_limit}台设备同时在线",
color: Colors.black, style: KrAppTextStyle(
fontSize: 14.sp, color: Colors.black,
fontWeight: FontWeight.w600, fontSize: 14.sp,
), fontWeight: FontWeight.w600,
) ),
/*Obx(() { )
/*Obx(() {
// final current = controller.kr_subscribeService.kr_currentSubscribe.value; // final current = controller.kr_subscribeService.kr_currentSubscribe.value;
// current null 0 // current null 0
@ -518,9 +502,9 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
return return
}),*/ }),*/
], ],
),
), ),
),
confirmText: KRAppRunData.getInstance().isDeviceLogin() ? '前往' : null, confirmText: KRAppRunData.getInstance().isDeviceLogin() ? '前往' : null,
cancelText: '取消', cancelText: '取消',
onConfirm: () { onConfirm: () {
@ -591,7 +575,7 @@ class HIUserInfoView extends GetView<HIUserInfoController> {
final RegExp regExp = RegExp(r'\((.*?)\)'); final RegExp regExp = RegExp(r'\((.*?)\)');
final Match? match = regExp.firstMatch(deviceName); final Match? match = regExp.firstMatch(deviceName);
if (match != null && match.groupCount >= 1 ) { if (match != null && match.groupCount >= 1) {
// //
final inside = match.group(1)!; // "Android; google Pixel 9; 15" final inside = match.group(1)!; // "Android; google Pixel 9; 15"

View File

@ -4,11 +4,11 @@ import 'package:get/get.dart';
import 'package:kaer_with_panels/app/common/app_run_data.dart'; import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart'; import 'package:kaer_with_panels/app/widgets/hi_base_scaffold.dart';
import 'package:kaer_with_panels/app/widgets/kr_local_image.dart'; import 'package:kaer_with_panels/app/widgets/kr_local_image.dart';
import 'package:kaer_with_panels/app/widgets/kr_subscription_expiry_text.dart';
import '../controllers/kr_delete_account_controller.dart'; import '../controllers/kr_delete_account_controller.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart'; import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart'; import 'package:kaer_with_panels/app/widgets/kr_app_text_style.dart';
class KRDeleteAccountView extends GetView<KRDeleteAccountController> { class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
const KRDeleteAccountView({super.key}); const KRDeleteAccountView({super.key});
@ -46,7 +46,8 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
padding: EdgeInsets.symmetric(vertical: 12.w), padding: EdgeInsets.symmetric(vertical: 12.w),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.w), borderRadius: BorderRadius.circular(24.w),
border: Border.all(color: const Color(0xFFFF2ED1), width: 2), border:
Border.all(color: const Color(0xFFFF2ED1), width: 2),
), ),
alignment: Alignment.center, alignment: Alignment.center,
child: Text( child: Text(
@ -87,7 +88,6 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
); );
} }
Widget _buildUserInfoSection() { Widget _buildUserInfoSection() {
return Row( return Row(
children: [ children: [
@ -129,12 +129,16 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
); );
}), }),
Obx(() { Obx(() {
final _ = KRAppRunData.getInstance().kr_isLogin.value; final account = KRAppRunData.getInstance()
final userId = (KRAppRunData.getInstance().kr_userId.value ?? '').toString(); .kr_account
final deviceId = KRAppRunData.getInstance().deviceId ?? ''; .value;
final isDeviceLogin = account != null &&
account.startsWith('9000');
final accountText = isDeviceLogin
? '待绑定'
: 'ID: ${KRAppRunData.getInstance().kr_account.value.toString()}';
return Text( return Text(
'ID: ${userId.isNotEmpty ? userId : deviceId}', accountText,
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.85), color: Colors.white.withOpacity(0.85),
fontSize: 14.sp, fontSize: 14.sp,
@ -142,51 +146,14 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
), ),
); );
}), }),
Obx(() { KRSubscriptionExpiryText(
final currentSubscribe = expireTimeProvider: () => controller
controller.kr_subscribeService.kr_currentSubscribe.value; .kr_subscribeService.kr_currentSubscribe.value?.expireTime,
String expiryText; style: TextStyle(
color: Colors.white,
if (currentSubscribe == null) { fontSize: 12.sp,
expiryText = '尚未购买套餐'; ),
} else { ),
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime =
DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
expireDateTime = null;
}
if (expireDateTime == null) {
expiryText = '套餐信息无效';
} else if (expireDateTime.isBefore(now)) {
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
expiryText = '已于 $formattedExpireDate 到期';
} else {
expiryText = '到期时间:${expireDateTime}';final year = expireDateTime.year;
final month = expireDateTime.month.toString().padLeft(2, '0');
final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
// 2.
final formattedDateTime = '$year/$month/$day $hour:$minute:$second';
expiryText = '到期时间:$formattedDateTime';
}
}
return Text(
expiryText,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
);
}),
], ],
), ),
), ),
@ -213,7 +180,8 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
color: const Color(0xFFA6A6A6), color: const Color(0xFFA6A6A6),
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
contentPadding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w), contentPadding:
EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.w), borderRadius: BorderRadius.circular(25.w),
borderSide: BorderSide(color: Colors.white, width: 2), borderSide: BorderSide(color: Colors.white, width: 2),
@ -233,14 +201,15 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
suffixIcon: Padding( suffixIcon: Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 5.w), padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 5.w),
child: Obx(() { child: Obx(() {
return GestureDetector( return GestureDetector(
onTap: controller.kr_canSendCode.value ? controller.kr_sendCode : null, onTap: controller.kr_canSendCode.value
? controller.kr_sendCode
: null,
child: Container( child: Container(
width: 100.w, width: 100.w,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: controller.kr_canSendCode.value color: controller.kr_canSendCode.value
? Theme.of(Get.context!).primaryColor ? Theme.of(Get.context!).primaryColor
: const Color(0xFFD5D5D5), : const Color(0xFFD5D5D5),
borderRadius: BorderRadius.circular(100.r), // borderRadius: BorderRadius.circular(100.r), //
@ -254,7 +223,7 @@ class KRDeleteAccountView extends GetView<KRDeleteAccountController> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: controller.kr_canSendCode.value color: controller.kr_canSendCode.value
? Colors.black ? Colors.black
: const Color(0xFF464655) , : const Color(0xFF464655),
), ),
), ),
), ),

View File

@ -86,8 +86,17 @@ class HIAnimatedConnectButton extends GetView<KRHomeController> {
print('🔵 Switch UI 正在更新,切换中点击了按钮: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching'); print('🔵 Switch UI 正在更新,切换中点击了按钮: status=${status.runtimeType}, isConnected=$isConnected, isSwitching=$isSwitching');
return; return;
} }
final hasValidSubscription = final current = controller.kr_subscribeService.kr_currentSubscribe.value;
controller.kr_subscribeService.kr_availableSubscribes.isNotEmpty; bool hasValidSubscription = false;
if (current != null) {
DateTime? expire;
try {
expire = DateTime.parse(current.expireTime);
} catch (_) {
expire = null;
}
hasValidSubscription = expire != null && expire.isAfter(DateTime.now());
}
if (hasValidSubscription) { if (hasValidSubscription) {
controller.kr_toggleSwitch(!controller.kr_isConnected.value); controller.kr_toggleSwitch(!controller.kr_isConnected.value);
} else { } else {
@ -291,4 +300,3 @@ class _ContinuousRippleEffectState extends State<ContinuousRippleEffect>
); );
} }
} }

View File

@ -18,7 +18,6 @@ import 'kr_home_subscription_view.dart';
import './hi_animated_connect_button.dart'; import './hi_animated_connect_button.dart';
import 'package:kaer_with_panels/app/services/global_overlay_service.dart'; import 'package:kaer_with_panels/app/services/global_overlay_service.dart';
class KRHomeView extends StatefulWidget { class KRHomeView extends StatefulWidget {
const KRHomeView({super.key}); const KRHomeView({super.key});
@ -35,8 +34,6 @@ class _KRHomeViewState extends State<KRHomeView> {
controller = Get.find<KRHomeController>(); controller = Get.find<KRHomeController>();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -50,174 +47,186 @@ class _KRHomeViewState extends State<KRHomeView> {
children: [ children: [
HIBaseScaffold( HIBaseScaffold(
showMenuButton: true, showMenuButton: true,
topContentAreaHeight: 80, topContentAreaHeight: 80,
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: child: SafeArea(
SafeArea( child: Padding(
child: Padding( padding: EdgeInsets.fromLTRB(40.w, 8.w, 0, 0),
padding: EdgeInsets.fromLTRB(40.w, 8.w, 0, 0), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ Text(
Text( 'Hi快VPN-网在我在,网快我快',
'Hi快VPN-网在我在,网快我快', style: TextStyle(
style: TextStyle( color: Colors.white,
color: Colors.white, fontSize: 14.sp,
fontSize: 14.sp, fontWeight: FontWeight.w600,
fontWeight: FontWeight.w600,
),
),
//
Obx(() {
// 1.
final currentSubscribe =
controller.kr_subscribeService.kr_currentSubscribe.value;
// 2. Widget
Widget content;
// --- ---
final textStyle = TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
height: 1.2, //
);
// 3.
if (currentSubscribe == null) {
// --- 1: ---
content = Text(
'尚未购买套餐',
style: textStyle,
);
} else {
// --- 2: ---
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime = DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
//
}
if (expireDateTime != null && expireDateTime.isBefore(now)) {
// --- 2.1: ---
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
// 使 \n Text
content = Text(
'您的套餐已于 $formattedExpireDate 到期\n请前往购买新套餐',
style: textStyle,
);
} else {
// --- 2.2: ---
final difference = expireDateTime?.difference(now);
final remainingDaysText = (difference?.inDays ?? 0) > 0
? '${difference!.inDays}'
: '不足一天';
// 使 \n Text
content = Text(
'套餐剩余:$remainingDaysText\n${controller.kr_isConnected.value ? '当前线路:${controller.kr_getRealConnectedNodeCountry()}' : '未连接'}',
style: textStyle,
);
}
}
// 4. Container
return Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 8.h),
child: content,
);
}),
SizedBox(height: 8.h), //
// --- Checkbox ---
Obx(() {
return Row(
mainAxisSize: MainAxisSize.min, // Row
children: [
// --- Checkbox ---
GestureDetector(
//
onTap: () => controller.toggleQuickConnect(!controller.isQuickConnectEnabled.value),
child: AbsorbPointer(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// 1. Checkbox
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
color: controller.isQuickConnectEnabled.value
? Theme.of(context).primaryColor
: Colors.transparent,
border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
),
borderRadius: BorderRadius.circular(4.r),
),
child: controller.isQuickConnectEnabled.value
? Icon(
Icons.check,
size: 14.w,
color: Colors.black,
)
: null,
),
SizedBox(width: 8.w),
// 2.
Text(
'闪连',
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w600,
),
),
],
),
),
), ),
// --- --- ),
SizedBox(width: 6.w), // //
GestureDetector( Obx(() {
onTap: () { // 1.
HIDialog.show( final currentSubscribe = controller
title: '*闪连功能', .kr_subscribeService.kr_currentSubscribe.value;
message: '开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
// 2. Widget
Widget content;
// --- ---
final normalStyle = TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w600,
height: 1.2,
);
final highlightStyle = normalStyle.copyWith(
color: const Color(0xFFFF00B7),
);
// 3.
if (currentSubscribe == null) {
// --- 1: ---
content = Text(
'尚未购买套餐',
style: highlightStyle,
);
} else {
// --- 2: ---
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime =
DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
//
}
if (expireDateTime != null &&
expireDateTime.isBefore(now)) {
// --- 2.1: ---
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
// 使 \n Text
content = Text(
'您的套餐已于 $formattedExpireDate 到期\n请前往购买新套餐',
style: highlightStyle,
); );
}, } else {
child: KrLocalImage( // --- 2.2: ---
imageName: 'question-icon', // final difference =
imageType: ImageType.svg, // expireDateTime?.difference(now);
width: 24.w, final remainingDaysText =
height: 24.w, (difference?.inDays ?? 0) > 0
color: Colors.white, // ? '${difference!.inDays}'
), : '不足一天';
), // 使 \n Text
], content = Text(
); '套餐剩余:$remainingDaysText\n${controller.kr_isConnected.value ? '当前线路:${controller.kr_getRealConnectedNodeCountry()}' : '未连接'}',
}), style: normalStyle,
], );
}
}
// 4. Container
return Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.only(top: 8.h),
child: content,
);
}),
SizedBox(height: 8.h), //
// --- Checkbox ---
Obx(() {
return Row(
mainAxisSize: MainAxisSize.min, // Row
children: [
// --- Checkbox ---
GestureDetector(
//
onTap: () => controller.toggleQuickConnect(
!controller.isQuickConnectEnabled.value),
child: AbsorbPointer(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// 1. Checkbox
Container(
width: 20.w,
height: 20.w,
decoration: BoxDecoration(
color: controller
.isQuickConnectEnabled.value
? Theme.of(context).primaryColor
: Colors.transparent,
border: Border.all(
color:
Theme.of(context).primaryColor,
width: 1.5,
),
borderRadius:
BorderRadius.circular(4.r),
),
child: controller
.isQuickConnectEnabled.value
? Icon(
Icons.check,
size: 14.w,
color: Colors.black,
)
: null,
),
SizedBox(width: 8.w),
// 2.
Text(
'闪连',
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
// --- ---
SizedBox(width: 6.w), //
GestureDetector(
onTap: () {
HIDialog.show(
title: '*闪连功能',
message:
'开启后,每次打开软件默认自动连接,无需点击连接按钮\n在后台关闭软件后,软件将自动断开',
);
},
child: KrLocalImage(
imageName: 'question-icon', //
imageType: ImageType.svg, //
width: 24.w,
height: 24.w,
color: Colors.white, //
),
),
],
);
}),
],
),
),
), ),
), ),
), ],
), ),
], ),
), // HIAnimatedConnectButton HIBaseScaffold SafeArea
), HIAnimatedConnectButton(),
// HIAnimatedConnectButton HIBaseScaffold SafeArea ],
HIAnimatedConnectButton(), );
],
);
} }
} }

View File

@ -12,6 +12,7 @@ import 'package:kaer_with_panels/app/common/app_run_data.dart';
import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart'; import 'package:kaer_with_panels/app/modules/kr_home/controllers/kr_home_controller.dart';
import 'package:kaer_with_panels/app/services/kr_site_config_service.dart'; import 'package:kaer_with_panels/app/services/kr_site_config_service.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:kaer_with_panels/app/widgets/kr_subscription_expiry_text.dart';
class KRLoginView extends GetView<KRLoginController> { class KRLoginView extends GetView<KRLoginController> {
const KRLoginView({super.key}); const KRLoginView({super.key});
@ -19,7 +20,8 @@ class KRLoginView extends GetView<KRLoginController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0; final isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 0;
final isHideBack = (Get.arguments as Map<String, dynamic>?)?['is-back'] ?? false; final isHideBack =
(Get.arguments as Map<String, dynamic>?)?['is-back'] ?? false;
return HIBaseScaffold( return HIBaseScaffold(
showBack: !isHideBack, showBack: !isHideBack,
resizeToAvoidBottomInset: true, resizeToAvoidBottomInset: true,
@ -27,7 +29,9 @@ class KRLoginView extends GetView<KRLoginController> {
children: [ children: [
SingleChildScrollView( SingleChildScrollView(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40.w,), padding: EdgeInsets.symmetric(
horizontal: 40.w,
),
child: Column( child: Column(
children: [ children: [
// Text( // Text(
@ -43,8 +47,7 @@ class KRLoginView extends GetView<KRLoginController> {
), ),
), ),
), ),
if (!isKeyboardVisible) if (!isKeyboardVisible) const HIHelpEntrance(),
const HIHelpEntrance(),
], ],
), ),
); );
@ -76,30 +79,17 @@ class KRLoginView extends GetView<KRLoginController> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Obx(() { Obx(() {
final account = KRAppRunData.getInstance().kr_account.value; final account = KRAppRunData.getInstance()
final isDeviceLogin = account != null && account.startsWith('9000'); .kr_account
.value;
if (isDeviceLogin) return const SizedBox(); final isDeviceLogin = account != null &&
account.startsWith('9000');
final accountText = isDeviceLogin
? '待绑定'
: 'ID: ${KRAppRunData.getInstance().kr_account.value.toString()}';
return Text( return Text(
account ?? '', accountText,
style: TextStyle(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
height: 0.9,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
);
}),
Obx(() {
final _ = KRAppRunData.getInstance().kr_isLogin.value;
final userId = (KRAppRunData.getInstance().kr_userId.value ?? '').toString();
final deviceId = KRAppRunData.getInstance().deviceId ?? '';
return Text(
'ID: ${userId.isNotEmpty ? userId : deviceId}',
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.85), color: Colors.white.withOpacity(0.85),
fontSize: 14.sp, fontSize: 14.sp,
@ -107,51 +97,14 @@ class KRLoginView extends GetView<KRLoginController> {
), ),
); );
}), }),
Obx(() { KRSubscriptionExpiryText(
final currentSubscribe = expireTimeProvider: () => controller
controller.kr_subscribeService.kr_currentSubscribe.value; .kr_subscribeService.kr_currentSubscribe.value?.expireTime,
String expiryText; style: TextStyle(
color: Colors.white,
if (currentSubscribe == null) { fontSize: 12.sp,
expiryText = '尚未购买套餐'; ),
} else { ),
final now = DateTime.now();
DateTime? expireDateTime;
try {
expireDateTime =
DateTime.parse(currentSubscribe.expireTime);
} catch (e) {
expireDateTime = null;
}
if (expireDateTime == null) {
expiryText = '套餐信息无效';
} else if (expireDateTime.isBefore(now)) {
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
expiryText = '已于 $formattedExpireDate 到期';
} else {
expiryText = '到期时间:${expireDateTime}';final year = expireDateTime.year;
final month = expireDateTime.month.toString().padLeft(2, '0');
final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
// 2.
final formattedDateTime = '$year/$month/$day $hour:$minute:$second';
expiryText = '到期时间:$formattedDateTime';
}
}
return Text(
expiryText,
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
);
}),
], ],
), ),
), ),
@ -283,7 +236,8 @@ class KRLoginView extends GetView<KRLoginController> {
color: const Color(0xFFA6A6A6), color: const Color(0xFFA6A6A6),
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
contentPadding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w), contentPadding:
EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.w), // borderRadius: BorderRadius.circular(25.w), //
borderSide: BorderSide(color: Colors.white, width: 2), borderSide: BorderSide(color: Colors.white, width: 2),
@ -327,7 +281,8 @@ class KRLoginView extends GetView<KRLoginController> {
color: const Color(0xFFA6A6A6), color: const Color(0xFFA6A6A6),
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
contentPadding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w), contentPadding:
EdgeInsets.symmetric(horizontal: 16.w, vertical: 10.w),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular(25.w), borderRadius: BorderRadius.circular(25.w),
borderSide: BorderSide(color: Colors.white, width: 2), borderSide: BorderSide(color: Colors.white, width: 2),
@ -347,14 +302,15 @@ class KRLoginView extends GetView<KRLoginController> {
suffixIcon: Padding( suffixIcon: Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 5.w), padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 5.w),
child: Obx(() { child: Obx(() {
return GestureDetector( return GestureDetector(
onTap: controller.kr_canSendCode.value ? () => controller.kr_sendCode() : null, onTap: controller.kr_canSendCode.value
? () => controller.kr_sendCode()
: null,
child: Container( child: Container(
width: 100.w, width: 100.w,
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: controller.kr_canSendCode.value color: controller.kr_canSendCode.value
? Theme.of(Get.context!).primaryColor ? Theme.of(Get.context!).primaryColor
: const Color(0xFFD5D5D5), : const Color(0xFFD5D5D5),
borderRadius: BorderRadius.circular(100.r), // borderRadius: BorderRadius.circular(100.r), //
@ -366,7 +322,7 @@ class KRLoginView extends GetView<KRLoginController> {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: controller.kr_canSendCode.value color: controller.kr_canSendCode.value
? Colors.black ? Colors.black
: const Color(0xFF464655) , : const Color(0xFF464655),
), ),
), ),
), ),
@ -378,7 +334,6 @@ class KRLoginView extends GetView<KRLoginController> {
); );
} }
Widget _buildSaveButton() { Widget _buildSaveButton() {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class KRSubscriptionExpiryText extends StatelessWidget {
final String? Function() expireTimeProvider;
final TextStyle? style;
const KRSubscriptionExpiryText({
Key? key,
required this.expireTimeProvider,
this.style,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
final expireTimeStr = expireTimeProvider();
String expiryText;
bool highlight = false;
if (expireTimeStr == null || expireTimeStr.isEmpty) {
expiryText = '尚未购买套餐';
highlight = true;
} else {
DateTime? expireDateTime;
try {
expireDateTime = DateTime.parse(expireTimeStr);
} catch (_) {
expireDateTime = null;
}
if (expireDateTime == null) {
expiryText = '套餐信息无效';
} else if (expireDateTime.isBefore(DateTime.now())) {
final formattedExpireDate =
'${expireDateTime.year}/${expireDateTime.month.toString().padLeft(2, '0')}/${expireDateTime.day.toString().padLeft(2, '0')}';
expiryText = '已于 $formattedExpireDate 到期';
highlight = true;
} else {
final year = expireDateTime.year;
final month = expireDateTime.month.toString().padLeft(2, '0');
final day = expireDateTime.day.toString().padLeft(2, '0');
final hour = expireDateTime.hour.toString().padLeft(2, '0');
final minute = expireDateTime.minute.toString().padLeft(2, '0');
final second = expireDateTime.second.toString().padLeft(2, '0');
final formattedDateTime = '$year/$month/$day $hour:$minute:$second';
expiryText = '到期时间:$formattedDateTime';
}
}
final baseStyle = style ??
const TextStyle(
color: Colors.white,
fontSize: 12,
);
final appliedStyle = highlight
? baseStyle.copyWith(color: const Color(0xFFFF00B7))
: baseStyle;
return Text(
expiryText,
style: appliedStyle,
);
});
}
}