更新验证码选择区号

This commit is contained in:
wyf
2025-12-10 15:22:17 +08:00
parent 793dddfba8
commit fb5a17830b
19 changed files with 671 additions and 438 deletions

View File

@@ -614,5 +614,14 @@
"详情": "Detail", "详情": "Detail",
"更新成功": "Update Success", "更新成功": "Update Success",
"原邮箱号": "Original Email", "原邮箱号": "Original Email",
"请输入邮箱号":"Please enter email" "请输入邮箱号": "Please enter email",
"中国": "China",
"香港": "Hong Kong",
"选择区号": "Select Country Code",
"输入邮箱号码": " Enter Email",
"通知设置": "Notification Settings",
"注意!关闭后将无法接受任何消息": "Note! After closing, you will not receive any messages",
"手机号登录": "Phone Login",
"邮箱登录": "Email Login",
"输入邮箱": "Enter Email"
} }

View File

@@ -615,5 +615,16 @@
"原邮箱号": "原邮箱号", "原邮箱号": "原邮箱号",
"用户拒绝授权": "用户拒绝授权", "用户拒绝授权": "用户拒绝授权",
"用户取消授权": "用户取消授权", "用户取消授权": "用户取消授权",
"请输入邮箱号":"请输入邮箱号" "请输入邮箱号":"请输入邮箱号",
"中国":"中国",
"香港":"香港",
"选择区号":"选择区号",
"输入邮箱号码":"输入邮箱号码",
"通知设置":"通知设置",
"注意!关闭后将无法接受任何消息":"注意!关闭后将无法接受任何消息",
"手机号登录":"手机号登录",
"邮箱登录":"邮箱登录",
"输入邮箱":"输入邮箱"
} }

View File

@@ -612,5 +612,14 @@
"原邮箱号": "原郵箱號", "原邮箱号": "原郵箱號",
"用户拒绝授权": "用戶拒絕授權", "用户拒绝授权": "用戶拒絕授權",
"用户取消授权": "用戶取消授權", "用户取消授权": "用戶取消授權",
"请输入邮箱号":"請輸入郵箱號" "请输入邮箱号": "請輸入郵箱號",
"中国": "中國",
"香港": "香港",
"选择区号": "选择區號",
"输入邮箱号码": "輸入郵箱號碼",
"通知设置": "通知設置",
"注意!关闭后将无法接受任何消息": "注意!關閉將無法接受任何消息",
"手机号登录": "手機登入",
"邮箱登录": "電子郵件登入",
"输入邮箱": "輸入電子郵件"
} }

View File

@@ -226,6 +226,11 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
if (res.code == HttpStatusCodes.ok) { if (res.code == HttpStatusCodes.ok) {
// bindDeviceNum.value = res.total!; // bindDeviceNum.value = res.total!;
deviceList.value = res.data!; deviceList.value = res.data!;
deviceList.value.sort((a, b) {
final int at = a['create_time'] ?? 0;
final int bt = b['create_time'] ?? 0;
return bt.compareTo(at);
});
updateAll(); updateAll();
return res; return res;
} }

View File

@@ -164,8 +164,10 @@ class LoginController extends GetControllerEx<LoginModel> {
queryUrl += "?lang=$language"; queryUrl += "?lang=$language";
} }
} }
UserInfoController userInfoController = Get.find();
var data = { var data = {
"userName": model.phone, "userName": model.phone,
"ccode":userInfoController.select_country_code.value,
}; };
if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) { if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) {
data['code'] = "hzhskj"; data['code'] = "hzhskj";
@@ -232,9 +234,11 @@ class LoginController extends GetControllerEx<LoginModel> {
queryUrl += "?lang=$language"; queryUrl += "?lang=$language";
} }
} }
UserInfoController userInfoController = Get.find();
var data = { var data = {
"userName": model.updatePhone, "userName": model.updatePhone,
"type": 5, "type": 5,
"ccode":userInfoController.select_country_code.value,
}; };
if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) { if (AppConstants().ent_type == APPPackageType.HUANSHUI.code) {
data['code'] = "hzhskj"; data['code'] = "hzhskj";

View File

@@ -74,6 +74,20 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
FluwxCancelable? fluwxCancelable; FluwxCancelable? fluwxCancelable;
final Fluwx fluwx = Fluwx(); final Fluwx fluwx = Fluwx();
List<Map<String, String>> country_code = [
{
"name": "中国",
"id": "+86",
},
{
"name": "香港",
"id": "+852",
},
];
RxString select_country_code = "+86".obs;
RxInt select_login_method = 1.obs;//1手机 2邮箱
Future<ApiResponse> uploadImg() async { Future<ApiResponse> uploadImg() async {
EasyDartModule.logger.info("请求上传图片"); EasyDartModule.logger.info("请求上传图片");
DailyLogUtils.writeLog("请求上传图片"); DailyLogUtils.writeLog("请求上传图片");

View File

@@ -207,6 +207,15 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
_pageController.animateToPage(index, _pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut); duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
BodyDeviceController deviceController = Get.find(); BodyDeviceController deviceController = Get.find();
if (index == 0) {
deviceController.model.type = 1;
homeController.model.type = 1;
} else if (index == 1) {
deviceController.model.type = 2;
homeController.model.type = 2;
}
deviceController.updateAll();
await deviceController.getDeviceList(); await deviceController.getDeviceList();
await deviceController.getSleepReport(); await deviceController.getSleepReport();
} }

View File

@@ -25,6 +25,7 @@ import 'package:vbvs_app/enum/BindType.dart';
import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
class DeviceDataComponentWidget extends StatefulWidget { class DeviceDataComponentWidget extends StatefulWidget {
final Map<String, dynamic> device; final Map<String, dynamic> device;

View File

@@ -21,6 +21,8 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/controller/weather/weather_controller.dart'; import 'package:vbvs_app/controller/weather/weather_controller.dart';
import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/person/select_time.dart';
import 'dart:ui' as ui;
class OtherLoginPage extends StatefulWidget { class OtherLoginPage extends StatefulWidget {
const OtherLoginPage({super.key}); const OtherLoginPage({super.key});
@@ -122,7 +124,6 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
} }
} }
}); });
} }
@override @override
@@ -255,6 +256,22 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
child: Padding( child: Padding(
padding: EdgeInsetsDirectional.fromSTEB( padding: EdgeInsetsDirectional.fromSTEB(
0, 95.rpx, 0, 0), 0, 95.rpx, 0, 0),
child: Container(
width: double.infinity,
height: bodysize.maxHeight * 0.056,
constraints: BoxConstraints(
minHeight: 90.rpx,
),
// ✅ 在这里塞进去
child: _buildLoginMethodSelector(bodysize),
),
),
),
Align(
alignment: AlignmentDirectional(-1, 0),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, 26.rpx, 0, 0),
child: Container( child: Container(
width: double.infinity, width: double.infinity,
height: bodysize.maxHeight * 0.056, height: bodysize.maxHeight * 0.056,
@@ -279,31 +296,55 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceBetween, MainAxisAlignment.spaceBetween,
children: [ children: [
Padding( Obx(() {
padding: EdgeInsetsDirectional.fromSTEB( if (userInfoController
.select_login_method.value ==
2) {
return Container();
}
return Padding(
padding:
EdgeInsetsDirectional.fromSTEB(
26.rpx, 0, 0, 0), 26.rpx, 0, 0, 0),
child: Row( child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
InkWell( InkWell(
onTap: () async {}, onTap: () async {
child: Container( await showCountryCodePickerDialog(
context,
initialCode: userInfoController
.select_country_code
.value, // ✅ 来自 controller
onConfirm: (String code) {
print("选中的区号:$code");
userInfoController
.select_country_code
.value = code;
userInfoController
.updateAll();
// setState / controller.update
},
title: "选择区号".tr,
);
},
child: Obx(() {
return Container(
width: 80.rpx,
alignment: Alignment.center, alignment: Alignment.center,
// constraints: BoxConstraints(
// minWidth: 150.rpx,
// ),
child: Text( child: Text(
"+86", "${userInfoController.select_country_code.value}",
style: TextStyle( style: TextStyle(
fontFamily: 'Readex Pro', fontFamily:
'Readex Pro',
color: themeController color: themeController
.currentColor.sc4, .currentColor.sc2,
fontSize: AppConstants() fontSize: AppConstants()
.middler_text_fontSize, .middler_text_fontSize,
letterSpacing: 0,
),
), ),
), ),
);
}),
), ),
SizedBox( SizedBox(
height: 30.rpx, height: 30.rpx,
@@ -315,8 +356,10 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
), ),
].divide(SizedBox(width: 10.rpx)), ].divide(SizedBox(width: 10.rpx)),
), ),
), );
Expanded( }),
Obx(() {
return Expanded(
child: Container( child: Container(
child: Align( child: Align(
alignment: alignment:
@@ -335,7 +378,12 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
fontSize: 26.rpx, fontSize: 26.rpx,
letterSpacing: 0.0, letterSpacing: 0.0,
), ),
hintText: '其他手机登录页.输入内容'.tr, hintText: userInfoController
.select_login_method
.value ==
2
? '输入邮箱'.tr
: "输入手机号码".tr,
hintStyle: TextStyle( hintStyle: TextStyle(
fontFamily: 'Inter', fontFamily: 'Inter',
fontSize: 26.rpx, fontSize: 26.rpx,
@@ -363,7 +411,8 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
BorderRadius.circular( BorderRadius.circular(
8.rpx), 8.rpx),
), ),
errorBorder: OutlineInputBorder( errorBorder:
OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: Colors.red, color: Colors.red,
width: 1.rpx, width: 1.rpx,
@@ -400,7 +449,8 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
), ),
), ),
), ),
), );
}),
], ],
), ),
), ),
@@ -629,7 +679,6 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
), ),
), ),
), ),
Padding( Padding(
padding: padding:
EdgeInsetsDirectional.fromSTEB(0, 26.rpx, 0, 0), EdgeInsetsDirectional.fromSTEB(0, 26.rpx, 0, 0),
@@ -1071,4 +1120,179 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
)), )),
); );
} }
Widget _buildLoginMethodSelector(BoxConstraints bodysize) {
final userInfoController = Get.find<UserInfoController>();
// 文本样式
final textStyle = TextStyle(
fontSize: 28.rpx,
fontWeight: FontWeight.w600,
);
// 两个 tab 之间的间距
final double gap = 60.rpx;
// 左侧内边距
final double leftPadding = 40.rpx;
// 下划线和文字的垂直间距
final double underlineGap = 20.rpx;
// 下划线高度
final double underlineHeight = 6.rpx;
return Align(
alignment: AlignmentDirectional(-1, 0),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0),
child: Container(
width: double.infinity,
height: bodysize.maxHeight * 0.056,
constraints: const BoxConstraints(minHeight: 90),
child: Obx(() {
final selectedIndex =
userInfoController.select_login_method.value; // 1 or 2
// --- 计算文字宽高 ---
final tp1 = TextPainter(
text: TextSpan(text: '手机号登录'.tr, style: textStyle),
textDirection: ui.TextDirection.ltr,
)..layout();
final tp2 = TextPainter(
text: TextSpan(text: '邮箱登录'.tr, style: textStyle),
textDirection: ui.TextDirection.ltr,
)..layout();
final double w1 = tp1.width;
final double w2 = tp2.width;
final double textHeight = tp1.height;
// --- 每个 tab 的起始 X ---
final double startX1 = leftPadding;
final double startX2 = leftPadding + w1 + gap;
// --- 下划线宽度(和文字等宽)---
final double underlineWidth1 = w1;
final double underlineWidth2 = w2;
// --- 下划线 left始终居中文字---
final double underlineLeft1 = startX1;
final double underlineLeft2 = startX2;
final double underlineLeft =
selectedIndex == 1 ? underlineLeft1 : underlineLeft2;
final double underlineWidth =
selectedIndex == 1 ? underlineWidth1 : underlineWidth2;
return Stack(
clipBehavior: Clip.none,
children: [
// ===== 文字行 =====
Positioned(
left: 0,
top: 0,
child: Row(
children: [
SizedBox(width: leftPadding),
// 手机号登录
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () =>
userInfoController.select_login_method.value = 1,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 6.rpx),
child: Obx(() {
final selected =
userInfoController.select_login_method.value ==
1;
return Text(
'手机号登录'.tr,
style: textStyle.copyWith(
color: selected
? themeController.currentColor.sc2
: themeController.currentColor.sc4,
),
);
}),
),
),
SizedBox(width: gap),
// 邮箱登录
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () =>
userInfoController.select_login_method.value = 2,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 6.rpx),
child: Obx(() {
final selected =
userInfoController.select_login_method.value ==
2;
return Text(
'邮箱登录'.tr,
style: textStyle.copyWith(
color: selected
? themeController.currentColor.sc2
: themeController.currentColor.sc4,
),
);
}),
),
),
],
),
),
// ===== 下划线(文字正下方) =====
AnimatedPositioned(
duration: const Duration(milliseconds: 280),
curve: Curves.easeOutCubic,
left: underlineLeft,
top: textHeight + underlineGap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 280),
curve: Curves.easeOutCubic,
width: underlineWidth,
height: underlineHeight,
decoration: BoxDecoration(
color: themeController.currentColor.sc2,
borderRadius: BorderRadius.circular(underlineHeight / 2),
),
),
),
],
);
}),
),
),
);
}
Widget _buildLoginTab({
required String text,
required int index,
required UserInfoController controller,
}) {
return GestureDetector(
onTap: () {
controller.select_login_method.value = index;
},
child: Obx(() {
final selected = controller.select_login_method.value == index;
return Text(
text,
style: TextStyle(
fontSize: 28.rpx,
fontWeight: FontWeight.w600,
color: selected
? themeController.currentColor.sc2
: themeController.currentColor.sc4,
),
);
}),
);
}
} }

