更新样式
This commit is contained in:
@@ -414,8 +414,8 @@
|
||||
"呼吸数据介绍": "Respiration data refers to basic respiration metrics during sleep, serving as core indicators for evaluating sleep respiration quality and screening sleep breathing disorders.",
|
||||
"心率散点图": "Heart rate scatter plot",
|
||||
"心率散点图介绍": "The ECG scatter plot is a nonlinear graphical method for recording continuous heart rate RR interval diagrams, also called scatter plots as they consist of scattered points.",
|
||||
"今日数据": "yesterday",
|
||||
"昨日数据": "today",
|
||||
"今日数据": "today",
|
||||
"昨日数据": "yesterday",
|
||||
"次": "times",
|
||||
"秒": "sec",
|
||||
"暂无": "None",
|
||||
|
||||
@@ -221,6 +221,39 @@ class CityModelController extends GetControllerEx<CityModel> {
|
||||
// 检查是否已加载数据
|
||||
bool get isDataLoaded => cityList.isNotEmpty;
|
||||
|
||||
// void searchCities(String keyword) {
|
||||
// model.keyword = keyword;
|
||||
// searchResults?.clear();
|
||||
|
||||
// if (keyword.isEmpty) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// final keywordLower = keyword.toLowerCase();
|
||||
|
||||
// // 遍历所有城市数据
|
||||
// for (var country in cityList) {
|
||||
// final countryName = country.value ?? country.country ?? '';
|
||||
|
||||
// for (var province in country.children ?? []) {
|
||||
// final provinceName = province.value ?? province.province ?? '';
|
||||
|
||||
// for (var city in province.children ?? []) {
|
||||
// final cityName = city.value ?? city.city ?? '';
|
||||
// final displayName = '$countryName-$provinceName-$cityName';
|
||||
|
||||
// // 模糊匹配
|
||||
// if (countryName.toLowerCase().contains(keywordLower) ||
|
||||
// provinceName.toLowerCase().contains(keywordLower) ||
|
||||
// cityName.toLowerCase().contains(keywordLower) ||
|
||||
// displayName.toLowerCase().contains(keywordLower)) {
|
||||
// searchResults?.add(displayName);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
void searchCities(String keyword) {
|
||||
model.keyword = keyword;
|
||||
searchResults?.clear();
|
||||
@@ -231,23 +264,43 @@ class CityModelController extends GetControllerEx<CityModel> {
|
||||
|
||||
final keywordLower = keyword.toLowerCase();
|
||||
|
||||
// 遍历所有城市数据
|
||||
for (var country in cityList) {
|
||||
final countryName = country.value ?? country.country ?? '';
|
||||
|
||||
for (var province in country.children ?? []) {
|
||||
// 如果该国家没有 children(一级结构)
|
||||
if (country.children == null || country.children!.isEmpty) {
|
||||
final name = countryName;
|
||||
if (name.toLowerCase().contains(keywordLower)) {
|
||||
searchResults?.add(name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 二层循环:省份或城市
|
||||
for (var province in country.children!) {
|
||||
final provinceName = province.value ?? province.province ?? '';
|
||||
final level2Name = '$countryName-$provinceName';
|
||||
|
||||
for (var city in province.children ?? []) {
|
||||
// 省本身是否匹配(适用于 国家→省 二级结构)
|
||||
if (province.children == null || province.children!.isEmpty) {
|
||||
if (countryName.toLowerCase().contains(keywordLower) ||
|
||||
provinceName.toLowerCase().contains(keywordLower) ||
|
||||
level2Name.toLowerCase().contains(keywordLower)) {
|
||||
searchResults?.add(level2Name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 有第三级城市 → 遍历城市
|
||||
for (var city in province.children!) {
|
||||
final cityName = city.value ?? city.city ?? '';
|
||||
final displayName = '$countryName-$provinceName-$cityName';
|
||||
final level3Name = '$countryName-$provinceName-$cityName';
|
||||
|
||||
// 模糊匹配
|
||||
if (countryName.toLowerCase().contains(keywordLower) ||
|
||||
provinceName.toLowerCase().contains(keywordLower) ||
|
||||
cityName.toLowerCase().contains(keywordLower) ||
|
||||
displayName.toLowerCase().contains(keywordLower)) {
|
||||
searchResults?.add(displayName);
|
||||
level3Name.toLowerCase().contains(keywordLower)) {
|
||||
searchResults?.add(level3Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,27 +308,77 @@ class CityModelController extends GetControllerEx<CityModel> {
|
||||
}
|
||||
|
||||
// 根据显示名称获取对应的 CityModel
|
||||
// CityModel? getCityByDisplayName(String displayName) {
|
||||
// final parts = displayName.split('-');
|
||||
// if (parts.length != 3) return null;
|
||||
|
||||
// final countryName = parts[0];
|
||||
// final provinceName = parts[1];
|
||||
// final cityName = parts[2];
|
||||
|
||||
// for (var country in cityList) {
|
||||
// if ((country.value ?? country.country ?? '') == countryName) {
|
||||
// for (var province in country.children ?? []) {
|
||||
// if ((province.value ?? province.province ?? '') == provinceName) {
|
||||
// for (var city in province.children ?? []) {
|
||||
// if ((city.value ?? city.city ?? '') == cityName) {
|
||||
// return city;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
CityModel? getCityByDisplayName(String displayName) {
|
||||
final parts = displayName.split('-');
|
||||
if (parts.length != 3) return null;
|
||||
|
||||
if (parts.isEmpty) return null;
|
||||
|
||||
final countryName = parts[0];
|
||||
final provinceName = parts[1];
|
||||
final cityName = parts[2];
|
||||
final provinceName = parts.length >= 2 ? parts[1] : null;
|
||||
final cityName = parts.length >= 3 ? parts[2] : null;
|
||||
|
||||
for (var country in cityList) {
|
||||
if ((country.value ?? country.country ?? '') == countryName) {
|
||||
for (var province in country.children ?? []) {
|
||||
if ((province.value ?? province.province ?? '') == provinceName) {
|
||||
for (var city in province.children ?? []) {
|
||||
if ((city.value ?? city.city ?? '') == cityName) {
|
||||
return city;
|
||||
}
|
||||
}
|
||||
final cName = country.value ?? country.country ?? '';
|
||||
if (cName != countryName) continue;
|
||||
|
||||
// 一级结构:直接 return 国家
|
||||
if (provinceName == null) {
|
||||
return country;
|
||||
}
|
||||
|
||||
// 如果没有 children,就不可能有省或市
|
||||
if (country.children == null || country.children!.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 二级匹配:省或城市
|
||||
for (var province in country.children!) {
|
||||
final pName = province.value ?? province.province ?? '';
|
||||
if (pName != provinceName) continue;
|
||||
|
||||
// 二级结构:国家-省
|
||||
if (cityName == null) {
|
||||
return province;
|
||||
}
|
||||
|
||||
// 有城市才继续
|
||||
if (province.children == null || province.children!.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 三级结构:国家-省-市
|
||||
for (var city in province.children!) {
|
||||
final ciName = city.value ?? city.city ?? '';
|
||||
if (ciName == cityName) {
|
||||
return city;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutterflow_ui/flutterflow_ui.dart';
|
||||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
|
||||
import '../../common/util/MyUtils.dart';
|
||||
|
||||
class ListSearchWidget extends GetView {
|
||||
@@ -38,7 +39,8 @@ class ListSearchWidget extends GetView {
|
||||
children: [
|
||||
// 搜索框部分
|
||||
Padding(
|
||||
padding: padding ?? EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
|
||||
padding:
|
||||
padding ?? EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
@@ -46,7 +48,7 @@ class ListSearchWidget extends GetView {
|
||||
borderRadius: BorderRadius.circular(16.rpx),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(35.rpx, 0, 35.rpx, 0),
|
||||
padding: EdgeInsetsDirectional.fromSTEB(35.rpx, 0, 0.rpx, 0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -139,7 +141,9 @@ class ListSearchWidget extends GetView {
|
||||
},
|
||||
onTap: () {
|
||||
// 点击输入框时显示结果列表(如果开启了显示功能且有搜索结果)
|
||||
if (showResultList && searchResults != null && searchResults!.isNotEmpty) {
|
||||
if (showResultList &&
|
||||
searchResults != null &&
|
||||
searchResults!.isNotEmpty) {
|
||||
_showResults.value = true;
|
||||
}
|
||||
},
|
||||
@@ -151,8 +155,12 @@ class ListSearchWidget extends GetView {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(26.rpx, 0, 0, 0),
|
||||
child: InkWell(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0, 0),
|
||||
child: ClickableContainer(
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor:
|
||||
themeController.currentColor.sc4.withOpacity(0.8),
|
||||
padding: EdgeInsets.fromLTRB(0, 0, 35.rpx, 0),
|
||||
onTap: () {
|
||||
findCallback?.call();
|
||||
// 点击搜索按钮后显示结果列表(如果开启了显示功能)
|
||||
@@ -192,7 +200,9 @@ class ListSearchWidget extends GetView {
|
||||
// 搜索结果列表(可选显示)
|
||||
if (showResultList) ...[
|
||||
Obx(() {
|
||||
if (!_showResults.value || searchResults == null || searchResults!.isEmpty) {
|
||||
if (!_showResults.value ||
|
||||
searchResults == null ||
|
||||
searchResults!.isEmpty) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -244,4 +254,4 @@ class ListSearchWidget extends GetView {
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
46
lib/controller/home/Dio.dart
Normal file
46
lib/controller/home/Dio.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||||
import 'package:vbvs_app/controller/login/login_controller.dart';
|
||||
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
||||
|
||||
class ApiService {
|
||||
static Dio dio = Dio();
|
||||
|
||||
static Dio request = Dio();
|
||||
static Dio requestNoInfo = Dio();
|
||||
static Dio requestNoError = Dio(); //不处理错误的请求,用于设备操作上报
|
||||
|
||||
static Dio reservation = Dio();
|
||||
|
||||
static void init() {
|
||||
|
||||
reservationInit();
|
||||
}
|
||||
|
||||
static void reservationInit() {
|
||||
reservation.options.baseUrl = "https://crm-api.swes.com.cn";
|
||||
reservation.options.connectTimeout = const Duration(seconds: 15);
|
||||
reservation.options.receiveTimeout = const Duration(seconds: 10);
|
||||
reservation.options.sendTimeout = const Duration(seconds: 10);
|
||||
reservation.interceptors.add(InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
print("---> ${options.method}, ${options.path}, ${options.data}");
|
||||
options.headers['token'] =
|
||||
"bMAQVR1x48t66u8EDYSftAJGo17r0rIB3z15JgyyoGz1rAEZHs1htHOCorYFJ2RT";
|
||||
return handler.next(options);
|
||||
},
|
||||
onResponse: (response, ResponseInterceptorHandler handler) {
|
||||
print("<--- ${response.statusCode} ${response.data}");
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (e, handler) {
|
||||
// 错误处理,例如打印错误信息
|
||||
showToast("网络异常或服务连接异常,请稍候再试", color: color_error);
|
||||
print("DioError: $e");
|
||||
return handler.reject(e);
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import 'package:vbvs_app/common/pojo/city.dart';
|
||||
import 'package:vbvs_app/common/util/CheckNetwork.dart';
|
||||
import 'package:vbvs_app/common/util/CommonVariables.dart';
|
||||
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
|
||||
import 'package:vbvs_app/common/util/Dio.dart';
|
||||
// import 'package:vbvs_app/common/util/Dio.dart';
|
||||
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||
import 'package:vbvs_app/common/util/JPushUtil.dart';
|
||||
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||||
@@ -32,6 +32,7 @@ import 'package:vbvs_app/controller/device/device_calibration_controller.dart';
|
||||
import 'package:vbvs_app/controller/device/device_share_controller.dart';
|
||||
import 'package:vbvs_app/controller/device/device_share_list_controller.dart';
|
||||
import 'package:vbvs_app/controller/device/device_type_controller.dart';
|
||||
import 'package:vbvs_app/controller/home/Dio.dart';
|
||||
import 'package:vbvs_app/controller/home/home_controller.dart';
|
||||
import 'package:vbvs_app/controller/login/login_controller.dart';
|
||||
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
|
||||
@@ -94,6 +95,12 @@ Future<void> main() async {
|
||||
final ThemeController themeController = Get.put(ThemeController());
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent, // 状态栏透明
|
||||
statusBarIconBrightness: Brightness.light, // Android:白色图标
|
||||
statusBarBrightness: Brightness.dark, // iOS:白色图标
|
||||
));
|
||||
|
||||
final Locale? deviceLocale = Get.deviceLocale;
|
||||
print('系统语言: ${deviceLocale?.languageCode}');
|
||||
|
||||
|
||||
@@ -64,12 +64,15 @@ Widget getOnePickers(
|
||||
bool looping = false,
|
||||
void Function(int)? onChanged,
|
||||
bool isMonthName = false,
|
||||
Key? pickerKey,
|
||||
}) {
|
||||
ThemeController themeController = Get.find();
|
||||
final bool isEn = Get.locale?.languageCode.startsWith('en') ?? false;
|
||||
|
||||
return Obx(() {
|
||||
final dynamicKey = ValueKey('picker_${arr.length}_${selectedIndex.value}');
|
||||
return CupertinoPicker.builder(
|
||||
key: pickerKey ?? dynamicKey,
|
||||
itemExtent: 90.rpx,
|
||||
useMagnifier: false,
|
||||
magnification: 1,
|
||||
|
||||
@@ -30,9 +30,11 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
final BodyDeviceController bodyDeviceController = Get.find();
|
||||
HomeController homeController = Get.find();
|
||||
final GlobalKey addIconKey = GlobalKey();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
final ScrollController _myDeviceScrollController = ScrollController();
|
||||
final ScrollController _cloudDeviceScrollController = ScrollController();
|
||||
OverlayEntry? _popupEntry;
|
||||
Timer? _timer;
|
||||
late PageController _pageController;
|
||||
|
||||
void _showPopup() {
|
||||
final renderBox =
|
||||
@@ -42,20 +44,16 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
final size = renderBox.size;
|
||||
double popupWidth = 190.rpx;
|
||||
|
||||
// 移除之前的弹窗
|
||||
_popupEntry?.remove();
|
||||
|
||||
// 创建新的OverlayEntry
|
||||
_popupEntry = OverlayEntry(
|
||||
builder: (context) => Stack(
|
||||
children: [
|
||||
// 半透明背景,点击后关闭弹窗
|
||||
ModalBarrier(
|
||||
dismissible: true,
|
||||
color: Colors.transparent,
|
||||
onDismiss: _hidePopup,
|
||||
),
|
||||
// 弹窗内容
|
||||
Positioned(
|
||||
top: position.dy + size.height + 26.rpx,
|
||||
left: position.dx + size.width - popupWidth - 40.rpx,
|
||||
@@ -81,36 +79,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 11.rpx),
|
||||
// ClickableContainer(
|
||||
// padding: EdgeInsets.symmetric(vertical: 10.rpx),
|
||||
// backgroundColor: Colors.transparent,
|
||||
// highlightColor:
|
||||
// themeController.currentColor.sc16.withOpacity(0.1),
|
||||
// borderRadius: 0.rpx,
|
||||
// onTap: () {
|
||||
// print('点击扫一扫');
|
||||
// _hidePopup();
|
||||
// TopSlideNotification.show(
|
||||
// context,
|
||||
// text: "待开发.提示".tr,
|
||||
// textColor: themeController.currentColor.sc2,
|
||||
// );
|
||||
// },
|
||||
// child: Container(
|
||||
// width: double.infinity,
|
||||
// child: Center(
|
||||
// child: Text(
|
||||
// '扫一扫.标题'.tr,
|
||||
// style: TextStyle(
|
||||
// fontSize: AppConstants().normal_text_fontSize,
|
||||
// color: themeController.currentColor.sc3,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 35.rpx),
|
||||
|
||||
ClickableContainer(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.rpx),
|
||||
backgroundColor: Colors.transparent,
|
||||
@@ -147,7 +115,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
),
|
||||
);
|
||||
|
||||
// 插入新的OverlayEntry
|
||||
Overlay.of(context)!.insert(_popupEntry!);
|
||||
}
|
||||
|
||||
@@ -161,6 +128,10 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
bodyDeviceController.keyWord.value = "";
|
||||
super.initState();
|
||||
|
||||
// 初始化PageController,根据当前类型设置初始页面
|
||||
_pageController = PageController(
|
||||
initialPage: bodyDeviceController.model.type == 1 ? 0 : 1);
|
||||
|
||||
// 处理传入的type参数
|
||||
if (widget.type != null && widget.type is Map) {
|
||||
final bindType = widget.type['bind_type'];
|
||||
@@ -169,23 +140,22 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
if (bindType != null) {
|
||||
bodyDeviceController.model.type = bindType;
|
||||
homeController.model.type = bindType;
|
||||
// 更新PageController到正确的位置
|
||||
_pageController = PageController(
|
||||
initialPage: bodyDeviceController.model.type == 1 ? 0 : 1);
|
||||
}
|
||||
|
||||
// 初次请求设备列表
|
||||
_fetchDeviceList().then((_) {
|
||||
if (mac != null) {
|
||||
// 延迟执行以确保列表已渲染
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToDeviceWithMac(mac);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 没有传入type时的默认逻辑
|
||||
_fetchDeviceList();
|
||||
}
|
||||
|
||||
// 每5秒定时请求一次
|
||||
_timer = Timer.periodic(Duration(seconds: 5), (timer) {
|
||||
_fetchDeviceList();
|
||||
});
|
||||
@@ -209,28 +179,54 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
final deviceList = bodyDeviceController.deviceList.value;
|
||||
final index = deviceList.indexWhere((device) => device['mac'] == mac);
|
||||
|
||||
if (index != -1 && _scrollController.hasClients) {
|
||||
// 动态计算高度:最小为 501.rpx,最大为 26.6% 屏幕高度
|
||||
if (index != -1) {
|
||||
final screenHeight = MediaQuery.of(Get.context!).size.height;
|
||||
final dynamicItemHeight = (screenHeight * 0.266).rpx;
|
||||
final itemHeight =
|
||||
dynamicItemHeight < 501.rpx ? 501.rpx : dynamicItemHeight;
|
||||
|
||||
final spacing = 25.rpx;
|
||||
final targetPosition = index * (itemHeight + spacing);
|
||||
|
||||
_scrollController.animateTo(
|
||||
targetPosition,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
// 根据当前类型选择对应的ScrollController
|
||||
final currentScrollController =
|
||||
bodyDeviceController.model.type == 1
|
||||
? _myDeviceScrollController
|
||||
: _cloudDeviceScrollController;
|
||||
|
||||
if (currentScrollController.hasClients) {
|
||||
currentScrollController.animateTo(
|
||||
targetPosition,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 标签切换回调
|
||||
void _onTabChanged(int index) {
|
||||
_pageController.animateToPage(index,
|
||||
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||
}
|
||||
|
||||
// 页面切换回调
|
||||
void _onPageChanged(int index) {
|
||||
int newType = index == 0 ? 1 : 2;
|
||||
if (bodyDeviceController.model.type != newType) {
|
||||
bodyDeviceController.model.type = newType;
|
||||
homeController.model.type = newType;
|
||||
bodyDeviceController.updateAll();
|
||||
homeController.updateAll();
|
||||
_fetchDeviceList();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel(); // 页面销毁时取消定时器
|
||||
_scrollController.dispose();
|
||||
_timer?.cancel();
|
||||
_myDeviceScrollController.dispose();
|
||||
_cloudDeviceScrollController.dispose();
|
||||
_pageController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -238,7 +234,10 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, bodysize) => GestureDetector(
|
||||
// onTap: () => FocusScope.of(context).unfocus(),,
|
||||
onTap: () {
|
||||
_hidePopup();
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
@@ -262,7 +261,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
/// 居中标题
|
||||
Text(
|
||||
'体征检测设备.标题'.tr,
|
||||
style: TextStyle(
|
||||
@@ -288,7 +286,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
highlightColor: themeController.currentColor.sc16,
|
||||
padding: EdgeInsets.all(8.rpx),
|
||||
onTap: () {
|
||||
// 点击图标时,展示弹窗
|
||||
if (_popupEntry == null) {
|
||||
_showPopup();
|
||||
} else {
|
||||
@@ -310,7 +307,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
centerTitle: false,
|
||||
),
|
||||
body: GestureDetector(
|
||||
onTap: _hidePopup, // 点击空白处自动关闭弹窗
|
||||
onTap: _hidePopup,
|
||||
child: SafeArea(
|
||||
top: true,
|
||||
child: Padding(
|
||||
@@ -335,6 +332,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// 标签切换部分 - 保持原有样式
|
||||
Stack(
|
||||
alignment: Alignment.bottomLeft,
|
||||
children: [
|
||||
@@ -348,19 +346,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
.currentColor.sc3,
|
||||
borderRadius: 8.rpx,
|
||||
padding: EdgeInsets.all(0),
|
||||
onTap: () async {
|
||||
bodyDeviceController.model.type =
|
||||
1;
|
||||
homeController.model.type = 1;
|
||||
await bodyDeviceController
|
||||
.getDeviceList();
|
||||
await bodyDeviceController
|
||||
.getDeviceList();
|
||||
await bodyDeviceController
|
||||
.getSleepReport();
|
||||
bodyDeviceController.updateAll();
|
||||
homeController.updateAll();
|
||||
},
|
||||
onTap: () => _onTabChanged(0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
@@ -400,19 +386,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
.currentColor.sc3,
|
||||
borderRadius: 8.rpx,
|
||||
padding: EdgeInsets.all(0),
|
||||
onTap: () async {
|
||||
homeController.model.type = 2;
|
||||
bodyDeviceController.model.type =
|
||||
2;
|
||||
await bodyDeviceController
|
||||
.getDeviceList();
|
||||
await bodyDeviceController
|
||||
.getDeviceList();
|
||||
await bodyDeviceController
|
||||
.getSleepReport();
|
||||
bodyDeviceController.updateAll();
|
||||
homeController.updateAll();
|
||||
},
|
||||
onTap: () => _onTabChanged(1),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
@@ -451,7 +425,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
],
|
||||
),
|
||||
Obx(() {
|
||||
// 横线宽度固定为180.rpx
|
||||
// 保持原有的横线宽度180.rpx
|
||||
double lineWidth = 180.rpx;
|
||||
return AnimatedPositioned(
|
||||
duration: Duration(milliseconds: 300),
|
||||
@@ -475,6 +449,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
}),
|
||||
],
|
||||
),
|
||||
// 搜索框部分保持不变
|
||||
Container(
|
||||
width:
|
||||
MediaQuery.sizeOf(context).width * 0.38,
|
||||
@@ -636,31 +611,63 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(() {
|
||||
final isEmpty =
|
||||
bodyDeviceController.deviceList.value.isEmpty;
|
||||
return Expanded(
|
||||
child: isEmpty
|
||||
? NullDataWidget()
|
||||
: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
30.rpx, 26.rpx, 30.rpx, 0),
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: bodyDeviceController
|
||||
.deviceList.value
|
||||
.map((device) =>
|
||||
DeviceDataComponentWidget(
|
||||
device: device))
|
||||
.toList()
|
||||
.divide(SizedBox(height: 25.rpx)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
// 使用PageView替换原来的单一列表
|
||||
Expanded(
|
||||
child: PageView(
|
||||
controller: _pageController,
|
||||
onPageChanged: _onPageChanged,
|
||||
children: [
|
||||
// 我的e护页面
|
||||
Obx(() {
|
||||
final myDeviceList = bodyDeviceController.deviceList.value
|
||||
.where((device) => device['type'] == 1 || device['bind_type'] == 1)
|
||||
.toList();
|
||||
return myDeviceList.isEmpty
|
||||
? NullDataWidget()
|
||||
: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
30.rpx, 26.rpx, 30.rpx, 0),
|
||||
child: SingleChildScrollView(
|
||||
controller: _myDeviceScrollController,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: myDeviceList
|
||||
.map((device) =>
|
||||
DeviceDataComponentWidget(
|
||||
device: device))
|
||||
.toList()
|
||||
.divide(SizedBox(height: 25.rpx)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
// 云关爱页面
|
||||
Obx(() {
|
||||
final cloudDeviceList = bodyDeviceController.deviceList.value
|
||||
.where((device) => device['type'] == 2 || device['bind_type'] == 2)
|
||||
.toList();
|
||||
return cloudDeviceList.isEmpty
|
||||
? NullDataWidget()
|
||||
: Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(
|
||||
30.rpx, 26.rpx, 30.rpx, 0),
|
||||
child: SingleChildScrollView(
|
||||
controller: _cloudDeviceScrollController,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: cloudDeviceList
|
||||
.map((device) =>
|
||||
DeviceDataComponentWidget(
|
||||
device: device))
|
||||
.toList()
|
||||
.divide(SizedBox(height: 25.rpx)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -717,4 +724,4 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class _CalibrationPageState extends State<CalibrationPage> {
|
||||
Timer? _pollingTimer;
|
||||
|
||||
bool exit = false;
|
||||
bool start = false;//是否开始进行校准
|
||||
bool start = false; //是否开始进行校准
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -691,6 +691,10 @@ class _CalibrationPageState extends State<CalibrationPage> {
|
||||
progressApi, isSecondStep);
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController
|
||||
.process.value = 0;
|
||||
deviceCalibrationController
|
||||
.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value =
|
||||
0;
|
||||
blueteethBindController.cid!.value = "";
|
||||
@@ -757,6 +761,10 @@ class _CalibrationPageState extends State<CalibrationPage> {
|
||||
progressApi, isSecondStep);
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController
|
||||
.process.value = 0;
|
||||
deviceCalibrationController
|
||||
.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value =
|
||||
0;
|
||||
blueteethBindController.cid!.value = "";
|
||||
@@ -935,7 +943,7 @@ class _CalibrationPageState extends State<CalibrationPage> {
|
||||
} else {
|
||||
deviceCalibrationController.statusContext.value = "";
|
||||
//当前步骤执行失败
|
||||
deviceCalibrationController.bed_calibration.value == 0;
|
||||
deviceCalibrationController.bed_calibration.value = 0;
|
||||
_pollingTimer?.cancel();
|
||||
blueteethBindController.cid?.value = "";
|
||||
deviceCalibrationController.process.value = 0;
|
||||
@@ -985,6 +993,8 @@ class _CalibrationPageState extends State<CalibrationPage> {
|
||||
}
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController.process.value = 0;
|
||||
deviceCalibrationController.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value = 0;
|
||||
_pollingTimer?.cancel();
|
||||
blueteethBindController.cid!.value = "";
|
||||
|
||||
@@ -685,6 +685,10 @@ class _CalibrationPersonPageState extends State<CalibrationPersonPage> {
|
||||
progressApi, isSecondStep);
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController
|
||||
.process.value = 0;
|
||||
deviceCalibrationController
|
||||
.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value =
|
||||
0;
|
||||
blueteethBindController.cid!.value = "";
|
||||
@@ -751,6 +755,10 @@ class _CalibrationPersonPageState extends State<CalibrationPersonPage> {
|
||||
progressApi, isSecondStep);
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController
|
||||
.process.value = 0;
|
||||
deviceCalibrationController
|
||||
.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value =
|
||||
0;
|
||||
blueteethBindController.cid!.value = "";
|
||||
@@ -934,7 +942,7 @@ class _CalibrationPersonPageState extends State<CalibrationPersonPage> {
|
||||
} else {
|
||||
deviceCalibrationController.statusContext.value = "";
|
||||
//当前步骤执行失败
|
||||
deviceCalibrationController.bed_calibration.value == 0;
|
||||
deviceCalibrationController.bed_calibration.value = 0;
|
||||
_pollingTimer?.cancel();
|
||||
blueteethBindController.cid?.value = "";
|
||||
deviceCalibrationController.process.value = 0;
|
||||
@@ -984,6 +992,8 @@ class _CalibrationPersonPageState extends State<CalibrationPersonPage> {
|
||||
}
|
||||
},
|
||||
onFailure: (res) {
|
||||
deviceCalibrationController.process.value = 0;
|
||||
deviceCalibrationController.bed_calibration.value = 0;
|
||||
deviceCalibrationController.flag.value = 0;
|
||||
_pollingTimer?.cancel();
|
||||
blueteethBindController.cid!.value = "";
|
||||
|
||||
@@ -275,6 +275,43 @@ 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',
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
fontSize: AppConstants()
|
||||
.middler_text_fontSize,
|
||||
letterSpacing: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30.rpx,
|
||||
child: VerticalDivider(
|
||||
thickness: 2.rpx,
|
||||
color: themeController
|
||||
.currentColor.sc4,
|
||||
),
|
||||
),
|
||||
].divide(SizedBox(width: 10.rpx)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
child: Align(
|
||||
|
||||
@@ -690,7 +690,7 @@ class _MinePageState extends State<MinePage> {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'V1.0.2511.18',
|
||||
'V1.0.2511.21',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Inter',
|
||||
// color: Color(0xFFD9E3EB),
|
||||
|
||||
@@ -281,476 +281,6 @@ Future showCitySelectionDialog(
|
||||
);
|
||||
}
|
||||
|
||||
// 加载中弹窗
|
||||
Widget _buildLoadingDialog(ThemeController themeController) {
|
||||
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: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.fromLTRB(30.rpx, 40.rpx, 30.rpx, 90.rpx),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CircularProgressIndicator(
|
||||
color: themeController.currentColor.sc2,
|
||||
),
|
||||
SizedBox(height: 20.rpx),
|
||||
Text(
|
||||
"加载中...".tr,
|
||||
style: TextStyle(
|
||||
color: themeController.currentColor.sc3,
|
||||
fontSize: 28.rpx,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 错误弹窗
|
||||
Widget _buildErrorDialog(
|
||||
ThemeController themeController, 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: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.fromLTRB(30.rpx, 40.rpx, 30.rpx, 90.rpx),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"数据加载失败".tr,
|
||||
style: TextStyle(
|
||||
color: themeController.currentColor.sc3,
|
||||
fontSize: 28.rpx,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.rpx),
|
||||
ClickableContainer(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 30.rpx, vertical: 15.rpx),
|
||||
decoration: BoxDecoration(
|
||||
color: themeController.currentColor.sc2,
|
||||
borderRadius: BorderRadius.circular(8.rpx),
|
||||
),
|
||||
child: Text(
|
||||
"关闭".tr,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 28.rpx,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 城市选择器弹窗
|
||||
Widget _buildCityPickerDialog(
|
||||
BuildContext context,
|
||||
ThemeController themeController,
|
||||
bool isChinese,
|
||||
String title,
|
||||
List<CityModel> cityData,
|
||||
Function? onCityChanged, {
|
||||
required RxList<String> countries,
|
||||
required RxList<String> provinces,
|
||||
required RxList<String> cities,
|
||||
required RxInt countryIndex,
|
||||
required RxInt provinceIndex,
|
||||
required RxInt cityIndex,
|
||||
}) {
|
||||
// 内部更新方法
|
||||
void updateCities() {
|
||||
try {
|
||||
if (provinces.isEmpty || countries.isEmpty) return;
|
||||
|
||||
final selectedCountry = countries[countryIndex.value];
|
||||
final selectedProvince = provinces[provinceIndex.value];
|
||||
|
||||
for (var country in cityData) {
|
||||
if (country.value == selectedCountry) {
|
||||
for (var province in country.children ?? []) {
|
||||
if (province.value == selectedProvince) {
|
||||
// 安全地处理城市列表
|
||||
final cityList = (province.children ?? [])
|
||||
.map((city) => city?.value ?? city?.city ?? '')
|
||||
.where((cityName) =>
|
||||
(cityName as String).isNotEmpty) // 明确转换为 String
|
||||
.toList()
|
||||
.cast<String>();
|
||||
|
||||
cities.value = cityList;
|
||||
cityIndex.value = cities.isNotEmpty ? 0 : 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cities.value = [];
|
||||
cityIndex.value = 0;
|
||||
} catch (e) {
|
||||
ef.log("更新城市列表失败:$e");
|
||||
cities.value = [];
|
||||
cityIndex.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void updateProvinces() {
|
||||
try {
|
||||
if (countries.isEmpty) return;
|
||||
|
||||
final selectedCountry = countries[countryIndex.value];
|
||||
|
||||
for (var country in cityData) {
|
||||
if (country.value == selectedCountry) {
|
||||
// 安全地处理省份列表
|
||||
final provinceList = (country.children ?? [])
|
||||
.map((province) => province?.value ?? province?.province ?? '')
|
||||
.where((provinceName) => provinceName.isNotEmpty)
|
||||
.toList()
|
||||
.cast<String>();
|
||||
|
||||
provinces.value = provinceList;
|
||||
provinceIndex.value = provinces.isNotEmpty ? 0 : 0;
|
||||
|
||||
// 更新城市
|
||||
updateCities();
|
||||
return;
|
||||
}
|
||||
}
|
||||
provinces.value = [];
|
||||
provinceIndex.value = 0;
|
||||
cities.value = [];
|
||||
cityIndex.value = 0;
|
||||
} catch (e) {
|
||||
ef.log("更新省份列表失败:$e");
|
||||
provinces.value = [];
|
||||
provinceIndex.value = 0;
|
||||
cities.value = [];
|
||||
cityIndex.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CityModel? getSelectedCityData() {
|
||||
try {
|
||||
if (countries.isEmpty) return null;
|
||||
|
||||
final selectedCountry = countries[countryIndex.value];
|
||||
|
||||
// 查找匹配的国家
|
||||
for (var country in cityData) {
|
||||
final countryName = country.value ?? country.country;
|
||||
if (countryName == selectedCountry) {
|
||||
// 情况1: 只有国家一级(没有省份和城市)
|
||||
if ((country.children == null || country.children!.isEmpty) &&
|
||||
(country.city != null || country.value != null)) {
|
||||
return country; // 直接返回国家数据
|
||||
}
|
||||
|
||||
// 情况2: 有省份但没有城市数据
|
||||
if (provinces.isNotEmpty && provinceIndex.value < provinces.length) {
|
||||
final selectedProvince = provinces[provinceIndex.value];
|
||||
|
||||
for (var province in country.children ?? []) {
|
||||
final provinceName = province.value ?? province.province;
|
||||
if (provinceName == selectedProvince) {
|
||||
// 情况2.1: 省份有城市数据
|
||||
if (cities.isNotEmpty && cityIndex.value < cities.length) {
|
||||
final selectedCityName = cities[cityIndex.value];
|
||||
|
||||
for (var city in province.children ?? []) {
|
||||
final cityName = city.value ?? city.city;
|
||||
if (cityName == selectedCityName) {
|
||||
return city; // 返回城市数据
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 情况2.2: 省份没有城市数据,但省份本身有信息
|
||||
if ((province.children == null || province.children!.isEmpty) &&
|
||||
(province.city != null || province.value != null)) {
|
||||
return province; // 返回省份数据
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 情况3: 有省份但没有选择具体省份,返回国家数据
|
||||
return country;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
ef.log("获取选中城市数据失败:$e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false, // 在这里也设置
|
||||
backgroundColor: Colors.transparent,
|
||||
body: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Dialog(
|
||||
backgroundColor: themeController.currentColor.sc17,
|
||||
insetPadding: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.fromLTRB(30.rpx, 10.rpx, 30.rpx, 0.rpx),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ClickableContainer(
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
width: 110.rpx,
|
||||
height: 60.rpx,
|
||||
alignment: Alignment.center,
|
||||
child: Text("取消".tr,
|
||||
style: TextStyle(
|
||||
fontSize: 30.rpx, color: Colors.white)),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontFamily: 'Readex Pro',
|
||||
color: themeController.currentColor.sc3,
|
||||
fontSize: 30.rpx,
|
||||
),
|
||||
),
|
||||
Obx(() {
|
||||
CityModelController cityModelController = Get.find();
|
||||
cityModelController.tmp;
|
||||
ef.log("${cityModelController.tmp.value}");
|
||||
return ClickableContainer(
|
||||
backgroundColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
padding: EdgeInsets.zero,
|
||||
// onTap: () {
|
||||
// final selectedCityData = getSelectedCityData();
|
||||
// if (selectedCityData != null) {
|
||||
// onCityChanged?.call(selectedCityData);
|
||||
// }
|
||||
// Navigator.of(context).pop();
|
||||
// },
|
||||
onTap: () {
|
||||
final selectedCityData = getSelectedCityData();
|
||||
if (selectedCityData != null) {
|
||||
// 根据ID查找完整的层级数据并补全
|
||||
final fullCityData = findCompleteCityDataById(
|
||||
selectedCityData.id, cityData);
|
||||
if (fullCityData != null) {
|
||||
// 使用完整的数据
|
||||
onCityChanged?.call(fullCityData);
|
||||
} else {
|
||||
// 如果没有找到完整数据,使用当前选中的数据
|
||||
onCityChanged?.call(selectedCityData);
|
||||
}
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
width: 110.rpx,
|
||||
height: 60.rpx,
|
||||
alignment: Alignment.center,
|
||||
child: Text("确定".tr,
|
||||
style: TextStyle(
|
||||
fontSize: 30.rpx,
|
||||
color: themeController.currentColor.sc2,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0),
|
||||
child: ListSearchWidget(
|
||||
keyword: cityModelController.model.keyword,
|
||||
color: cityModelController.model.color,
|
||||
hint: "输入国家、省份或城市".tr,
|
||||
onChange: (d) {
|
||||
cityModelController.model.keyword = d;
|
||||
// 实时搜索
|
||||
cityModelController.searchCities(d);
|
||||
},
|
||||
findCallback: () {
|
||||
// 点击搜索按钮时搜索
|
||||
cityModelController.searchCities(
|
||||
cityModelController.model.keyword ?? "");
|
||||
},
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
0.rpx,
|
||||
30.rpx,
|
||||
0.rpx,
|
||||
10.rpx,
|
||||
),
|
||||
showResultList: true, // 开启结果列表
|
||||
searchResults:
|
||||
cityModelController.searchResults, // 搜索结果
|
||||
onResultTap: (result) {
|
||||
// 处理选择结果
|
||||
final selectedCity =
|
||||
cityModelController.getCityByDisplayName(result);
|
||||
|
||||
if (selectedCity != null) {
|
||||
final fullCityData = findCompleteCityDataById(
|
||||
selectedCity.id, cityData);
|
||||
// 更新选中的城市
|
||||
PersonController personController = Get.find();
|
||||
personController.cityModel = fullCityData;
|
||||
personController.updateAll();
|
||||
ef.log("选择了城市: ${selectedCity.displayName}");
|
||||
|
||||
// 关闭弹窗(如果是在弹窗中使用)
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20.rpx),
|
||||
Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: Center(
|
||||
child: Container(
|
||||
height: 90.rpx,
|
||||
margin: EdgeInsets.symmetric(horizontal: 0.rpx),
|
||||
decoration: BoxDecoration(
|
||||
color: themeController.currentColor.sc2,
|
||||
borderRadius: BorderRadius.circular(16.rpx),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(20.rpx, 0, 20.rpx, 0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
CityModelController cityModelController =
|
||||
Get.find();
|
||||
cityModelController.tmp;
|
||||
ef.log("${cityModelController.tmp.value}");
|
||||
return getOnePickers(
|
||||
context,
|
||||
countries,
|
||||
countryIndex,
|
||||
unit: "",
|
||||
onChanged: (_) => updateProvinces(),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
CityModelController cityModelController =
|
||||
Get.find();
|
||||
cityModelController.tmp;
|
||||
ef.log("${cityModelController.tmp.value}");
|
||||
return getOnePickers(
|
||||
context,
|
||||
provinces,
|
||||
provinceIndex,
|
||||
unit: "",
|
||||
onChanged: (_) => updateCities(),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Expanded(
|
||||
child: Obx(() {
|
||||
CityModelController cityModelController =
|
||||
Get.find();
|
||||
cityModelController.tmp;
|
||||
ef.log("${cityModelController.tmp.value}");
|
||||
return getOnePickers(
|
||||
context,
|
||||
cities,
|
||||
cityIndex,
|
||||
unit: "",
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 根据ID查找完整的城市数据(包含国家、省份、城市信息)
|
||||
CityModel? findCompleteCityDataById(int? id, List<CityModel> cityData) {
|
||||
if (id == null) return null;
|
||||
@@ -1095,6 +625,8 @@ Widget _buildCityPickerContent(
|
||||
countryIndex,
|
||||
unit: "",
|
||||
onChanged: (_) => updateProvinces(),
|
||||
pickerKey: ValueKey(
|
||||
'country_${DateTime.now().millisecondsSinceEpoch}'), // 添加动态 key
|
||||
);
|
||||
}),
|
||||
),
|
||||
@@ -1110,6 +642,8 @@ Widget _buildCityPickerContent(
|
||||
provinceIndex,
|
||||
unit: "",
|
||||
onChanged: (_) => updateCities(),
|
||||
pickerKey: ValueKey(
|
||||
'province_${DateTime.now().millisecondsSinceEpoch}'), // 添加动态 key
|
||||
);
|
||||
}),
|
||||
),
|
||||
@@ -1124,6 +658,8 @@ Widget _buildCityPickerContent(
|
||||
cities,
|
||||
cityIndex,
|
||||
unit: "",
|
||||
pickerKey: ValueKey(
|
||||
'city_${DateTime.now().millisecondsSinceEpoch}'), // 添加动态 key
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -199,6 +199,7 @@ class _LineChartByRangePainter extends CustomPainter {
|
||||
for (var item in data) {
|
||||
int start = item['startTime'];
|
||||
int end = item['endTime'];
|
||||
// int times = item['times'];
|
||||
int times = item['times'];
|
||||
|
||||
double startX = xStart * 2 +
|
||||
|
||||
@@ -29,41 +29,112 @@ class SleepRadarChart extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget _buildRadarChart() {
|
||||
// return AspectRatio(
|
||||
// aspectRatio: 1.3,
|
||||
// child: RadarChart(
|
||||
// RadarChartData(
|
||||
// dataSets: [
|
||||
// // 今日数据
|
||||
// RadarDataSet(
|
||||
// dataEntries: data
|
||||
// .map((e) => RadarEntry(value: (e['t'] as num).toDouble()))
|
||||
// .toList(),
|
||||
// borderColor: stringToColor("#00C1AA"),
|
||||
// borderWidth: 2,
|
||||
// fillColor: Colors.transparent,
|
||||
// entryRadius: 0,
|
||||
// ),
|
||||
// // 昨日数据
|
||||
// RadarDataSet(
|
||||
// dataEntries: data
|
||||
// .map((e) => RadarEntry(value: (e['y'] as num).toDouble()))
|
||||
// .toList(),
|
||||
// borderColor: stringToColor("#FFD251"),
|
||||
// borderWidth: 2,
|
||||
// fillColor: Colors.transparent,
|
||||
// entryRadius: 0,
|
||||
// ),
|
||||
// ],
|
||||
// radarBackgroundColor: stringToColor("#343844").withOpacity(0.6),
|
||||
// radarBorderData: BorderSide(
|
||||
// color: themeController.currentColor.sc4, width: 0.5.rpx),
|
||||
// radarShape: RadarShape.polygon,
|
||||
// titlePositionPercentageOffset: 0.2,
|
||||
// titleTextStyle: TextStyle(
|
||||
// fontSize: AppConstants().normal_text_fontSize,
|
||||
// color: themeController.currentColor.sc3),
|
||||
// getTitle: (index, angle) {
|
||||
// return RadarChartTitle(text: data[index]['name'] ?? '未知'.tr);
|
||||
// },
|
||||
// tickCount: 5,
|
||||
// ticksTextStyle:
|
||||
// const TextStyle(color: Colors.transparent, fontSize: 10),
|
||||
// gridBorderData: BorderSide(color: Colors.transparent, width: 1),
|
||||
// tickBorderData: BorderSide(
|
||||
// color: themeController.currentColor.sc4, width: 0.5.rpx),
|
||||
// ),
|
||||
// swapAnimationDuration: const Duration(milliseconds: 400),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget _buildRadarChart() {
|
||||
// 判断 t 是否全为 0(今日)
|
||||
final bool isTodayAllZero =
|
||||
data.every((e) => (e['t'] as num).toDouble() == 0);
|
||||
|
||||
// 判断 y 是否全为 0(昨日)
|
||||
final bool isYesterdayAllZero =
|
||||
data.every((e) => (e['y'] as num).toDouble() == 0);
|
||||
|
||||
// 构建 dataSets
|
||||
final List<RadarDataSet> dataSets = [];
|
||||
|
||||
// 今日数据(非全 0 才加入)
|
||||
if (!isTodayAllZero) {
|
||||
dataSets.add(
|
||||
RadarDataSet(
|
||||
dataEntries: data
|
||||
.map((e) => RadarEntry(value: (e['t'] as num).toDouble()))
|
||||
.toList(),
|
||||
borderColor: stringToColor("#00C1AA"),
|
||||
borderWidth: 2,
|
||||
fillColor: Colors.transparent,
|
||||
entryRadius: 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 昨日数据(非全 0 才加入)
|
||||
if (!isYesterdayAllZero) {
|
||||
dataSets.add(
|
||||
RadarDataSet(
|
||||
dataEntries: data
|
||||
.map((e) => RadarEntry(value: (e['y'] as num).toDouble()))
|
||||
.toList(),
|
||||
borderColor: stringToColor("#FFD251"),
|
||||
borderWidth: 2,
|
||||
fillColor: Colors.transparent,
|
||||
entryRadius: 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.3,
|
||||
child: RadarChart(
|
||||
RadarChartData(
|
||||
dataSets: [
|
||||
// 今日数据
|
||||
RadarDataSet(
|
||||
dataEntries: data
|
||||
.map((e) => RadarEntry(value: (e['t'] as num).toDouble()))
|
||||
.toList(),
|
||||
borderColor: stringToColor("#00C1AA"),
|
||||
borderWidth: 2,
|
||||
fillColor: Colors.transparent,
|
||||
entryRadius: 0,
|
||||
),
|
||||
// 昨日数据
|
||||
RadarDataSet(
|
||||
dataEntries: data
|
||||
.map((e) => RadarEntry(value: (e['y'] as num).toDouble()))
|
||||
.toList(),
|
||||
borderColor: stringToColor("#FFD251"),
|
||||
borderWidth: 2,
|
||||
fillColor: Colors.transparent,
|
||||
entryRadius: 0,
|
||||
),
|
||||
],
|
||||
dataSets: dataSets,
|
||||
radarBackgroundColor: stringToColor("#343844").withOpacity(0.6),
|
||||
radarBorderData: BorderSide(
|
||||
color: themeController.currentColor.sc4, width: 0.5.rpx),
|
||||
radarShape: RadarShape.polygon,
|
||||
titlePositionPercentageOffset: 0.2,
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: AppConstants().normal_text_fontSize,
|
||||
color: themeController.currentColor.sc3),
|
||||
fontSize: AppConstants().normal_text_fontSize,
|
||||
color: themeController.currentColor.sc3,
|
||||
),
|
||||
getTitle: (index, angle) {
|
||||
return RadarChartTitle(text: data[index]['name'] ?? '未知'.tr);
|
||||
},
|
||||
|
||||
@@ -83,8 +83,13 @@ class _BreatheCardState extends State<BreatheCard>
|
||||
return Container();
|
||||
}
|
||||
|
||||
// List data = widget.sleepReport['brs'] ?? [];
|
||||
List data = widget.sleepReport['brs'] ?? [];
|
||||
|
||||
data = data.where((item) {
|
||||
return item['show'] != false; // 只保留 show 不为 false 的元素
|
||||
}).toList(); // 添加 .toList()
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -25,10 +25,11 @@ 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),
|
||||
|
||||
@@ -15,7 +15,8 @@ class HeartRateCard extends StatefulWidget {
|
||||
State<HeartRateCard> createState() => _HeartRateCardState();
|
||||
}
|
||||
|
||||
class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateMixin {
|
||||
class _HeartRateCardState extends State<HeartRateCard>
|
||||
with TickerProviderStateMixin {
|
||||
final GlobalKey _highlightKey = GlobalKey();
|
||||
AnimationController? _animationController;
|
||||
bool _shouldAnimate = false;
|
||||
@@ -30,9 +31,10 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
_shouldAnimate = true;
|
||||
_initAnimation();
|
||||
}
|
||||
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (widget.highlightItem != null && _highlightKey.currentContext != null) {
|
||||
if (widget.highlightItem != null &&
|
||||
_highlightKey.currentContext != null) {
|
||||
Scrollable.ensureVisible(
|
||||
_highlightKey.currentContext!,
|
||||
duration: Duration(milliseconds: 500),
|
||||
@@ -67,7 +69,7 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
_animationController!.forward();
|
||||
}
|
||||
|
||||
@@ -86,8 +88,16 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
return Container();
|
||||
}
|
||||
|
||||
// List data = widget.sleepReport['hrs'] ?? [];
|
||||
|
||||
// data = data.where((item) {
|
||||
// return item['show'] != false; // 只保留 show 不为 false 的元素
|
||||
// });
|
||||
List data = widget.sleepReport['hrs'] ?? [];
|
||||
|
||||
data = data.where((item) {
|
||||
return item['show'] != false; // 只保留 show 不为 false 的元素
|
||||
}).toList(); // 添加 .toList()
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
@@ -104,9 +114,9 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
children: List.generate(data.length, (index) {
|
||||
final item = data[index];
|
||||
item['showTip'] = true;
|
||||
final bool isHighlighted = _shouldAnimate &&
|
||||
item['id'] == _highlightedId;
|
||||
|
||||
final bool isHighlighted =
|
||||
_shouldAnimate && item['id'] == _highlightedId;
|
||||
|
||||
return SizedBox(
|
||||
width: (MediaQuery.of(context).size.width - 160.rpx) / 3,
|
||||
child: AnimatedBuilder(
|
||||
@@ -118,7 +128,8 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
? BoxDecoration(
|
||||
border: Border.all(
|
||||
color: themeController.currentColor.sc2
|
||||
.withOpacity(_animationController?.value ?? 0),
|
||||
.withOpacity(
|
||||
_animationController?.value ?? 0),
|
||||
width: 1.rpx,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@@ -138,4 +149,4 @@ class _HeartRateCardState extends State<HeartRateCard> with TickerProviderStateM
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,40 +842,7 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Obx(() {
|
||||
// if (sleepReportController.isLoading.value) {
|
||||
// return Center(
|
||||
// child: CircularProgressIndicator(
|
||||
// strokeWidth: 2,
|
||||
// valueColor: AlwaysStoppedAnimation<Color>(
|
||||
// themeController.currentColor.sc1,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// switch (sleepReportController.model.type) {
|
||||
// case 1:
|
||||
// return DailyDataWidget(
|
||||
// sleepReport,
|
||||
// sleepCardKey,
|
||||
// heartRateCardKey,
|
||||
// breatheCardKey,
|
||||
// widget.data);
|
||||
// case 2:
|
||||
// return WeekDataWidget(
|
||||
// sleepReport,
|
||||
// widget.data,
|
||||
// );
|
||||
// case 3:
|
||||
// return MonthDataWidget(
|
||||
// sleepReport,
|
||||
// widget.data,
|
||||
// );
|
||||
// default:
|
||||
// return NullDataWidget();
|
||||
// }
|
||||
// }),
|
||||
),
|
||||
Obx(() {
|
||||
if (sleepReportController.isLoading.value) {
|
||||
return Center(
|
||||
|
||||
Reference in New Issue
Block a user