更新验证码选择区号
This commit is contained in:
@@ -207,6 +207,15 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
_pageController.animateToPage(index,
|
||||
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
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.getSleepReport();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import 'package:vbvs_app/enum/BindType.dart';
|
||||
import 'package:vbvs_app/model/api_response.dart';
|
||||
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
|
||||
|
||||
|
||||
class DeviceDataComponentWidget extends StatefulWidget {
|
||||
final Map<String, dynamic> device;
|
||||
|
||||
|
||||
@@ -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/weather/weather_controller.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 {
|
||||
const OtherLoginPage({super.key});
|
||||
@@ -106,7 +108,7 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
// 登录成功移出网络检查监听
|
||||
Checknetwork.subscription?.cancel();
|
||||
if (Get.currentRoute != '/mianPageBottomChange') {
|
||||
Get.offAndToNamed("/mianPageBottomChange");
|
||||
Get.offAndToNamed("/mianPageBottomChange");
|
||||
}
|
||||
}
|
||||
// TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听
|
||||
@@ -122,7 +124,6 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -255,6 +256,22 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
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(
|
||||
width: double.infinity,
|
||||
height: bodysize.maxHeight * 0.056,
|
||||
@@ -279,128 +296,161 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
26.rpx, 0, 0, 0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
// constraints: BoxConstraints(
|
||||
// minWidth: 150.rpx,
|
||||
// ),
|
||||
child: Text(
|
||||
"+86",
|
||||
style: TextStyle(
|
||||
fontFamily: 'Readex Pro',
|
||||
Obx(() {
|
||||
if (userInfoController
|
||||
.select_login_method.value ==
|
||||
2) {
|
||||
return Container();
|
||||
}
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsetsDirectional.fromSTEB(
|
||||
26.rpx, 0, 0, 0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
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,
|
||||
child: Text(
|
||||
"${userInfoController.select_country_code.value}",
|
||||
style: TextStyle(
|
||||
fontFamily:
|
||||
'Readex Pro',
|
||||
color: themeController
|
||||
.currentColor.sc2,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30.rpx,
|
||||
child: VerticalDivider(
|
||||
thickness: 2.rpx,
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
),
|
||||
),
|
||||
].divide(SizedBox(width: 10.rpx)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
Obx(() {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
child: Align(
|
||||
alignment:
|
||||
AlignmentDirectional(-1, 0),
|
||||
child: TextFormField(
|
||||
onChanged: (value) {
|
||||
loginController.model.phone =
|
||||
value;
|
||||
},
|
||||
autofocus: false,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
labelStyle: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
hintText: userInfoController
|
||||
.select_login_method
|
||||
.value ==
|
||||
2
|
||||
? '输入邮箱'.tr
|
||||
: "输入手机号码".tr,
|
||||
hintStyle: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
enabledBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0x00000000),
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
focusedBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0x00000000),
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
errorBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.red,
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
focusedErrorBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.red,
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
filled: false,
|
||||
fillColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30.rpx,
|
||||
child: VerticalDivider(
|
||||
thickness: 2.rpx,
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
),
|
||||
),
|
||||
].divide(SizedBox(width: 10.rpx)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Align(
|
||||
alignment:
|
||||
AlignmentDirectional(-1, 0),
|
||||
child: TextFormField(
|
||||
onChanged: (value) {
|
||||
loginController.model.phone =
|
||||
value;
|
||||
},
|
||||
autofocus: false,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
labelStyle: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
),
|
||||
hintText: '其他手机登录页.输入内容'.tr,
|
||||
hintStyle: TextStyle(
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
.currentColor.sc3,
|
||||
),
|
||||
enabledBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0x00000000),
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
focusedBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0x00000000),
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.red,
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
focusedErrorBorder:
|
||||
OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.red,
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(
|
||||
8.rpx),
|
||||
),
|
||||
filled: false,
|
||||
fillColor: Colors.white,
|
||||
),
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 26.rpx,
|
||||
letterSpacing: 0.0,
|
||||
color: themeController
|
||||
cursorColor: themeController
|
||||
.currentColor.sc3,
|
||||
// validator: _model
|
||||
// .textControllerValidator
|
||||
// .asValidator(context),
|
||||
),
|
||||
cursorColor: themeController
|
||||
.currentColor.sc3,
|
||||
// validator: _model
|
||||
// .textControllerValidator
|
||||
// .asValidator(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -629,7 +679,6 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding:
|
||||
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,
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/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/mht_login_controller.dart';
|
||||
import 'package:vbvs_app/pages/person/select_time.dart';
|
||||
|
||||
class THBindTelWidget extends GetView<AuthBindTelController> {
|
||||
BoxConstraints? bodysize;
|
||||
Map img;
|
||||
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
UserInfoController userInfoController = Get.find();
|
||||
|
||||
THBindTelWidget({super.key, required this.img});
|
||||
|
||||
@@ -182,26 +183,42 @@ class THBindTelWidget extends GetView<AuthBindTelController> {
|
||||
0.rpx, 0, 0, 0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
// constraints: BoxConstraints(
|
||||
// minWidth: 150.rpx,
|
||||
// ),
|
||||
child: Text(
|
||||
"+86",
|
||||
style: TextStyle(
|
||||
fontFamily: 'Readex Pro',
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
letterSpacing: 0,
|
||||
onTap: () async {
|
||||
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,
|
||||
child: Text(
|
||||
"${userInfoController.select_country_code.value}",
|
||||
style: TextStyle(
|
||||
fontFamily: 'Readex Pro',
|
||||
color: themeController
|
||||
.currentColor.sc2,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30.rpx,
|
||||
|
||||
@@ -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/requestWithLog.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/user_data.dart';
|
||||
|
||||
@@ -112,9 +113,11 @@ class AuthBindTelController extends GetControllerEx<AuthBindTelModel> {
|
||||
String serviceName = ServiceConstant.server_service;
|
||||
String serviceApi = ServiceConstant.send_code;
|
||||
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
|
||||
UserInfoController userInfoController = Get.find();
|
||||
var data = {
|
||||
"userName": model.phone!,
|
||||
'type': 5,
|
||||
"ccode":userInfoController.select_country_code.value,
|
||||
};
|
||||
if (img != null) {
|
||||
if (img!['code'] != null) {
|
||||
|
||||
@@ -143,8 +143,8 @@ class BindTelWidget extends GetView<AuthBindTelController> {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsetsDirectional.fromSTEB(0, 16, 0, 0),
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
0, 16.rpx, 0, 0),
|
||||
child: Container(
|
||||
width: bodysize!.maxWidth,
|
||||
height: bodysize!.maxHeight * 0.06,
|
||||
@@ -469,8 +469,10 @@ class BindTelWidget extends GetView<AuthBindTelController> {
|
||||
text: message);
|
||||
return;
|
||||
}
|
||||
if (authBindTelController.model.code == null ||
|
||||
authBindTelController.model.code!.isEmpty) {
|
||||
if (authBindTelController.model.code ==
|
||||
null ||
|
||||
authBindTelController
|
||||
.model.code!.isEmpty) {
|
||||
message = "请输入验证码".tr;
|
||||
TopSlideNotification.show(context,
|
||||
text: message);
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:vbvs_app/common/util/FitTool.dart';
|
||||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||||
import 'package:vbvs_app/component/tool/ClickableContainer.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/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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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/requestWithLog.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/controller/message/common_message_setting_controller.dart';
|
||||
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
|
||||
@@ -204,6 +205,14 @@ class _CommonMessageSettingPageState extends State<CommonMessageSettingPage> {
|
||||
? true
|
||||
: false,
|
||||
onChanged: (val) {
|
||||
if (val == false) {
|
||||
NewTopSlideNotification.show(
|
||||
text: "注意!关闭后将无法接受任何消息".tr,
|
||||
textColor: themeController
|
||||
.currentColor.sc9,
|
||||
duration: Duration(seconds: 3),
|
||||
);
|
||||
}
|
||||
String serviceAddress =
|
||||
ServiceConstant.service_address;
|
||||
String serviceName =
|
||||
|
||||
@@ -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 'package:flutter/material.dart';
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
@@ -300,6 +23,11 @@ class SnoreChartContainer extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// barData.add({
|
||||
// "type": 6,
|
||||
// "et": 1765291778963,
|
||||
// "st": 1765289341000,
|
||||
// });
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -378,6 +106,7 @@ class SnoreBarPainter extends CustomPainter {
|
||||
final int st = item['st'];
|
||||
final int et = item['et'];
|
||||
final int type = item['type'];
|
||||
int heightInit = 1;
|
||||
|
||||
final match = showLabel.firstWhere(
|
||||
(e) => e['type'] == type,
|
||||
@@ -400,7 +129,17 @@ class SnoreBarPainter extends CustomPainter {
|
||||
final double rightX = (et - startTime) * pixelPerMs;
|
||||
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 rect = Rect.fromLTWH(leftX, top, barWidth, barHeight);
|
||||
|
||||
@@ -25,11 +25,10 @@ Widget DailyDataWidget(
|
||||
GlobalKey breatheCardKey,
|
||||
dynamic data,
|
||||
) {
|
||||
|
||||
List<Widget> _buildSectionList() {
|
||||
EdgeInsetsDirectional padding =
|
||||
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx);
|
||||
|
||||
|
||||
return [
|
||||
SleepScoreWidget(sleepReport: sleepReport),
|
||||
SleepViewWidget(sleepReport: sleepReport),
|
||||
@@ -47,7 +46,7 @@ Widget DailyDataWidget(
|
||||
sleepReport: sleepReport,
|
||||
highlightItem: data['itemName'],
|
||||
),
|
||||
HeartChangeWidget(sleepReport: sleepReport),//心率变异性
|
||||
HeartChangeWidget(sleepReport: sleepReport), //心率变异性
|
||||
BreatheStandardWidget(sleepReport: sleepReport),
|
||||
BreatheCard(
|
||||
key: breatheCardKey,
|
||||
|
||||
@@ -65,6 +65,12 @@ class _SleepViewWidgetState extends State<SleepViewWidget> {
|
||||
int minutes = time['minutes'];
|
||||
|
||||
List stages = widget.sleepReport['sleepData']['stages'];
|
||||
// showLabel.add({
|
||||
// "type": 6,
|
||||
// "name": "rem",
|
||||
// "color": "#FFC0CB",
|
||||
// });
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
@@ -95,7 +101,8 @@ class _SleepViewWidgetState extends State<SleepViewWidget> {
|
||||
14.rpx, 10.rpx, 14.rpx, 10.rpx), //
|
||||
borderRadius: 0.rpx, // 圆形点击区域
|
||||
onTap: () {
|
||||
if (AppConstants().ent_type == APPPackageType.MHT.code) {
|
||||
if (AppConstants().ent_type ==
|
||||
APPPackageType.MHT.code) {
|
||||
showTipDialog(
|
||||
context,
|
||||
Container(
|
||||
|
||||
@@ -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/sleep_report/chart/SnoreWaveform.dart';
|
||||
|
||||
//睡眠规律性
|
||||
//睡眠规律性 眠花糖
|
||||
class NewSleepViewWidget extends StatefulWidget {
|
||||
var sleepReport;
|
||||
final VoidCallback? onRefresh; // 添加回调函数
|
||||
|
||||
@@ -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/model/api_response.dart';
|
||||
import 'package:vbvs_app/model/user_data.dart';
|
||||
import 'package:vbvs_app/pages/person/select_time.dart';
|
||||
|
||||
class UpdateUserTelPage extends StatefulWidget {
|
||||
const UpdateUserTelPage({super.key});
|
||||
@@ -231,25 +232,43 @@ class _UpdateUserTelPageState extends State<UpdateUserTelPage> {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () async {},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
// constraints: BoxConstraints(
|
||||
// minWidth: 150.rpx,
|
||||
// ),
|
||||
child: Text(
|
||||
"+86",
|
||||
style: TextStyle(
|
||||
fontFamily: 'Readex Pro',
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
letterSpacing: 0,
|
||||
onTap: () async {
|
||||
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,
|
||||
child: Text(
|
||||
"${userInfoController.select_country_code.value}",
|
||||
style: TextStyle(
|
||||
fontFamily:
|
||||
'Readex Pro',
|
||||
color: themeController
|
||||
.currentColor.sc2,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 30.rpx,
|
||||
child: VerticalDivider(
|
||||
|
||||
Reference in New Issue
Block a user