View File

@@ -15,13 +15,14 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/mh_page/user/controller/bind_tel_controller.dart'; import 'package:vbvs_app/pages/mh_page/user/controller/bind_tel_controller.dart';
import 'package:vbvs_app/pages/mh_page/user/controller/mht_login_controller.dart'; import 'package:vbvs_app/pages/person/select_time.dart';
class THBindTelWidget extends GetView<AuthBindTelController> { class THBindTelWidget extends GetView<AuthBindTelController> {
BoxConstraints? bodysize; BoxConstraints? bodysize;
Map img; Map img;
final scaffoldKey = GlobalKey<ScaffoldState>(); final scaffoldKey = GlobalKey<ScaffoldState>();
UserInfoController userInfoController = Get.find();
THBindTelWidget({super.key, required this.img}); THBindTelWidget({super.key, required this.img});
@@ -184,24 +185,40 @@ class THBindTelWidget extends GetView<AuthBindTelController> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
InkWell( InkWell(
onTap: () async {}, onTap: () async {
child: Container( await showCountryCodePickerDialog(
context,
initialCode: userInfoController
.select_country_code
.value, // ✅ 来自 controller
onConfirm: (String code) {
print("选中的区号:$code");
userInfoController
.select_country_code
.value = code;
userInfoController
.updateAll();
// setState / controller.update
},
title: "选择区号".tr,
);
},
child: Obx(() {
return Container(
width: 80.rpx,
alignment: Alignment.center, alignment: Alignment.center,
// constraints: BoxConstraints(
// minWidth: 150.rpx,
// ),
child: Text( child: Text(
"+86", "${userInfoController.select_country_code.value}",
style: TextStyle( style: TextStyle(
fontFamily: 'Readex Pro', fontFamily: 'Readex Pro',
color: themeController color: themeController
.currentColor.sc4, .currentColor.sc2,
fontSize: AppConstants() fontSize: AppConstants()
.middler_text_fontSize, .middler_text_fontSize,
letterSpacing: 0,
),
), ),
), ),
);
}),
), ),
SizedBox( SizedBox(
height: 30.rpx, height: 30.rpx,

View File

@@ -10,6 +10,7 @@ import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart'; import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/model/user_data.dart'; import 'package:vbvs_app/model/user_data.dart';
@@ -112,9 +113,11 @@ class AuthBindTelController extends GetControllerEx<AuthBindTelModel> {
String serviceName = ServiceConstant.server_service; String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.send_code; String serviceApi = ServiceConstant.send_code;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
UserInfoController userInfoController = Get.find();
var data = { var data = {
"userName": model.phone!, "userName": model.phone!,
'type': 5, 'type': 5,
"ccode":userInfoController.select_country_code.value,
}; };
if (img != null) { if (img != null) {
if (img!['code'] != null) { if (img!['code'] != null) {

View File

@@ -143,8 +143,8 @@ class BindTelWidget extends GetView<AuthBindTelController> {
), ),
), ),
Padding( Padding(
padding: padding: EdgeInsetsDirectional.fromSTEB(
EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0), 0, 16.rpx, 0, 0),
child: Container( child: Container(
width: bodysize!.maxWidth, width: bodysize!.maxWidth,
height: bodysize!.maxHeight * 0.06, height: bodysize!.maxHeight * 0.06,
@@ -469,8 +469,10 @@ class BindTelWidget extends GetView<AuthBindTelController> {
text: message); text: message);
return; return;
} }
if (authBindTelController.model.code == null || if (authBindTelController.model.code ==
authBindTelController.model.code!.isEmpty) { null ||
authBindTelController
.model.code!.isEmpty) {
message = "请输入验证码".tr; message = "请输入验证码".tr;
TopSlideNotification.show(context, TopSlideNotification.show(context,
text: message); text: message);

View File

@@ -11,6 +11,7 @@ import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/language/AppLanguage.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/common/selectDialog.dart';
@@ -1154,3 +1155,154 @@ Future<void> showIntervalPickerDialog(
}, },
); );
} }
Future<void> showCountryCodePickerDialog(
BuildContext context, {
required String initialCode, // 初始区号,如 "+86"
required Function(String selectedCode) onConfirm,
String title = "选择区号",
}) async {
UserInfoController controller = Get.find();
ThemeController themeController = Get.find();
final List<Map<String, String>> list = controller.country_code;
int selectedIndex =
list.indexWhere((e) => e["id"] == initialCode);
if (selectedIndex < 0) selectedIndex = 0;
final RxInt tempIndex = RxInt(selectedIndex);
await showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return Stack(
children: [
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Material(
color: Colors.transparent,
child: Dialog(
backgroundColor: themeController.currentColor.sc17,
insetPadding: EdgeInsets.zero,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
child: Container(
padding: EdgeInsets.fromLTRB(0, 0, 0, 90.rpx),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
/// 顶部栏
Container(
padding:
EdgeInsets.symmetric(horizontal: 30.rpx),
color: themeController.currentColor.sc5,
height: 80.rpx,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
onTap: Get.back,
padding: EdgeInsets.all(0),
child: SizedBox(
width: 110.rpx,
height: 60.rpx,
child: Center(
child: Text(
"取消".tr,
style: const TextStyle(
color: Colors.white),
),
),
),
),
Text(
title.tr,
style: TextStyle(
fontFamily: 'Readex Pro',
color:
themeController.currentColor.sc3,
fontSize: 30.rpx,
),
),
ClickableContainer(
padding: EdgeInsets.all(0),
backgroundColor: Colors.transparent,
highlightColor: Colors.transparent,
onTap: () {
onConfirm(
list[tempIndex.value]["id"]!);
Get.back();
},
child: SizedBox(
width: 110.rpx,
height: 60.rpx,
child: Center(
child: Text(
"确定".tr,
style: TextStyle(
color: themeController
.currentColor.sc2,
),
),
),
),
),
],
),
),
SizedBox(height: 20.rpx),
/// Picker
Stack(
children: [
Positioned.fill(
child: IgnorePointer(
child: Center(
child: Container(
height: 90.rpx,
margin: EdgeInsets.symmetric(
horizontal: 95.rpx),
decoration: BoxDecoration(
color: themeController
.currentColor.sc2,
borderRadius:
BorderRadius.circular(
16.rpx),
),
),
),
),
),
SizedBox(
height: 240.rpx,
child: getOnePickers(
context,
list
.map((e) =>
"${e['name']}".tr+"(${e['id']})")
.toList(),
tempIndex,
),
),
],
),
],
),
),
),
),
),
],
);
},
);
}

