更新快检报告

This commit is contained in:
wyf
2026-04-02 16:02:25 +08:00
parent 9a431e907e
commit bdeef22130
12 changed files with 2080 additions and 732 deletions

Binary file not shown.

View File

@@ -8,8 +8,8 @@ import 'package:vbvs_app/enum/APPPackageType.dart';
class AppConstants { class AppConstants {
// App-related constants // App-related constants
static const String zhmht_app_version = "SWES_1.2026.3.30"; //眠花糖 static const String zhmht_app_version = "SWES_1.2026.04.02"; //眠花糖
static const String theh_app_version = "1.2603.30"; //太和 static const String theh_app_version = "1.2604.02"; //太和
// 1. 纯字符串列表格式 // 1. 纯字符串列表格式
static const List<String> integerTimeZones = [ static const List<String> integerTimeZones = [
@@ -91,8 +91,8 @@ class AppConstants {
//系统参数 //系统参数
//运行打包APP模式 //运行打包APP模式
// int ent_type = APPPackageType.MHT.code; //1.默认太和 2.欢睡 3.眠花糖 int ent_type = APPPackageType.MHT.code; //1.默认太和 2.欢睡 3.眠花糖
int ent_type = APPPackageType.TH.code; //1.默认太和 2.欢睡 3.眠花糖 // int ent_type = APPPackageType.TH.code; //1.默认太和 2.欢睡 3.眠花糖
// int ent_type = APPPackageType.HUANSHUI.code; //1.默认太和 2.欢睡 3.眠花糖 // int ent_type = APPPackageType.HUANSHUI.code; //1.默认太和 2.欢睡 3.眠花糖
// int ent_type = APPPackageType.DONGHUA.code; //1.默认太和 2.欢睡 3.眠花糖 4.东华 // int ent_type = APPPackageType.DONGHUA.code; //1.默认太和 2.欢睡 3.眠花糖 4.东华
// int ent_type = APPPackageType.HAIER.code; //1.默认太和 2.欢睡 3.眠花糖 4.东华 5.海尔沃棣 // int ent_type = APPPackageType.HAIER.code; //1.默认太和 2.欢睡 3.眠花糖 4.东华 5.海尔沃棣

View File

@@ -106,27 +106,31 @@ class DeviceTypeController extends GetControllerEx<DeviceTypeModel> {
} }
Future<bool> checkReportStatus(String mac) async { Future<bool> checkReportStatus(String mac) async {
String serviceAddress = ServiceConstant.qc_service_address;
String serviceApi = ServiceConstant.checkQuickStatus;
String queryUrl = "$serviceAddress$serviceApi?mac=${mac}";
bool flag = false; bool flag = false;
await requestWithLog( try {
logTitle: "查询快检状态", String serviceAddress = ServiceConstant.qc_service_address;
method: MyHttpMethod.get, String serviceApi = ServiceConstant.checkQuickStatus;
queryUrl: queryUrl, String queryUrl = "$serviceAddress$serviceApi?mac=${mac}";
onSuccess: (res) { await requestWithLog(
flag = res.data['status'] != 200; logTitle: "查询快检状态",
experience_status.value = res.data['status']; method: MyHttpMethod.get,
experience_percent.value = res.data['per'] ?? 0; queryUrl: queryUrl,
experience_id.value = res.data['id'] ?? ""; onSuccess: (res) {
own.value = res.data['own'] ?? false; flag = res.data['status'] != 200;
updateAll(); experience_status.value = res.data['status'];
}, experience_percent.value = res.data['per'] ?? 0;
onFailure: (res) { experience_id.value = res.data['id'] ?? "";
flag = false; own.value = res.data['own'] ?? false;
experience_status.value = -1; updateAll();
}, },
); onFailure: (res) {
flag = false;
experience_status.value = -1;
},
);
} catch (e) {
print("查询快检状态出错: $e");
}
return flag; return flag;
} }
@@ -299,6 +303,9 @@ class DeviceTypeController extends GetControllerEx<DeviceTypeModel> {
flag = true; flag = true;
}, },
onFailure: (res) { onFailure: (res) {
NewTopSlideNotification.show(
text:res.msg ?? "请求失败".tr,
textColor: themeController.currentColor.sc9);
flag = false; flag = false;
}, },
); );

View File

@@ -698,7 +698,7 @@ void initEasyDartModule() {
EasyDartModule.init( EasyDartModule.init(
loggerConfig: LoggerConfig( loggerConfig: LoggerConfig(
host: "https://zhmht.swes.com.cn:27020/vsbs_log", host: "https://zhmht.swes.com.cn:27020/vsbs_log",
serviceName: "太和e护test2026-03-30"), serviceName: "太和e护test2026-04-01"),
webSocketConfig: webSocketConfig:
WebSocketConfig(ServiceConstant.webSocketService, (data) { WebSocketConfig(ServiceConstant.webSocketService, (data) {
// 接收到服务消息 // 接收到服务消息

View File

@@ -0,0 +1,92 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import 'package:flutter/services.dart';
class SpeedControlledGif extends StatefulWidget {
final String assetPath;
final double speedFactor;
final BoxFit? fit;
/// [speedFactor] 播放速度倍数默认1.0
/// 大于1表示加速播放小于1表示减速播放
const SpeedControlledGif(
this.assetPath, {
Key? key,
this.speedFactor = 1.0,
this.fit,
}) : super(key: key);
@override
_SpeedControlledGifState createState() => _SpeedControlledGifState();
}
class _SpeedControlledGifState extends State<SpeedControlledGif> {
ui.Codec? _codec;
ui.FrameInfo? _currentFrame;
Timer? _timer;
bool _isDisposed = false;
@override
void initState() {
super.initState();
_loadGif();
}
Future<void> _loadGif() async {
final data = await rootBundle.load(widget.assetPath);
_codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
if (_isDisposed) return;
_showNextFrame();
}
Future<void> _showNextFrame() async {
if (_isDisposed || _codec == null) return;
_currentFrame = await _codec!.getNextFrame();
if (_isDisposed) return;
if (mounted) {
setState(() {});
}
// 取当前帧持续时间,单位毫秒
final baseDuration = _currentFrame?.duration.inMilliseconds ?? 100;
// 限制最小帧间隔,防止刷新过快
const minFrameDuration = 50;
// 计算实际播放帧间隔speedFactor越大速度越快
final adjustedDuration = (baseDuration / widget.speedFactor)
.round()
.clamp(minFrameDuration, 10000);
_timer = Timer(Duration(milliseconds: adjustedDuration), _showNextFrame);
}
@override
void dispose() {
_isDisposed = true;
_timer?.cancel();
_codec?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_currentFrame == null) {
// 加载中或无帧时显示空容器或占位
return Container();
}
return RawImage(
image: _currentFrame!.image,
fit: widget.fit,
);
}
}

View File

@@ -0,0 +1,63 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:vbvs_app/controller/device/device_type_controller.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
DeviceTypeController deviceTypeController = Get.find();
String getGenderText(dynamic gender) {
var genderMap = {
'1': ''.tr,
'2': ''.tr,
};
String genderStr = gender.toString().trim();
return genderMap[genderStr] ?? '-'.tr;
}
// 显示解绑确认对话框
void showCancelConfirmDialog(BuildContext context, personInfo) {
showConfirmDialog(
context,
Container(),
"是否确认结束?".tr,
onConfirm: () async {
bool opRes = await deviceTypeController.qcCheckControl(personInfo, 2);
if (!opRes) {
return;
}
deviceTypeController.experience_status.value = 404;
deviceTypeController.updateAll();
},
onCancel: () {},
);
}
String calculateAge(String birthdayStr) {
try {
// 解析生日字符串 (格式: yyyy/MM/dd)
List<String> parts = birthdayStr.trim().split('/');
if (parts.length != 3) return '-'.tr;
int year = int.parse(parts[0]);
int month = int.parse(parts[1]);
int day = int.parse(parts[2]);
DateTime birthDate = DateTime(year, month, day);
DateTime today = DateTime.now();
// 计算年龄
int age = today.year - birthDate.year;
// 如果今年还没过生日年龄减1
if (today.month < birthDate.month ||
(today.month == birthDate.month && today.day < birthDate.day)) {
age--;
}
return age.toString();
} catch (e) {
return '-'.tr;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
import 'dart:ui'; import 'dart:ui';
import 'dart:io'; // 添加这个导入
import 'package:ef/ef.dart'; import 'package:ef/ef.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -22,6 +23,18 @@ class _MattressControlPageState extends State<MattressControlPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// 如果是 iOS 平台,使用 PopScope 禁用左滑返回
if (Platform.isIOS) {
return PopScope(
canPop: false, // 禁用返回手势
child: buildContent(),
);
}
return buildContent();
}
// 将原有的 build 内容提取到这个方法中
Widget buildContent() {
return LayoutBuilder( return LayoutBuilder(
builder: (context, bodySize) => GestureDetector( builder: (context, bodySize) => GestureDetector(
child: Obx(() { child: Obx(() {
@@ -73,4 +86,4 @@ class _MattressControlPageState extends State<MattressControlPage> {
class ControlCardController extends GetxController { class ControlCardController extends GetxController {
final List<RxBool> switchStates = List.generate(9, (index) => false.obs); final List<RxBool> switchStates = List.generate(9, (index) => false.obs);
} }

View File

@@ -14,10 +14,12 @@
// class QcTimeSeriesChart extends StatelessWidget { // class QcTimeSeriesChart extends StatelessWidget {
// final List<QcTimeSeriesPoint> dataPoints; // final List<QcTimeSeriesPoint> dataPoints;
// final double yMin; // final double yMin;
// final double yMax; // final double yMax; // 注意:这个值在使用时会自动加 padding
// final int xSegmentCount; // final int xSegmentCount;
// final double? baseValue; // 基准值,可选 // final double? baseValue; // 基准值,可选
// final String? baseLabel; // 基准值标签,可选 // final String? baseLabel; // 基准值标签,可选
// final double yAxisPadding; // Y轴顶部留白默认为20
// final double yAxisMargin; // Y轴上下边距默认为2
// const QcTimeSeriesChart({ // const QcTimeSeriesChart({
// Key? key, // Key? key,
@@ -27,16 +29,21 @@
// this.xSegmentCount = 11, // this.xSegmentCount = 11,
// this.baseValue, // this.baseValue,
// this.baseLabel, // this.baseLabel,
// this.yAxisPadding = 20.0, // 默认为20
// this.yAxisMargin = 2.0, // Y轴上下边距默认为2
// }) : super(key: key); // }) : super(key: key);
// // 添加一个 getter 来获取实际使用的 yMax自动加 padding
// double get _actualYMax => yMax + yAxisPadding;
// int get _dataPointCount => dataPoints.length; // int get _dataPointCount => dataPoints.length;
// List<double> _generateYAxisTicks() { // List<double> _generateYAxisTicks() {
// if (yMin >= yMax) { // if (yMin >= _actualYMax) {
// return [0, 20, 40, 60, 80, 100]; // return [0, 20, 40, 60, 80, 100];
// } // }
// double step = (yMax - yMin) / 5; // double step = (_actualYMax - yMin) / 5;
// List<double> ticks = []; // List<double> ticks = [];
// for (int i = 0; i <= 5; i++) { // for (int i = 0; i <= 5; i++) {
@@ -114,7 +121,7 @@
// Widget build(BuildContext context) { // Widget build(BuildContext context) {
// // final yTicks = _generateYAxisTicks(); // // final yTicks = _generateYAxisTicks();
// final double xMax = _dataPointCount.toDouble(); // final double xMax = _dataPointCount.toDouble();
// final double yRange = yMax - yMin; // final double yRange = _actualYMax - yMin;
// final double yInterval = yRange > 0 ? yRange / 5 : 20.0; // final double yInterval = yRange > 0 ? yRange / 5 : 20.0;
// final tickIndices = _getTickIndices(); // final tickIndices = _getTickIndices();
@@ -229,8 +236,8 @@
// LineChartData( // LineChartData(
// minX: 1, // minX: 1,
// maxX: xMax + 0.5, // maxX: xMax + 0.5,
// minY: yMin - 2, // minY: yMin - yAxisMargin,
// maxY: yMax + 2, // maxY: _actualYMax + yAxisMargin,
// gridData: FlGridData(show: false), // gridData: FlGridData(show: false),
// extraLinesData: ExtraLinesData( // extraLinesData: ExtraLinesData(
// horizontalLines: horizontalLines, // horizontalLines: horizontalLines,
@@ -333,7 +340,7 @@
// // 使用 Positioned 来绘制最大值和最小值的标签 // // 使用 Positioned 来绘制最大值和最小值的标签
// if (minMaxData['minIndex'] != -1 || minMaxData['maxIndex'] != -1) // if (minMaxData['minIndex'] != -1 || minMaxData['maxIndex'] != -1)
// _buildMinMaxLabels( // _buildMinMaxLabels(
// context, constraints, minMaxData, xMax, yMin, yMax), // context, constraints, minMaxData, xMax, yMin, _actualYMax),
// ], // ],
// ); // );
// }, // },
@@ -360,11 +367,11 @@
// double chartHeight = constraints.maxHeight - topPadding - bottomPadding; // double chartHeight = constraints.maxHeight - topPadding - bottomPadding;
// // X轴范围1 到 xMax // // X轴范围1 到 xMax
// // Y轴范围yMin - 2 到 yMax + 2 // // Y轴范围yMin - yAxisMargin 到 yMax + yAxisMargin
// double xMin = 1; // double xMin = 1;
// double xMaxValue = xMax; // double xMaxValue = xMax;
// double yMinValue = yMin - 2; // double yMinValue = yMin - yAxisMargin;
// double yMaxValue = yMax + 2; // double yMaxValue = yMax + yAxisMargin;
// double xRange = xMaxValue - xMin; // double xRange = xMaxValue - xMin;
// double yRange = yMaxValue - yMinValue; // double yRange = yMaxValue - yMinValue;
@@ -400,14 +407,12 @@
// children: [ // children: [
// // SVG图片作为背景 // // SVG图片作为背景
// SvgPicture.asset( // SvgPicture.asset(
// 'assets/img/icon/location.svg', // 替换为你的SVG图片路径 // 'assets/img/icon/location.svg',
// // width: 28.rpx,
// // height: 40.rpx,
// fit: BoxFit.contain, // fit: BoxFit.contain,
// color: stringToColor("#d69dd2"), // color: stringToColor("#d69dd2"),
// ), // ),
// Padding( // Padding(
// padding: EdgeInsets.only(bottom: 12.rpx), // 调整这个值来控制向上移动的距离 // padding: EdgeInsets.only(bottom: 12.rpx),
// child: Text( // child: Text(
// '${minMaxData['minValue'].toStringAsFixed(0)}', // '${minMaxData['minValue'].toStringAsFixed(0)}',
// style: TextStyle( // style: TextStyle(
@@ -453,23 +458,12 @@
// children: [ // children: [
// // SVG图片作为背景 // // SVG图片作为背景
// SvgPicture.asset( // SvgPicture.asset(
// 'assets/img/icon/location.svg', // 替换为你的SVG图片路径 // 'assets/img/icon/location.svg',
// // width: 28.rpx,
// // height: 40.rpx,
// fit: BoxFit.contain, // fit: BoxFit.contain,
// color: stringToColor("#FF9F66"), // color: stringToColor("#FF9F66"),
// ), // ),
// // 文字覆盖在SVG上
// // Text(
// // '${minMaxData['maxValue'].toStringAsFixed(0)}',
// // style: TextStyle(
// // color: Colors.white,
// // fontSize: AppConstants().smaller_text_fontSize,
// // // fontWeight: FontWeight.bold,
// // ),
// // ),
// Padding( // Padding(
// padding: EdgeInsets.only(bottom: 12.rpx), // 调整这个值来控制向上移动的距离 // padding: EdgeInsets.only(bottom: 12.rpx),
// child: Text( // child: Text(
// '${minMaxData['maxValue'].toStringAsFixed(0)}', // '${minMaxData['maxValue'].toStringAsFixed(0)}',
// style: TextStyle( // style: TextStyle(
@@ -512,6 +506,7 @@ class QcTimeSeriesChart extends StatelessWidget {
final double? baseValue; // 基准值,可选 final double? baseValue; // 基准值,可选
final String? baseLabel; // 基准值标签,可选 final String? baseLabel; // 基准值标签,可选
final double yAxisPadding; // Y轴顶部留白默认为20 final double yAxisPadding; // Y轴顶部留白默认为20
final double yAxisMargin; // Y轴上下边距默认为2
const QcTimeSeriesChart({ const QcTimeSeriesChart({
Key? key, Key? key,
@@ -522,6 +517,7 @@ class QcTimeSeriesChart extends StatelessWidget {
this.baseValue, this.baseValue,
this.baseLabel, this.baseLabel,
this.yAxisPadding = 20.0, // 默认为20 this.yAxisPadding = 20.0, // 默认为20
this.yAxisMargin = 2.0, // Y轴上下边距默认为2
}) : super(key: key); }) : super(key: key);
// 添加一个 getter 来获取实际使用的 yMax自动加 padding // 添加一个 getter 来获取实际使用的 yMax自动加 padding
@@ -618,6 +614,10 @@ class QcTimeSeriesChart extends StatelessWidget {
final tickIndices = _getTickIndices(); final tickIndices = _getTickIndices();
final minMaxData = _findMinMax(); final minMaxData = _findMinMax();
// 计算实际显示的Y轴范围
final double actualMinY = yMin - yAxisMargin;
final double actualMaxY = _actualYMax + yAxisMargin;
// 将数据点分割成多个连续段 // 将数据点分割成多个连续段
List<List<FlSpot>> lineSegments = []; List<List<FlSpot>> lineSegments = [];
List<FlSpot> currentSegment = []; List<FlSpot> currentSegment = [];
@@ -727,8 +727,8 @@ class QcTimeSeriesChart extends StatelessWidget {
LineChartData( LineChartData(
minX: 1, minX: 1,
maxX: xMax + 0.5, maxX: xMax + 0.5,
minY: yMin - 2, minY: actualMinY,
maxY: _actualYMax + 2, maxY: actualMaxY,
gridData: FlGridData(show: false), gridData: FlGridData(show: false),
extraLinesData: ExtraLinesData( extraLinesData: ExtraLinesData(
horizontalLines: horizontalLines, horizontalLines: horizontalLines,
@@ -776,6 +776,19 @@ class QcTimeSeriesChart extends StatelessWidget {
reservedSize: 60.rpx, reservedSize: 60.rpx,
interval: yInterval, interval: yInterval,
getTitlesWidget: (value, meta) { getTitlesWidget: (value, meta) {
// 不显示最小值和最大值
// 判断是否是实际显示范围的最小值或最大值
if (value == actualMinY || value == actualMaxY) {
return const SizedBox.shrink();
}
// 检查值是否为整数(避免显示小数)
double roundedValue = value.roundToDouble();
if ((value - roundedValue).abs() > 0.01) {
// 如果不是整数,不显示
return const SizedBox.shrink();
}
return Padding( return Padding(
padding: const EdgeInsets.only(right: 8.0), padding: const EdgeInsets.only(right: 8.0),
child: Text( child: Text(
@@ -858,11 +871,11 @@ class QcTimeSeriesChart extends StatelessWidget {
double chartHeight = constraints.maxHeight - topPadding - bottomPadding; double chartHeight = constraints.maxHeight - topPadding - bottomPadding;
// X轴范围1 到 xMax // X轴范围1 到 xMax
// Y轴范围yMin - 2 到 yMax + 2 // Y轴范围yMin - yAxisMargin 到 yMax + yAxisMargin
double xMin = 1; double xMin = 1;
double xMaxValue = xMax; double xMaxValue = xMax;
double yMinValue = yMin - 2; double yMinValue = yMin - yAxisMargin;
double yMaxValue = yMax + 2; double yMaxValue = yMax + yAxisMargin;
double xRange = xMaxValue - xMin; double xRange = xMaxValue - xMin;
double yRange = yMaxValue - yMinValue; double yRange = yMaxValue - yMinValue;

View File

@@ -110,19 +110,19 @@ class _QcBreatheStandardWidgetState extends State<QcBreatheStandardWidget> {
}; };
Map<String, dynamic> baseBreath = { Map<String, dynamic> baseBreath = {
'name': '呼吸', 'name': '呼吸'.tr,
'value': brData['base'].toInt() ?? 0, 'value': brData['base'].toInt() ?? 0,
'unit': '次/分', 'unit': '次/分',
}; };
Map<String, dynamic> minBreath = { Map<String, dynamic> minBreath = {
'name': '最低呼吸', 'name': '最低呼吸'.tr,
'value': brData['min'].toInt() ?? 0, 'value': brData['min'].toInt() ?? 0,
'unit': '次/分', 'unit': '次/分',
}; };
Map<String, dynamic> maxBreath = { Map<String, dynamic> maxBreath = {
'name': '最高呼吸', 'name': '最高呼吸'.tr,
'value': brData['max'].toInt() ?? 0, 'value': brData['max'].toInt() ?? 0,
'unit': '次/分', 'unit': '次/分',
}; };
@@ -260,6 +260,7 @@ class _QcBreatheStandardWidgetState extends State<QcBreatheStandardWidget> {
// baseValue: 16, // 传入基准值 // baseValue: 16, // 传入基准值
baseLabel: '基准', // 可选的自定义标签 baseLabel: '基准', // 可选的自定义标签
yAxisPadding: 0, yAxisPadding: 0,
), ),
), ),
].divide(SizedBox( ].divide(SizedBox(

View File

@@ -267,7 +267,8 @@ class _QcHeartRateStandardWidgetState extends State<QcHeartRateStandardWidget> {
baseValue: hrData['base'].toDouble(), // 传入基准值 baseValue: hrData['base'].toDouble(), // 传入基准值
// baseValue: 65, // 传入基准值 // baseValue: 65, // 传入基准值
baseLabel: '基准', // 可选的自定义标签 baseLabel: '基准', // 可选的自定义标签
yAxisPadding: 20, yAxisPadding: 0,
yAxisMargin: 5,
), ),
), ),
), ),

View File

@@ -92,6 +92,7 @@ dependencies:
network_info_plus: ^5.0.1 network_info_plus: ^5.0.1
# wifi_info_flutter: ^2.0.2 # wifi_info_flutter: ^2.0.2
shelf_static: ^1.1.3 shelf_static: ^1.1.3
vibration: ^3.1.8
dev_dependencies: dev_dependencies: