Files
tuiche/lib/pages/sleep_report/qc_report/QcBreatheStandardWidget.dart
2026-04-02 16:02:25 +08:00

361 lines
13 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:EasyDartModule/EasyDartModule.dart' as es;
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
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/enum/APPPackageType.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
import 'package:vbvs_app/pages/sleep_report/chart/QcTimeSeriesChart.dart';
class QcBreatheStandardWidget extends StatefulWidget {
var reportData;
QcBreatheStandardWidget({super.key, required this.reportData});
@override
State<QcBreatheStandardWidget> createState() =>
_QcBreatheStandardWidgetState();
}
class _QcBreatheStandardWidgetState extends State<QcBreatheStandardWidget> {
@override
void setState(VoidCallback callback) {
super.setState(callback);
}
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
// 计算y轴的最大最小值
(double, double) _calculateYMinMax(List<QcTimeSeriesPoint> dataPoints) {
if (dataPoints.isEmpty) {
return (8.0, 20.0);
}
// 过滤掉无效数据点(值为-1的
final validPoints = dataPoints.where((point) => point.value >= 0).toList();
if (validPoints.isEmpty) {
return (8.0, 20.0);
}
// 找出数据中的实际最小值和最大值
double dataMin =
validPoints.map((point) => point.value).reduce((a, b) => a < b ? a : b);
double dataMax =
validPoints.map((point) => point.value).reduce((a, b) => a > b ? a : b);
// 计算最小值向下取整到5的倍数
double yMin = (dataMin / 5).floor() * 5.0;
// 如果最小值小于0设为0
if (yMin < 0) yMin = 0;
// 计算最大值向上取整到5的倍数
double yMax = (dataMax / 5).ceil() * 5.0;
// 确保至少有10的差值
if (yMax - yMin < 10) {
yMax = yMin + 10;
}
return (yMin, yMax);
}
@override
Widget build(BuildContext context) {
try {
if (widget.reportData == null || widget.reportData is! Map) {
return Container();
}
// 从reportData中获取br数据
Map<String, dynamic> brData = widget.reportData['br'] ?? {};
if (brData.isEmpty) {
return Container();
}
// 获取呼吸数据点
List<dynamic> dataList = brData['data'] ?? [];
List<QcTimeSeriesPoint> dataPoints = [];
// 构建数据点(只保留值,不需要时间戳)
for (int i = 0; i < dataList.length; i++) {
dynamic value = dataList[i];
if (value == null || value == '') {
dataPoints.add(QcTimeSeriesPoint(-1));
} else {
double y = (value as num).toDouble();
dataPoints.add(QcTimeSeriesPoint(y));
}
}
// 计算动态的y轴范围
final (yMin, yMax) = _calculateYMinMax(dataPoints);
// 构建呼吸统计数据
Map<String, dynamic> avgBreath = {
'name': '平均呼吸'.tr,
'value': brData['avg'].toInt() ?? 0,
'unit': '次/分',
};
Map<String, dynamic> baseBreath = {
'name': '基准呼吸'.tr,
'value': brData['base'].toInt() ?? 0,
'unit': '次/分',
};
Map<String, dynamic> minBreath = {
'name': '最低呼吸'.tr,
'value': brData['min'].toInt() ?? 0,
'unit': '次/分',
};
Map<String, dynamic> maxBreath = {
'name': '最高呼吸'.tr,
'value': brData['max'].toInt() ?? 0,
'unit': '次/分',
};
// 构建正常范围字符串
String range = '';
if (baseBreath['value'] != 0) {
int baseValue = baseBreath['value'];
range = '${baseValue - 3}~${baseValue + 3}';
} else {
range = '12~20';
}
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc5,
borderRadius:
BorderRadius.circular(AppConstants().normal_container_radius),
),
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"呼吸数据".tr,
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: AppConstants().title_text_fontSize),
),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: Colors.white,
padding: EdgeInsetsDirectional.fromSTEB(
14.rpx, 10.rpx, 14.rpx, 10.rpx),
borderRadius: 0.rpx,
onTap: () {
if (AppConstants().ent_type ==
APPPackageType.MHT.code) {
showTipDialog(
context,
Container(
child: Text(
"呼吸数据是指用户在睡眠过程中呼吸的基本数据,是评估睡眠呼吸质量、筛查睡眠呼吸障碍的核心指标。"
.tr,
style: TextStyle(
fontSize: 26.rpx,
color: Colors.black,
),
),
),
backgroundColor: Color(0xFFFFFFFF),
colors: [
Color(0XFF1592AA),
Color(0xFF0C83A7),
Color(0xFF006FA3)
],
);
} else {
showTipDialog(
context,
Container(
child: Text(
"呼吸数据是指用户在睡眠过程中呼吸的基本数据,是评估睡眠呼吸质量、筛查睡眠呼吸障碍的核心指标。"
.tr,
style: TextStyle(
fontSize: 26.rpx,
color: themeController.currentColor.sc3,
),
),
),
backgroundColor: themeController.currentColor.sc17,
colors: AppConstants().thNormalButton,
);
}
},
child: Container(
padding:
EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0.rpx, 0),
width: 28.rpx,
height: 28.rpx,
child: SvgPicture.asset(
'assets/img/icon/explain.svg',
fit: BoxFit.cover,
color: themeController.currentColor.sc4,
),
),
),
],
),
),
SizedBox(
height: 31.rpx,
),
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 30.rpx, 0.rpx),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
width: 14.rpx,
height: 14.rpx,
decoration: BoxDecoration(
color: themeController.currentColor.sc2,
shape: BoxShape.circle,
),
),
SizedBox(width: 15.rpx),
Text(
'正常范围 '.tr + range,
style: TextStyle(
fontSize: AppConstants().smaller_text_fontSize,
color: themeController.currentColor.sc3,
),
),
],
),
Container(
width: double.infinity,
child: QcTimeSeriesChart(
yMin: yMin,
yMax: yMax,
dataPoints: dataPoints,
xSegmentCount: 11,
baseValue: brData['base'].toDouble(), // 传入基准值
// baseValue: 16, // 传入基准值
baseLabel: '基准', // 可选的自定义标签
yAxisPadding: 0,
),
),
].divide(SizedBox(
height: 18.rpx,
)),
),
),
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0.rpx, 0.rpx, 0.rpx, 0.rpx),
child: Row(
children: [
_buildBreathItem(avgBreath,
valueColor: stringToColor("#00C1AA")),
_buildBreathItem(baseBreath,
valueColor: stringToColor("#FF7159")),
_buildBreathItem(minBreath,
valueColor: stringToColor("#E3AFDD")),
_buildBreathItem(maxBreath,
valueColor: stringToColor("#FF9F66")),
],
),
),
],
),
),
);
} catch (e) {
es.EasyDartModule.logger.error("呼吸基准监测绘制异常${e}");
return Container();
}
}
Widget _buildBreathItem(Map<String, dynamic> data, {Color? valueColor}) {
return Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 4.rpx, vertical: 4.rpx),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Flexible(
child: Text(
"${data['name']}",
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
SizedBox(height: 4.rpx),
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
fit: FlexFit.loose,
child: Text(
"${data['value']}",
style: TextStyle(
color: valueColor ?? themeController.currentColor.sc2,
fontSize: AppConstants().normal_text_fontSize,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
SizedBox(width: 6.rpx),
Flexible(
fit: FlexFit.loose,
child: Text(
"${data['unit']}",
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: AppConstants().small_text_fontSize,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),
),
],
),
),
],
),
),
);
}
}