View File

@@ -9,6 +9,7 @@ import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/common/util/requestWithLog.dart'; import 'package:vbvs_app/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/base/GradientSwitch.dart'; import 'package:vbvs_app/component/base/GradientSwitch.dart';
import 'package:vbvs_app/component/tool/NewTopSlideNotification.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/message/common_message_setting_controller.dart'; import 'package:vbvs_app/controller/message/common_message_setting_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
@@ -204,6 +205,14 @@ class _CommonMessageSettingPageState extends State<CommonMessageSettingPage> {
? true ? true
: false, : false,
onChanged: (val) { onChanged: (val) {
if (val == false) {
NewTopSlideNotification.show(
text: "注意!关闭后将无法接受任何消息".tr,
textColor: themeController
.currentColor.sc9,
duration: Duration(seconds: 3),
);
}
String serviceAddress = String serviceAddress =
ServiceConstant.service_address; ServiceConstant.service_address;
String serviceName = String serviceName =

View File

@@ -1,280 +1,3 @@
// import 'dart:ui' as ui;
// import 'package:flutter/material.dart';
// import 'package:flutterflow_ui/flutterflow_ui.dart';
// import 'package:vbvs_app/common/util/FitTool.dart';
// import 'package:vbvs_app/common/util/MyUtils.dart';
// import 'package:intl/intl.dart';
// class SnoreChartContainer extends StatelessWidget {
// final List<dynamic> snoreValues;
// final List<dynamic> barData;
// final List<dynamic> showLabel;
// final int startTime;
// final int endTime;
// const SnoreChartContainer({
// required this.snoreValues,
// required this.barData,
// required this.showLabel,
// required this.startTime,
// required this.endTime,
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// SnoreBarOverlay(
// barData: barData,
// showLabel: showLabel,
// startTime: startTime,
// endTime: endTime,
// ),
// Container(height: 32.rpx),
// Container(
// height: 23.rpx,
// child: SnoreWaveform(
// snoreValues: snoreValues,
// startTime: startTime,
// endTime: endTime,
// ),
// ),
// ],
// );
// }
// }
// class SnoreBarOverlay extends StatelessWidget {
// final List<dynamic> barData;
// final List<dynamic> showLabel; // ✅ 加入
// final int startTime;
// final int endTime;
// const SnoreBarOverlay({
// required this.barData,
// required this.showLabel, // ✅ 加入
// required this.startTime,
// required this.endTime,
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// const double barHeight = 50;
// return SizedBox(
// height: barHeight,
// child: CustomPaint(
// size: Size(double.infinity, barHeight),
// painter: SnoreBarPainter(
// barData: barData,
// showLabel: showLabel, // ✅ 加入
// startTime: startTime,
// endTime: endTime,
// ),
// ),
// );
// }
// }
// class SnoreBarPainter extends CustomPainter {
// final List<dynamic> barData;
// final List<dynamic> showLabel; // ✅ 加入
// final int startTime;
// final int endTime;
// SnoreBarPainter({
// required this.barData,
// required this.showLabel, // ✅ 加入
// required this.startTime,
// required this.endTime,
// });
// @override
// void paint(Canvas canvas, Size size) {
// final double width = size.width;
// final double height = size.height;
// final double pixelPerMs = width / (endTime - startTime);
// for (var item in barData) {
// final int st = item['st'];
// final int et = item['et'];
// final int type = item['type'];
// // 查找匹配的颜色
// final match = showLabel.firstWhere(
// (e) => e['type'] == type,
// orElse: () => null,
// );
// Color barColor = Colors.transparent;
// if (match != null) {
// final dynamic colorStr = match['color'];
// if (colorStr != null && colorStr.toString().isNotEmpty) {
// barColor = stringToColor(colorStr);
// }
// }
// final Paint barPaint = Paint()
// ..color = barColor
// ..style = PaintingStyle.fill;
// final double leftX = (st - startTime) * pixelPerMs;
// final double rightX = (et - startTime) * pixelPerMs;
// final double barWidth = rightX - leftX;
// final double barHeight = (type + 5).toDouble() * 8;
// final double top = height - barHeight;
// final rect = Rect.fromLTWH(leftX, top, barWidth, barHeight);
// canvas.drawRect(rect, barPaint);
// }
// }
// @override
// bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
// }
// class SnoreWaveform extends StatelessWidget {
// final List<dynamic> snoreValues;
// final int startTime;
// final int endTime;
// const SnoreWaveform({
// required this.snoreValues,
// required this.startTime,
// required this.endTime,
// super.key,
// });
// @override
// Widget build(BuildContext context) {
// return SizedBox(
// height: 150,
// child: CustomPaint(
// size: Size(double.infinity, 150),
// painter: SnoreWaveformPainter(
// snoreValues: snoreValues,
// startTime: startTime,
// endTime: endTime,
// ),
// ),
// );
// }
// }
// class SnoreWaveformPainter extends CustomPainter {
// final List<dynamic> snoreValues;
// final int startTime;
// final int endTime;
// SnoreWaveformPainter({
// required this.snoreValues,
// required this.startTime,
// required this.endTime,
// });
// @override
// void paint(Canvas canvas, Size size) {
// final double width = size.width;
// final double height = size.height;
// final double centerY = height / 2;
// final double totalDuration = (endTime - startTime).toDouble();
// final double pixelPerMs = width / totalDuration;
// final Paint wavePaint = Paint()
// ..color = stringToColor("#8E7DEF").withOpacity(0.8)
// ..strokeWidth = 1.5
// ..style = PaintingStyle.stroke;
// final Path upperPath = Path();
// final Path lowerPath = Path();
// // ✅ 获取最大值用于自适应比例
// double maxValue = snoreValues.fold<double>(0, (prev, e) {
// final value = e["value"]?.toDouble() ?? 0;
// return value > prev ? value : prev;
// });
// // ✅ 自适应缩放比例,限制波形最大高度为 height * 0.45
// final double maxWaveHeight = height * 1;
// final double scaleY = maxValue > 0 ? (maxWaveHeight / maxValue) : 1;
// for (int i = 0; i < snoreValues.length; i++) {
// final timestamp = snoreValues[i]["st"];
// final value = snoreValues[i]["value"]?.toDouble() ?? 0;
// final x = (timestamp - startTime) * pixelPerMs;
// final y = centerY - value * scaleY;
// final yMirror = centerY + value * scaleY;
// if (i == 0) {
// upperPath.moveTo(x, y);
// lowerPath.moveTo(x, yMirror);
// } else {
// upperPath.lineTo(x, y);
// lowerPath.lineTo(x, yMirror);
// }
// }
// canvas.drawPath(upperPath, wavePaint);
// canvas.drawPath(lowerPath, wavePaint);
// // ✅ 最后绘制中心线,防止被覆盖
// final Paint axisPaint = Paint()
// ..color = Colors.grey.withOpacity(0.6)
// ..strokeWidth = 0.5;
// canvas.drawLine(Offset(0, centerY), Offset(width, centerY), axisPaint);
// // ✅ 时间刻度绘制
// final textPainter = TextPainter(
// textAlign: TextAlign.center,
// textDirection: ui.TextDirection.ltr,
// );
// final int hourMs = 60 * 60 * 1000;
// for (int t = startTime; t < endTime; t += hourMs) {
// double x = (t - startTime) * pixelPerMs;
// DateTime dt = DateTime.fromMillisecondsSinceEpoch(t);
// String label = t == startTime
// ? DateFormat('HH:mm').format(dt)
// : DateFormat('h').format(dt); // 12小时制
// textPainter.text = TextSpan(
// text: label,
// style: TextStyle(fontSize: 10, color: Colors.grey),
// );
// textPainter.layout();
// textPainter.paint(
// canvas,
// Offset(x - textPainter.width / 2, height + 2), // 标签显示在底部
// );
// }
// // ✅ 画终点时间
// {
// double x = (endTime - startTime) * pixelPerMs;
// DateTime dt = DateTime.fromMillisecondsSinceEpoch(endTime);
// String label = DateFormat('HH:mm').format(dt);
// textPainter.text = TextSpan(
// text: label,
// style: TextStyle(fontSize: 10, color: Colors.grey),
// );
// textPainter.layout();
// textPainter.paint(
// canvas,
// Offset(x - textPainter.width / 2, height + 2),
// );
// }
// }
// @override
// bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
// }
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart';
@@ -300,6 +23,11 @@ class SnoreChartContainer extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// barData.add({
// "type": 6,
// "et": 1765291778963,
// "st": 1765289341000,
// });
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -378,6 +106,7 @@ class SnoreBarPainter extends CustomPainter {
final int st = item['st']; final int st = item['st'];
final int et = item['et']; final int et = item['et'];
final int type = item['type']; final int type = item['type'];
int heightInit = 1;
final match = showLabel.firstWhere( final match = showLabel.firstWhere(
(e) => e['type'] == type, (e) => e['type'] == type,
@@ -400,7 +129,17 @@ class SnoreBarPainter extends CustomPainter {
final double rightX = (et - startTime) * pixelPerMs; final double rightX = (et - startTime) * pixelPerMs;
final double barWidth = rightX - leftX; final double barWidth = rightX - leftX;
final double barHeight = (type + 5).toDouble() * 8; //rem 深睡 中
//浅睡 低
//其他 高
if (type == 1) {
heightInit = 1;
} else if (type == 2 || type == 6) {
heightInit = 2;
} else {
heightInit = 3;
}
final double barHeight = (heightInit + 5).toDouble() * 8;
final double top = height - barHeight; final double top = height - barHeight;
final rect = Rect.fromLTWH(leftX, top, barWidth, barHeight); final rect = Rect.fromLTWH(leftX, top, barWidth, barHeight);

View File

@@ -25,7 +25,6 @@ Widget DailyDataWidget(
GlobalKey breatheCardKey, GlobalKey breatheCardKey,
dynamic data, dynamic data,
) { ) {
List<Widget> _buildSectionList() { List<Widget> _buildSectionList() {
EdgeInsetsDirectional padding = EdgeInsetsDirectional padding =
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx); EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx);
@@ -47,7 +46,7 @@ Widget DailyDataWidget(
sleepReport: sleepReport, sleepReport: sleepReport,
highlightItem: data['itemName'], highlightItem: data['itemName'],
), ),
HeartChangeWidget(sleepReport: sleepReport),//心率变异性 HeartChangeWidget(sleepReport: sleepReport), //心率变异性
BreatheStandardWidget(sleepReport: sleepReport), BreatheStandardWidget(sleepReport: sleepReport),
BreatheCard( BreatheCard(
key: breatheCardKey, key: breatheCardKey,

View File

@@ -65,6 +65,12 @@ class _SleepViewWidgetState extends State<SleepViewWidget> {
int minutes = time['minutes']; int minutes = time['minutes'];
List stages = widget.sleepReport['sleepData']['stages']; List stages = widget.sleepReport['sleepData']['stages'];
// showLabel.add({
// "type": 6,
// "name": "rem",
// "color": "#FFC0CB",
// });
return Container( return Container(
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -95,7 +101,8 @@ class _SleepViewWidgetState extends State<SleepViewWidget> {
14.rpx, 10.rpx, 14.rpx, 10.rpx), // 14.rpx, 10.rpx, 14.rpx, 10.rpx), //
borderRadius: 0.rpx, // 圆形点击区域 borderRadius: 0.rpx, // 圆形点击区域
onTap: () { onTap: () {
if (AppConstants().ent_type == APPPackageType.MHT.code) { if (AppConstants().ent_type ==
APPPackageType.MHT.code) {
showTipDialog( showTipDialog(
context, context,
Container( Container(

View File

@@ -11,7 +11,7 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart'; import 'package:vbvs_app/pages/mh_page/homepage/controller/mht_home_controller.dart';
import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart'; import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart';
//睡眠规律性 //睡眠规律性 眠花糖
class NewSleepViewWidget extends StatefulWidget { class NewSleepViewWidget extends StatefulWidget {
var sleepReport; var sleepReport;
final VoidCallback? onRefresh; // 添加回调函数 final VoidCallback? onRefresh; // 添加回调函数

View File

@@ -23,6 +23,7 @@ import 'package:vbvs_app/controller/time/countdown_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/model/user_data.dart'; import 'package:vbvs_app/model/user_data.dart';
import 'package:vbvs_app/pages/person/select_time.dart';
class UpdateUserTelPage extends StatefulWidget { class UpdateUserTelPage extends StatefulWidget {
const UpdateUserTelPage({super.key}); const UpdateUserTelPage({super.key});
@@ -231,25 +232,43 @@ class _UpdateUserTelPageState extends State<UpdateUserTelPage> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
InkWell( InkWell(
onTap: () async {}, onTap: () async {
child: Container( await showCountryCodePickerDialog(
context,
initialCode: userInfoController
.select_country_code
.value, // ✅ 来自 controller
onConfirm: (String code) {
print("选中的区号:$code");
userInfoController
.select_country_code
.value = code;
userInfoController
.updateAll();
// setState / controller.update
},
title: "选择区号".tr,
);
},
child: Obx(() {
return Container(
width: 80.rpx,
alignment: Alignment.center, alignment: Alignment.center,
// constraints: BoxConstraints(
// minWidth: 150.rpx,
// ),
child: Text( child: Text(
"+86", "${userInfoController.select_country_code.value}",
style: TextStyle( style: TextStyle(
fontFamily: 'Readex Pro', fontFamily:
'Readex Pro',
color: themeController color: themeController
.currentColor.sc4, .currentColor.sc2,
fontSize: AppConstants() fontSize: AppConstants()
.middler_text_fontSize, .middler_text_fontSize,
letterSpacing: 0,
),
), ),
), ),
);
}),
), ),
SizedBox( SizedBox(
height: 30.rpx, height: 30.rpx,
child: VerticalDivider( child: VerticalDivider(