日报周报月报
This commit is contained in:
@@ -208,5 +208,9 @@
|
|||||||
"解除分享": "解除分享",
|
"解除分享": "解除分享",
|
||||||
"最高分": "最高分",
|
"最高分": "最高分",
|
||||||
"最低分": "最低分",
|
"最低分": "最低分",
|
||||||
"本周平均分":"本周平均分"
|
"本周平均分":"本周平均分",
|
||||||
|
"本月平均分":"本月平均分",
|
||||||
|
"每日得分":"每日得分",
|
||||||
|
"每日得分介绍":"每日得分介绍",
|
||||||
|
"与上月对比":"与上月对比"
|
||||||
}
|
}
|
||||||
@@ -18,11 +18,6 @@ class _TestWidgetState extends State<NullDataWidget> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
ThemeController themeController = Get.find();
|
ThemeController themeController = Get.find();
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
child: Scaffold(
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
key: scaffoldKey,
|
|
||||||
body: SafeArea(
|
|
||||||
top: true,
|
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
@@ -31,7 +26,7 @@ class _TestWidgetState extends State<NullDataWidget> {
|
|||||||
height: MediaQuery.sizeOf(context).height * 1,
|
height: MediaQuery.sizeOf(context).height * 1,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0),
|
padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0),
|
||||||
@@ -69,8 +64,6 @@ class _TestWidgetState extends State<NullDataWidget> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ getOnePickers(
|
|||||||
? const Color(0xFF011D33) // ✅ 选中项颜色
|
? const Color(0xFF011D33) // ✅ 选中项颜色
|
||||||
: Color(0xFF9AA0B3), // 未选中颜色
|
: Color(0xFF9AA0B3), // 未选中颜色
|
||||||
fontSize: 30.rpx,
|
fontSize: 30.rpx,
|
||||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -197,7 +197,8 @@ Future showDateSelectionDialog(BuildContext context,
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text("确定",
|
child: Text("确定",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30.rpx, color: stringToColor("#84F5FF"))),
|
fontSize: 30.rpx,
|
||||||
|
color: stringToColor("#84F5FF"))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -744,7 +745,8 @@ Future showDayTimeSelectionDialog(
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text("确定".tr,
|
child: Text("确定".tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30.rpx, color: stringToColor("#84F5FF"))),
|
fontSize: 30.rpx,
|
||||||
|
color: stringToColor("#84F5FF"))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -881,7 +883,8 @@ Future showOneSelectionDialog(
|
|||||||
height: 60.rpx,
|
height: 60.rpx,
|
||||||
child: Text("确定".tr,
|
child: Text("确定".tr,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 30.rpx, color: stringToColor("#84F5FF"))),
|
fontSize: 30.rpx,
|
||||||
|
color: stringToColor("#84F5FF"))),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:ef/ef.dart';
|
import 'package:ef/ef.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vbvs_app/component/NullDataComponentWidget.dart';
|
||||||
import 'package:vbvs_app/pages/common/bezier_bottom_navigation_bar.dart';
|
import 'package:vbvs_app/pages/common/bezier_bottom_navigation_bar.dart';
|
||||||
import 'package:vbvs_app/pages/mh_page/MattressControl.dart';
|
import 'package:vbvs_app/pages/mh_page/MattressControl.dart';
|
||||||
import 'package:vbvs_app/pages/mh_page/homepage/mht_sleep_report_page.dart';
|
import 'package:vbvs_app/pages/mh_page/homepage/mht_sleep_report_page.dart';
|
||||||
@@ -48,6 +49,7 @@ class _HomePageState extends State<MainPageBBottomChange>
|
|||||||
|
|
||||||
final List<Widget> pages = [
|
final List<Widget> pages = [
|
||||||
NewHomePage(),
|
NewHomePage(),
|
||||||
|
// NullDataWidget(),
|
||||||
// PeopleInfoPage(),
|
// PeopleInfoPage(),
|
||||||
// Text('报告'),
|
// Text('报告'),
|
||||||
// RegisterPage(),
|
// RegisterPage(),
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class _BluetoothPageState extends State<BluetoothPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return LayoutBuilder(builder: (context, cc) {
|
return LayoutBuilder(builder: (context, cc) {
|
||||||
bodysize = cc;
|
bodysize = cc;
|
||||||
|
final isBind = obsData['bind_type'] == 1;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => FocusScope.of(context).unfocus(),
|
onTap: () => FocusScope.of(context).unfocus(),
|
||||||
child: Container(
|
child: Container(
|
||||||
@@ -158,7 +159,6 @@ class _BluetoothPageState extends State<BluetoothPage> {
|
|||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// 按钮列表
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
|
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
|
||||||
@@ -166,6 +166,7 @@ class _BluetoothPageState extends State<BluetoothPage> {
|
|||||||
_buildMenuButton(
|
_buildMenuButton(
|
||||||
context, '详情', "/devicePeopleInfo",
|
context, '详情', "/devicePeopleInfo",
|
||||||
arguments: obsData),
|
arguments: obsData),
|
||||||
|
if (isBind) ...[
|
||||||
_buildMenuButton(
|
_buildMenuButton(
|
||||||
context,
|
context,
|
||||||
'人员资料',
|
'人员资料',
|
||||||
@@ -183,6 +184,7 @@ class _BluetoothPageState extends State<BluetoothPage> {
|
|||||||
_buildMenuButton(
|
_buildMenuButton(
|
||||||
context, '分享设备', "/deviceSharePage",
|
context, '分享设备', "/deviceSharePage",
|
||||||
arguments: obsData),
|
arguments: obsData),
|
||||||
|
],
|
||||||
_buildMenuButton(
|
_buildMenuButton(
|
||||||
context,
|
context,
|
||||||
obsData['bind_type'] == 1 ? '解绑' : '删除',
|
obsData['bind_type'] == 1 ? '解绑' : '删除',
|
||||||
@@ -243,6 +245,104 @@ class _BluetoothPageState extends State<BluetoothPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Expanded(
|
||||||
|
// child: Align(
|
||||||
|
// alignment: Alignment.bottomCenter,
|
||||||
|
// child: SingleChildScrollView(
|
||||||
|
// reverse: true, // 👈 optional,如果你想要滚动时内容从底部弹出
|
||||||
|
// child: Column(
|
||||||
|
// crossAxisAlignment:
|
||||||
|
// CrossAxisAlignment.stretch,
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
|
||||||
|
// children: [
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context, '详情', "/devicePeopleInfo",
|
||||||
|
// arguments: obsData),
|
||||||
|
// if (isBind) ...[
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context,
|
||||||
|
// '人员资料',
|
||||||
|
// "/peopleInfoPage",
|
||||||
|
// arguments: obsData,
|
||||||
|
// ),
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context, '房间选择', "/roomPickerPage",
|
||||||
|
// arguments: obsData),
|
||||||
|
// _buildMenuButton(context, '设备校准', ""),
|
||||||
|
// _buildMenuButton(context, '体征传感器', ""),
|
||||||
|
// _buildMenuButton(context, 'WIFI配置', ""),
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context, '睡眠习惯', "/sleepHabitPage"),
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context, '分享设备', "/deviceSharePage",
|
||||||
|
// arguments: obsData),
|
||||||
|
// ],
|
||||||
|
// _buildMenuButton(
|
||||||
|
// context,
|
||||||
|
// obsData['bind_type'] == 1 ? '解绑' : '删除',
|
||||||
|
// "",
|
||||||
|
// onTap: () {
|
||||||
|
// if (obsData['bind_type'] == 1) {
|
||||||
|
// // 解绑弹窗
|
||||||
|
// showUnbindConfirmDialog(
|
||||||
|
// context: context,
|
||||||
|
// title: "是否进行解绑?",
|
||||||
|
// onConfirm: () async {
|
||||||
|
// await deviceListController
|
||||||
|
// .unbindDevice(obsData);
|
||||||
|
// await deviceListController
|
||||||
|
// .getDeviceList();
|
||||||
|
// MHTHomeController homeController =
|
||||||
|
// Get.find();
|
||||||
|
// homeController
|
||||||
|
// .selectDevcie.value = "";
|
||||||
|
// try {
|
||||||
|
// WebviewTestController
|
||||||
|
// webviewTestController =
|
||||||
|
// Get.find();
|
||||||
|
// webviewTestController
|
||||||
|
// .web.jsbridge?.dart
|
||||||
|
// .unBindDevice();
|
||||||
|
// } catch (e) {
|
||||||
|
// ef.log("[h5]通知列表更新报错:$e");
|
||||||
|
// }
|
||||||
|
// Get.toNamed(
|
||||||
|
// "/mianPageBottomChange");
|
||||||
|
// // 执行解绑逻辑
|
||||||
|
// },
|
||||||
|
// onCancel: () {
|
||||||
|
// // 点击取消后的逻辑
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// } else if (obsData['bind_type'] == 2) {
|
||||||
|
// // 删除弹窗
|
||||||
|
// showDeleteDeviceConfirmDialog(
|
||||||
|
// context: context,
|
||||||
|
// title: "是否进行删除?",
|
||||||
|
// onConfirm: () async {
|
||||||
|
// await deviceListController
|
||||||
|
// .unbindDevice(
|
||||||
|
// obsData,
|
||||||
|
// );
|
||||||
|
// await deviceListController
|
||||||
|
// .getDeviceList();
|
||||||
|
// Get.toNamed(
|
||||||
|
// "/mianPageBottomChange");
|
||||||
|
// },
|
||||||
|
// onCancel: () {
|
||||||
|
// // 点击取消后的逻辑
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
|
|||||||
191
lib/pages/mh_page/component/easychart.dart
Normal file
191
lib/pages/mh_page/component/easychart.dart
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import 'package:ef/ef.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:ef/base/chart/drawer.dart';
|
||||||
|
import 'package:ef/base/chart/easychart.dart';
|
||||||
|
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||||
|
import 'package:vbvs_app/common/util/MyUtils.dart';
|
||||||
|
|
||||||
|
enum ChartDisplayMode { line, bar, both, dualBar }
|
||||||
|
|
||||||
|
class LineView extends StatelessWidget {
|
||||||
|
final List<ChartLables> xLabels;
|
||||||
|
final List<ChartLables> yLabels;
|
||||||
|
final double barWidth;
|
||||||
|
final int xCount;
|
||||||
|
final int yCount;
|
||||||
|
final List<Offset> points;
|
||||||
|
final ChartDisplayMode displayMode;
|
||||||
|
final List<String>? barColors;
|
||||||
|
final List<List<Offset>>? dualBarPoints;
|
||||||
|
final String? xUnit;
|
||||||
|
final List<String> tips;
|
||||||
|
final double bottomPadding;
|
||||||
|
const LineView({
|
||||||
|
super.key,
|
||||||
|
required this.xLabels,
|
||||||
|
required this.yLabels,
|
||||||
|
required this.xCount,
|
||||||
|
required this.yCount,
|
||||||
|
required this.points,
|
||||||
|
required this.barWidth,
|
||||||
|
required this.tips,
|
||||||
|
this.xUnit = '',
|
||||||
|
this.bottomPadding = 100,
|
||||||
|
this.displayMode = ChartDisplayMode.bar,
|
||||||
|
this.barColors, // 添加进构造函数
|
||||||
|
this.dualBarPoints,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(builder: (a, b) {
|
||||||
|
return Container(
|
||||||
|
alignment: Alignment.topCenter,
|
||||||
|
margin: EdgeInsets.only(
|
||||||
|
left: 43.rpx,
|
||||||
|
bottom: bottomPadding.rpx,
|
||||||
|
),
|
||||||
|
child: EasyChartView(
|
||||||
|
size: Size(b.maxWidth, 220.rpx),
|
||||||
|
drawer: ChartDrawer(
|
||||||
|
xAxis: ChartAxis(
|
||||||
|
intent: 44,
|
||||||
|
entintent: 15,
|
||||||
|
labels: xLabels,
|
||||||
|
count: xCount,
|
||||||
|
),
|
||||||
|
yAxis: ChartAxis(
|
||||||
|
color: Colors.transparent,
|
||||||
|
isX: false,
|
||||||
|
intent: 0,
|
||||||
|
entintent: 45.rpx.toInt(),
|
||||||
|
labels: yLabels,
|
||||||
|
count: yCount,
|
||||||
|
),
|
||||||
|
ondrawer: (canvas, size, drawer) {
|
||||||
|
canvas.scale(1, -1);
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
xUnit!,
|
||||||
|
Offset(drawer.yAxis.labels.first.offset.dx,
|
||||||
|
-drawer.drawsize.height + 0),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
canvas.scale(1, -1);
|
||||||
|
// 虚线
|
||||||
|
for (var i = 1;
|
||||||
|
i < drawer.yAxis.labels.first.labels.length;
|
||||||
|
i++) {
|
||||||
|
drawer.drawHorizontalDashedLine(
|
||||||
|
canvas,
|
||||||
|
drawer.yAxis.labels.first.steps[i].dy,
|
||||||
|
drawer.drawsize.width,
|
||||||
|
dashWidth: 3,
|
||||||
|
dashSpace: 3,
|
||||||
|
paint: Paint()..color = Colors.grey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayMode == ChartDisplayMode.line ||
|
||||||
|
displayMode == ChartDisplayMode.both) {
|
||||||
|
drawer.drawcurveline(
|
||||||
|
canvas,
|
||||||
|
points,
|
||||||
|
Paint()
|
||||||
|
..color = Color(0xFF00C1AA)
|
||||||
|
..strokeWidth = 1.5,
|
||||||
|
);
|
||||||
|
drawer.drawpoints(
|
||||||
|
canvas,
|
||||||
|
points,
|
||||||
|
5,
|
||||||
|
Paint()..color = Color(0Xff00C1AA),
|
||||||
|
values: tips,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayMode == ChartDisplayMode.bar ||
|
||||||
|
displayMode == ChartDisplayMode.both) {
|
||||||
|
for (var i = 0; i < points.length; i++) {
|
||||||
|
final offset = points[i];
|
||||||
|
// final color = (barColors != null && barColors!.length > i)
|
||||||
|
// ? barColors![i]
|
||||||
|
// : Colors.yellow.withAlpha(100 + i * 10); // 默认回退
|
||||||
|
drawer.drawBar(
|
||||||
|
canvas,
|
||||||
|
Offset(offset.dx, 0),
|
||||||
|
barWidth,
|
||||||
|
offset.dy,
|
||||||
|
|
||||||
|
Paint()
|
||||||
|
..color = barColors!.length == 0
|
||||||
|
? Colors.white
|
||||||
|
: stringToColor(barColors![i]),
|
||||||
|
value: tips[i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((displayMode == ChartDisplayMode.dualBar ||
|
||||||
|
displayMode == ChartDisplayMode.both) &&
|
||||||
|
dualBarPoints != null) {
|
||||||
|
for (int i = 0; i < dualBarPoints!.length; i++) {
|
||||||
|
final List<Offset> bar = dualBarPoints![i];
|
||||||
|
if (bar.length < 3) continue;
|
||||||
|
|
||||||
|
final Offset deep = bar[0]; // 深睡:起点 -> deep.dy
|
||||||
|
final Offset light = bar[1]; // 浅睡:起点 -> light.dy
|
||||||
|
final Offset total = bar[2]; // 总睡眠:起点 -> total.dy
|
||||||
|
|
||||||
|
const String deepColor = "#21AD5D"; // 深睡
|
||||||
|
const String lightColor = "#45D989"; // 浅睡
|
||||||
|
const String remainColor = "#D3D3D3"; // 剩余
|
||||||
|
|
||||||
|
// 画深睡
|
||||||
|
if (deep.dy > 0) {
|
||||||
|
drawer.drawBar(
|
||||||
|
canvas,
|
||||||
|
Offset(deep.dx, 0),
|
||||||
|
barWidth,
|
||||||
|
deep.dy,
|
||||||
|
value: tips[i],
|
||||||
|
Paint()..color = stringToColor(deepColor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 画浅睡(从 deep.dy 开始)
|
||||||
|
final double lightHeight = light.dy - deep.dy;
|
||||||
|
if (lightHeight > 0) {
|
||||||
|
drawer.drawBar(
|
||||||
|
canvas,
|
||||||
|
Offset(light.dx, deep.dy),
|
||||||
|
barWidth,
|
||||||
|
lightHeight,
|
||||||
|
Paint()..color = stringToColor(lightColor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否需要画灰色剩余段和文字
|
||||||
|
final double remainHeight = total.dy - light.dy;
|
||||||
|
if (remainHeight > 0.01) {
|
||||||
|
// 灰色部分
|
||||||
|
drawer.drawBar(
|
||||||
|
canvas,
|
||||||
|
Offset(total.dx, light.dy),
|
||||||
|
barWidth,
|
||||||
|
remainHeight,
|
||||||
|
Paint()
|
||||||
|
..color = stringToColor(remainColor).withOpacity(0.8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 若已满,不绘制灰色段、也不显示文字(不做任何处理)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@ class _EditBedPageState extends State<EditBedPage> {
|
|||||||
children: [
|
children: [
|
||||||
// 中间居中的标题
|
// 中间居中的标题
|
||||||
Text(
|
Text(
|
||||||
'智能床名称',
|
'智能设备名称',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@@ -123,7 +123,7 @@ class _EditBedPageState extends State<EditBedPage> {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "请输入床的名称",
|
hintText: "请输入设备的名称",
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsetsDirectional
|
const EdgeInsetsDirectional
|
||||||
.fromSTEB(10, 0, 10, 0),
|
.fromSTEB(10, 0, 10, 0),
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class WebviewTestController extends GetControllerEx<WebviewTestModel> {
|
|||||||
WebviewTestController() : super(WebviewTestModel()) {
|
WebviewTestController() : super(WebviewTestModel()) {
|
||||||
web = WebviewHelper(
|
web = WebviewHelper(
|
||||||
jsbridge: buildsdk(
|
jsbridge: buildsdk(
|
||||||
father: this,
|
// father: this,
|
||||||
clientId: '494641114',
|
// clientId: '494641114',
|
||||||
// dbgserverUrl: 'ws://192.168.1.2:9001',
|
// dbgserverUrl: 'ws://192.168.1.2:9001',
|
||||||
),
|
),
|
||||||
settings: buildsettings(),
|
settings: buildsettings(),
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class SegmentedCircleWithCenterWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
centerWidget, // 放置自定义的中心 Widget
|
centerWidget, // 放置自定义的中心 Widget
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 200.rpx,
|
bottom: 140.rpx,
|
||||||
right: 60.rpx, // 放置在右侧
|
right: 60.rpx, // 放置在右侧
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
_getTrendIcon(trend),
|
_getTrendIcon(trend),
|
||||||
|
|||||||
73
lib/pages/sleep_report/component/DailyDataWidget.dart
Normal file
73
lib/pages/sleep_report/component/DailyDataWidget.dart
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/AIAdviceWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/BreatheCard.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/BreathePauseNewWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/BreatheStandardWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/CompareSleepWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/DiseasePercentsWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/HeartChangeWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/HeartHealthWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/HeartPointWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/HeartRateCard.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/HeartRateStandardWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SkinPercentWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepCard.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepScoreWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepView.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SnoreViewWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart';
|
||||||
|
|
||||||
|
Widget DailyDataWidget(
|
||||||
|
Map<dynamic, dynamic> sleepReport,
|
||||||
|
GlobalKey sleepCardKey,
|
||||||
|
GlobalKey heartRateCardKey,
|
||||||
|
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),
|
||||||
|
SleepCard(
|
||||||
|
key: sleepCardKey,
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
highlightItem: data['itemName'],
|
||||||
|
),
|
||||||
|
CompareSleepWidget(sleepReport: sleepReport),
|
||||||
|
HeartPointWidget(sleepReport: sleepReport),
|
||||||
|
AIAdviceWidget(sleepReport: sleepReport),
|
||||||
|
HeartRateStandardWidget(sleepReport: sleepReport),
|
||||||
|
HeartRateCard(
|
||||||
|
key: heartRateCardKey,
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
highlightItem: data['itemName'],
|
||||||
|
),
|
||||||
|
HeartChangeWidget(sleepReport: sleepReport),//心率变异性
|
||||||
|
BreatheStandardWidget(sleepReport: sleepReport),
|
||||||
|
BreatheCard(
|
||||||
|
key: breatheCardKey,
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
highlightItem: data['itemName'],
|
||||||
|
),
|
||||||
|
SnoreViewWidgetWidget(sleepReport: sleepReport),
|
||||||
|
BreathePauseNewWidget(sleepReport: sleepReport),
|
||||||
|
HeartHealthWidget(sleepReport: sleepReport),
|
||||||
|
DiseasePercentsWidget(sleepReport: sleepReport),
|
||||||
|
ZiZhuShenJingPercentWidget(sleepReport: sleepReport),
|
||||||
|
SkinPercentWidget(sleepReport: sleepReport),
|
||||||
|
]
|
||||||
|
.map((widget) => Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: SizedBox(width: double.infinity, child: widget),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: _buildSectionList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
757
lib/pages/sleep_report/component/MonthDataWidget.dart
Normal file
757
lib/pages/sleep_report/component/MonthDataWidget.dart
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
import 'package:ef/base/chart/drawer.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||||
|
import 'package:vbvs_app/pages/mh_page/component/easychart.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepCard.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepChartWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTablePage.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTextPage.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/WeekSleepScoreWidget.dart';
|
||||||
|
|
||||||
|
Widget MonthDataWidget(
|
||||||
|
Map<dynamic, dynamic> sleepReport,
|
||||||
|
dynamic data,
|
||||||
|
) {
|
||||||
|
List<Widget> _buildSectionList() {
|
||||||
|
EdgeInsetsDirectional padding =
|
||||||
|
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx);
|
||||||
|
|
||||||
|
return [
|
||||||
|
AvgSleepScoreWidget(
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
mediumLabel: "本月平均分",
|
||||||
|
), //睡眠评分
|
||||||
|
SleepChartContainer(
|
||||||
|
title: "每日得分",
|
||||||
|
tipText: "每日得分介绍",
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: sleepReport['scoreList']['yRange']['min'] ?? 0.0,
|
||||||
|
max: sleepReport['scoreList']['yRange']['max'] ?? 100.0,
|
||||||
|
q: 100,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['scoreList']['data'], '分', 1),
|
||||||
|
xCount: buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['scoreList']['yLable'].length,
|
||||||
|
points: buildMonthlyChartData(sleepReport['scoreList'])['points'],
|
||||||
|
displayMode: ChartDisplayMode.bar,
|
||||||
|
barColors: buildMonthlyChartData(sleepReport['scoreList'])['colors'],
|
||||||
|
xUnit: sleepReport['scoreList']['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
),
|
||||||
|
showLabel: sleepReport['scoreList']['type'],
|
||||||
|
),
|
||||||
|
SleepCard(
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
highlightItem: data['itemName'],
|
||||||
|
),
|
||||||
|
IndicatorCompareCard(
|
||||||
|
title: "与上月对比",
|
||||||
|
headers: ["名称", "上月", "本月", "参考范围"],
|
||||||
|
tooltip: "与上月对比提示",
|
||||||
|
rows: (sleepReport['cwl'] ?? []).map<List<Widget>>((item) {
|
||||||
|
return [
|
||||||
|
Text(
|
||||||
|
item['name']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['lastCurr']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['curr']?.toString() ?? '-',
|
||||||
|
style: TextStyle(
|
||||||
|
color: item["currColor"] ?? Color(0xFFFFFFFF),
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['range']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
SleepChartContainer(
|
||||||
|
title: "本月睡眠时长",
|
||||||
|
tipText: "每日得分介绍",
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: sleepReport['scoreList']['yRange']['min'] ?? 0.0,
|
||||||
|
max: sleepReport['scoreList']['yRange']['max'] ?? 10.0,
|
||||||
|
q: 10,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['csd']['yLable'].map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['scoreList']['data'], '小时', 1),
|
||||||
|
xCount: buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['csd']['yLable'].length,
|
||||||
|
points: [],
|
||||||
|
dualBarPoints: buildTripleBarData(sleepReport['csd']),
|
||||||
|
displayMode: ChartDisplayMode.dualBar,
|
||||||
|
xUnit: sleepReport['csd']['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
),
|
||||||
|
showLabel: sleepReport['csd']['type'],
|
||||||
|
),
|
||||||
|
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][0]['name'],
|
||||||
|
tipText: "入睡时间趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][0]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-30.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][0]['value'], '入睡时间:', 0),
|
||||||
|
xCount:
|
||||||
|
buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['dysp'][0]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][0]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][0]['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
bottomPadding: 16,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][1]['name'],
|
||||||
|
tipText: "起床时间趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 4,
|
||||||
|
q: 4,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][1]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-30.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][1]['value'], '起床时间:', 0),
|
||||||
|
xCount:
|
||||||
|
buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['dysp'][1]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][1]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][1]['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
bottomPadding: 16,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][2]['name'],
|
||||||
|
tipText: "心率基准趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 4,
|
||||||
|
q: 4,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][2]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][2]['value'], '次', 1),
|
||||||
|
xCount:
|
||||||
|
buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['dysp'][2]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][2]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][2]['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
bottomPadding: 16,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][3]['name'],
|
||||||
|
tipText: "心率变异性趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][3]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][3]['value'], '毫秒', 1),
|
||||||
|
xCount:
|
||||||
|
buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['dysp'][3]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][3]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][3]['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
bottomPadding: 16,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][4]['name'],
|
||||||
|
tipText: "呼吸基准趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: buildMonthlyChartData(
|
||||||
|
sleepReport['scoreList'])['daysInMonth'], //最大值30
|
||||||
|
q: buildMonthlyChartData(sleepReport['scoreList'])[
|
||||||
|
'daysInMonth'], //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['xLable'].map((e) => e['name']),
|
||||||
|
),
|
||||||
|
indexs: (sleepReport['scoreList']['xLable'] as List)
|
||||||
|
.map((e) => e['index'] as int)
|
||||||
|
.toList(),
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][4]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][4]['value'], '次/分', 1),
|
||||||
|
xCount:
|
||||||
|
buildMonthlyChartData(sleepReport['scoreList'])['daysInMonth']
|
||||||
|
.toInt(),
|
||||||
|
yCount: sleepReport['dysp'][4]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][4]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][4]['yUnit'],
|
||||||
|
barWidth: 0.5,
|
||||||
|
bottomPadding: 16,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text("MAC号:${data['mac']}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFD3D3D3).withOpacity(0.2), fontSize: 18.rpx))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.map((widget) => Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: SizedBox(width: double.infinity, child: widget),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: _buildSectionList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> buildValueTexts(
|
||||||
|
List<dynamic> data,
|
||||||
|
String unit,
|
||||||
|
int direction, // 0 表示左侧,1 表示右侧
|
||||||
|
) {
|
||||||
|
if (data.isEmpty) return [];
|
||||||
|
|
||||||
|
return data
|
||||||
|
.where((item) => item.containsKey('value') && item.containsKey('st'))
|
||||||
|
.map((item) {
|
||||||
|
final val = item['value'].toString();
|
||||||
|
|
||||||
|
// 单位位置
|
||||||
|
final prefix = direction == 1 ? '' : unit;
|
||||||
|
final suffix = direction == 1 ? unit : '';
|
||||||
|
|
||||||
|
// 中文年月日格式
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
final dateStr = "${date.year}年 ${date.month}月 ${date.day}日";
|
||||||
|
|
||||||
|
return "$prefix$val$suffix\n$dateStr";
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> buildMonthlyChartData(Map<String, dynamic> dyspData) {
|
||||||
|
final List<Map<String, dynamic>> data =
|
||||||
|
(dyspData['data'] as List?)?.whereType<Map<String, dynamic>>().toList() ??
|
||||||
|
[];
|
||||||
|
final List<Map<String, dynamic>> yLabels = (dyspData['yLable'] as List?)
|
||||||
|
?.whereType<Map<String, dynamic>>()
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
final List<Map<String, dynamic>> types =
|
||||||
|
(dyspData['type'] as List?)?.whereType<Map<String, dynamic>>().toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
if (data.isEmpty || yLabels.length < 2) {
|
||||||
|
return {
|
||||||
|
'points': <Offset>[],
|
||||||
|
'colors': <String>[],
|
||||||
|
'daysInMonth': 0.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 yLabel 为数值
|
||||||
|
List<double> yValues =
|
||||||
|
yLabels.map((e) => double.tryParse(e['name'].toString()) ?? 0).toList();
|
||||||
|
double minY = yValues.first;
|
||||||
|
double maxY = yValues.last;
|
||||||
|
double yRange = maxY - minY;
|
||||||
|
double yStepCount = (yValues.length - 1).toDouble(); // 👈 用 double
|
||||||
|
|
||||||
|
// 获取当前月的总天数(以第一条数据为准)
|
||||||
|
DateTime baseDate = DateTime.fromMillisecondsSinceEpoch(data.first['st']);
|
||||||
|
double daysInMonth = DateTime(baseDate.year, baseDate.month + 1, 0)
|
||||||
|
.day
|
||||||
|
.toDouble(); // 👈 改为 double
|
||||||
|
|
||||||
|
List<Offset> points = [];
|
||||||
|
List<String> colors = [];
|
||||||
|
|
||||||
|
for (var item in data) {
|
||||||
|
final int st = item['st'];
|
||||||
|
final int level = item['level'];
|
||||||
|
final double value = (item['value'] as num).toDouble();
|
||||||
|
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(st);
|
||||||
|
double day = date.day.toDouble();
|
||||||
|
|
||||||
|
// x: day - 1(0 起始)
|
||||||
|
double x = day - 1;
|
||||||
|
|
||||||
|
// // y: 归一化
|
||||||
|
// double y = ((value - minY) / yRange) * yStepCount;
|
||||||
|
|
||||||
|
// 获取 color
|
||||||
|
String color = types.firstWhere(
|
||||||
|
(e) => e['level'] == level,
|
||||||
|
orElse: () => {'color': '#CCCCCC'},
|
||||||
|
)['color'];
|
||||||
|
|
||||||
|
points.add(Offset(x, value));
|
||||||
|
colors.add(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'points': points,
|
||||||
|
'colors': colors,
|
||||||
|
'daysInMonth': daysInMonth,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Offset> buildGeneralPoints(Map<String, dynamic> dyspData) {
|
||||||
|
final values =
|
||||||
|
(dyspData['value'] as List?)?.whereType<Map<String, dynamic>>().toList();
|
||||||
|
final yLabels =
|
||||||
|
(dyspData['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList();
|
||||||
|
if (values == null || values.isEmpty || yLabels == null || yLabels.length < 2)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
bool isTimeAxis = yLabels.first['name'].toString().contains(":");
|
||||||
|
|
||||||
|
double labelToValue(String name) {
|
||||||
|
if (isTimeAxis) {
|
||||||
|
final parts = name.split(':');
|
||||||
|
int hour = int.parse(parts[0]);
|
||||||
|
int minute = int.parse(parts[1]);
|
||||||
|
return hour * 60 + minute * 1.0;
|
||||||
|
} else {
|
||||||
|
return double.tryParse(name) ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<double> labelValues =
|
||||||
|
yLabels.map((e) => labelToValue(e['name'])).toList();
|
||||||
|
|
||||||
|
// 处理时间轴跨午夜
|
||||||
|
bool crossMidnight = false;
|
||||||
|
if (isTimeAxis && labelValues.last < labelValues.first) {
|
||||||
|
crossMidnight = true;
|
||||||
|
double base = labelValues.first;
|
||||||
|
for (int i = 1; i < labelValues.length; i++) {
|
||||||
|
if (labelValues[i] < base) {
|
||||||
|
labelValues[i] += 1440; // +24小时,单位分钟
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Offset> points = [];
|
||||||
|
|
||||||
|
for (var item in values) {
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
double x = (date.day - 1).toDouble(); // 👈 按照“日”定位 X 坐标
|
||||||
|
|
||||||
|
dynamic rawValue = item['value'];
|
||||||
|
double valueMinBased;
|
||||||
|
|
||||||
|
if (isTimeAxis && rawValue is String) {
|
||||||
|
final parts = rawValue.split(':');
|
||||||
|
int hour = int.parse(parts[0]);
|
||||||
|
int minute = int.parse(parts[1]);
|
||||||
|
valueMinBased = hour * 60 + minute * 1.0;
|
||||||
|
|
||||||
|
if (crossMidnight && valueMinBased < labelValues.first) {
|
||||||
|
valueMinBased += 1440;
|
||||||
|
}
|
||||||
|
} else if (!isTimeAxis && rawValue is num) {
|
||||||
|
valueMinBased = rawValue.toDouble();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueMinBased < labelValues.first) valueMinBased = labelValues.first;
|
||||||
|
if (valueMinBased > labelValues.last) valueMinBased = labelValues.last;
|
||||||
|
|
||||||
|
double y = getYPositionBySegmentedLabels(labelValues, valueMinBased);
|
||||||
|
|
||||||
|
points.add(Offset(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getYPositionBySegmentedLabels(List<double> labels, double value) {
|
||||||
|
if (value <= labels.first) return 0.0;
|
||||||
|
if (value >= labels.last) return labels.length - 1.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.length - 1; i++) {
|
||||||
|
double low = labels[i];
|
||||||
|
double high = labels[i + 1];
|
||||||
|
if (value >= low && value <= high) {
|
||||||
|
double ratio = (value - low) / (high - low);
|
||||||
|
return i + ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 不应该走到这里,默认返回0
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//多色柱状图坐标
|
||||||
|
List<List<Offset>> buildTripleBarData(Map<String, dynamic> csd) {
|
||||||
|
final List<Map<String, dynamic>> data =
|
||||||
|
(csd['data'] as List?)?.whereType<Map<String, dynamic>>().toList() ?? [];
|
||||||
|
|
||||||
|
final List<Map<String, dynamic>> yLabels =
|
||||||
|
(csd['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
if (data.isEmpty || yLabels.isEmpty) return [];
|
||||||
|
|
||||||
|
double minY = double.tryParse(yLabels.first['name'].toString()) ?? 0;
|
||||||
|
double maxY = double.tryParse(yLabels.last['name'].toString()) ?? 10;
|
||||||
|
|
||||||
|
List<List<Offset>> points = [];
|
||||||
|
|
||||||
|
for (var item in data) {
|
||||||
|
double dst = (item['dst'] as num?)?.toDouble() ?? 0;
|
||||||
|
double lst = (item['lst'] as num?)?.toDouble() ?? 0;
|
||||||
|
double slt = (item['slt'] as num?)?.toDouble() ?? 0;
|
||||||
|
|
||||||
|
// 限制 y 值范围
|
||||||
|
dst = dst.clamp(minY, maxY);
|
||||||
|
lst = lst.clamp(minY, maxY);
|
||||||
|
slt = slt.clamp(minY, maxY);
|
||||||
|
|
||||||
|
// 计算 X:本月第几天(从 0 开始)
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
int dayOfMonth = date.day; // 1 ~ 31
|
||||||
|
double x = (dayOfMonth - 1).toDouble(); // 转为从 0 开始
|
||||||
|
|
||||||
|
points.add([
|
||||||
|
Offset(x, dst), // 深睡终点
|
||||||
|
Offset(x, dst + lst), // 浅睡+深睡终点
|
||||||
|
Offset(x, slt), // 总睡眠终点(第三段)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
114
lib/pages/sleep_report/component/SleepChartWidget.dart
Normal file
114
lib/pages/sleep_report/component/SleepChartWidget.dart
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import 'package:ef/ef.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.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/pages/device_bind/componnet/bind_dialog.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/chart/GradientLine.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/chart/SnoreWaveform.dart';
|
||||||
|
import 'package:EasyDartModule/EasyDartModule.dart' as es;
|
||||||
|
|
||||||
|
class SleepChartContainer extends StatelessWidget {
|
||||||
|
final Map sleepReport;
|
||||||
|
final String title;
|
||||||
|
final String tipText;
|
||||||
|
final Widget? chartContent;
|
||||||
|
final List<dynamic> showLabel;
|
||||||
|
const SleepChartContainer(
|
||||||
|
{Key? key,
|
||||||
|
required this.sleepReport,
|
||||||
|
required this.title,
|
||||||
|
required this.tipText,
|
||||||
|
required this.chartContent,
|
||||||
|
required this.showLabel})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: themeController.currentColor.sc5,
|
||||||
|
borderRadius: BorderRadius.circular(25.rpx),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.fromLTRB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 30.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showTipDialog(
|
||||||
|
context,
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
tipText,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
color: themeController.currentColor.sc3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 30.rpx,
|
||||||
|
height: 30.rpx,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
'assets/img/icon/explain.svg',
|
||||||
|
color: Color(0xFFD3D3D3),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Container(
|
||||||
|
child: chartContent ?? Container(),
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
spacing: 32.rpx,
|
||||||
|
runSpacing: 20.rpx,
|
||||||
|
children: showLabel.map<Widget>((item) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 20.rpx,
|
||||||
|
height: 20.rpx,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: stringToColor("${item["color"]}"),
|
||||||
|
borderRadius: BorderRadius.circular(10.rpx),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 17.rpx),
|
||||||
|
Text(
|
||||||
|
item["name"],
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 24.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,19 +48,21 @@ class _SleepScoreWidgetState extends State<SleepScoreWidget> {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
// decoration: BoxDecoration(color: Colors.green),
|
// decoration: BoxDecoration(color: Colors.green),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
|
padding:
|
||||||
|
EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(),
|
decoration: BoxDecoration(),
|
||||||
child: Row(
|
child: Stack(
|
||||||
mainAxisSize: MainAxisSize.max,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Row(
|
||||||
decoration: BoxDecoration(),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Column(
|
children: [
|
||||||
|
Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@@ -86,10 +88,33 @@ class _SleepScoreWidgetState extends State<SleepScoreWidget> {
|
|||||||
),
|
),
|
||||||
].divide(SizedBox(height: 56.rpx)),
|
].divide(SizedBox(height: 56.rpx)),
|
||||||
),
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'睡眠等级'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFD3D3D3),
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
letterSpacing: 0.0,
|
||||||
),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: Container(
|
Text(
|
||||||
decoration: BoxDecoration(),
|
'${getSleepLevel(widget.sleepReport)}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: stringToColor("#FF9F66"),
|
||||||
|
fontSize: 48.rpx,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
].divide(SizedBox(height: 56.rpx)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 256.rpx,
|
||||||
|
height: 256.rpx,
|
||||||
child: SegmentedCircleWithCenterWidget(
|
child: SegmentedCircleWithCenterWidget(
|
||||||
// segments: [
|
// segments: [
|
||||||
// SegmentData(color: Colors.red, value: 30),
|
// SegmentData(color: Colors.red, value: 30),
|
||||||
@@ -98,7 +123,7 @@ class _SleepScoreWidgetState extends State<SleepScoreWidget> {
|
|||||||
// SegmentData(color: Colors.orange, value: 10),
|
// SegmentData(color: Colors.orange, value: 10),
|
||||||
// ],
|
// ],
|
||||||
segments: segments,
|
segments: segments,
|
||||||
strokeWidth: 8,
|
strokeWidth: 6.rpx,
|
||||||
gapAngle: 8,
|
gapAngle: 8,
|
||||||
centerWidget: Container(
|
centerWidget: Container(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -122,38 +147,111 @@ class _SleepScoreWidgetState extends State<SleepScoreWidget> {
|
|||||||
),
|
),
|
||||||
trend: widget.sleepReport['score']?['trend'],
|
trend: widget.sleepReport['score']?['trend'],
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
// Row(
|
||||||
|
// mainAxisSize: MainAxisSize.max,
|
||||||
|
// children: [
|
||||||
|
// Container(
|
||||||
|
// decoration: BoxDecoration(),
|
||||||
|
// child: Column(
|
||||||
|
// mainAxisSize: MainAxisSize.max,
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// Container(
|
||||||
|
// constraints: BoxConstraints(maxWidth: 150.rpx),
|
||||||
|
// child: Text(
|
||||||
|
// '30天平均分'.tr,
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: Color(0xFFD3D3D3),
|
||||||
|
// fontSize: 26.rpx,
|
||||||
|
// letterSpacing: 0.0,
|
||||||
|
// ),
|
||||||
|
// maxLines: 1,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Text(
|
||||||
|
// '${widget.sleepReport['score']?['avg']}',
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: Colors.white,
|
||||||
|
// fontSize: 48.rpx,
|
||||||
|
// letterSpacing: 0.0,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ].divide(SizedBox(height: 56.rpx)),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Expanded(
|
||||||
|
// child: Container(
|
||||||
|
// decoration: BoxDecoration(),
|
||||||
|
// child: SegmentedCircleWithCenterWidget(
|
||||||
|
// // segments: [
|
||||||
|
// // SegmentData(color: Colors.red, value: 30),
|
||||||
|
// // SegmentData(color: Colors.green, value: 20),
|
||||||
|
// // SegmentData(color: Colors.blue, value: 40),
|
||||||
|
// // SegmentData(color: Colors.orange, value: 10),
|
||||||
|
// // ],
|
||||||
|
// segments: segments,
|
||||||
|
// strokeWidth: 8,
|
||||||
|
// gapAngle: 8,
|
||||||
|
// centerWidget: Container(
|
||||||
|
// child: Column(
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// "睡眠评分".tr,
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: stringToColor("#FFFFFF"),
|
||||||
|
// fontSize:
|
||||||
|
// AppConstants().normal_text_fontSize),
|
||||||
|
// ),
|
||||||
|
// Text(
|
||||||
|
// '${widget.sleepReport['score']?['score']}',
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: stringToColor("#FF9F66"),
|
||||||
|
// fontSize: 100.rpx),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// trend: widget.sleepReport['score']?['trend'],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Container(
|
||||||
|
// decoration: BoxDecoration(),
|
||||||
|
// child: Column(
|
||||||
|
// mainAxisSize: MainAxisSize.max,
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// Text(
|
||||||
|
// '睡眠等级'.tr,
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: Color(0xFFD3D3D3),
|
||||||
|
// fontSize: 26.rpx,
|
||||||
|
// letterSpacing: 0.0,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Text(
|
||||||
|
// '${getSleepLevel(widget.sleepReport)}',
|
||||||
|
// style: TextStyle(
|
||||||
|
// color: stringToColor("#FF9F66"),
|
||||||
|
// fontSize: 48.rpx,
|
||||||
|
// letterSpacing: 0.0,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ].divide(SizedBox(height: 56.rpx)),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ].divide(SizedBox(
|
||||||
|
// width: 33.rpx,
|
||||||
|
// )),
|
||||||
|
// ),
|
||||||
|
|
||||||
),
|
),
|
||||||
),
|
SizedBox(height: 50.rpx),
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'睡眠等级'.tr,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFFD3D3D3),
|
|
||||||
fontSize: 26.rpx,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${getSleepLevel(widget.sleepReport)}',
|
|
||||||
style: TextStyle(
|
|
||||||
color: stringToColor("#FF9F66"),
|
|
||||||
fontSize: 48.rpx,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
].divide(SizedBox(height: 56.rpx)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
].divide(SizedBox(
|
|
||||||
width: 33.rpx,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 32.rpx,
|
spacing: 32.rpx,
|
||||||
runSpacing: 20.rpx,
|
runSpacing: 20.rpx,
|
||||||
@@ -188,7 +286,6 @@ class _SleepScoreWidgetState extends State<SleepScoreWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
es.EasyDartModule.logger.error("打鼾监测绘制异常${e}");
|
es.EasyDartModule.logger.error("打鼾监测绘制异常${e}");
|
||||||
return Container();
|
return Container();
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class _SleepViewWidgetState extends State<SleepViewWidget> {
|
|||||||
context,
|
context,
|
||||||
Container(
|
Container(
|
||||||
child: Text(
|
child: Text(
|
||||||
"睡眠规律性介绍。",
|
"睡眠规律性介绍",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 26.rpx,
|
fontSize: 26.rpx,
|
||||||
color: themeController.currentColor.sc3,
|
color: themeController.currentColor.sc3,
|
||||||
|
|||||||
78
lib/pages/sleep_report/component/TrendDataTablePage.dart
Normal file
78
lib/pages/sleep_report/component/TrendDataTablePage.dart
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.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:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
|
||||||
|
|
||||||
|
class TrendDataTablePage extends StatefulWidget {
|
||||||
|
final String title;
|
||||||
|
final String tipText;
|
||||||
|
final Widget? chartContent;
|
||||||
|
final double? padding;
|
||||||
|
|
||||||
|
const TrendDataTablePage({
|
||||||
|
Key? key,
|
||||||
|
required this.title,
|
||||||
|
required this.tipText,
|
||||||
|
required this.chartContent,
|
||||||
|
required this.padding,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TrendDataTablePage> createState() => _TrendDataTablePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TrendDataTablePageState extends State<TrendDataTablePage> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: themeController.currentColor.sc5,
|
||||||
|
borderRadius: BorderRadius.circular(25.rpx)),
|
||||||
|
padding:
|
||||||
|
EdgeInsets.fromLTRB(26.rpx, 29.rpx, 26.rpx, widget.padding ?? 45.rpx),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 30.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showTipDialog(
|
||||||
|
context,
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
widget.tipText,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
color: themeController.currentColor.sc3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 30.rpx,
|
||||||
|
height: 30.rpx,
|
||||||
|
child: SvgPicture.asset('assets/img/icon/explain.svg',
|
||||||
|
color: Color(0xFFD3D3D3)),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 47.rpx),
|
||||||
|
widget.chartContent ?? Container(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
123
lib/pages/sleep_report/component/TrendDataTextPage.dart
Normal file
123
lib/pages/sleep_report/component/TrendDataTextPage.dart
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import 'package:ef/ef.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.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/pages/device_bind/componnet/bind_dialog.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/chart/DataShowWidget.dart';
|
||||||
|
import 'package:EasyDartModule/EasyDartModule.dart' as es;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
class IndicatorCompareCard extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String? tooltip;
|
||||||
|
final List<String> headers; // 表头 ["名称", "测量值", "参考范围", "趋势"]
|
||||||
|
final List<List<Widget>> rows; // 每一行的 4 个 widget
|
||||||
|
final double spacing;
|
||||||
|
|
||||||
|
const IndicatorCompareCard({
|
||||||
|
super.key,
|
||||||
|
required this.title,
|
||||||
|
this.tooltip,
|
||||||
|
required this.headers,
|
||||||
|
required this.rows,
|
||||||
|
this.spacing = 31,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
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(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
/// 标题行
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: themeController.currentColor.sc3,
|
||||||
|
fontSize: AppConstants().title_text_fontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (tooltip != null)
|
||||||
|
ClickableContainer(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.white,
|
||||||
|
padding:
|
||||||
|
EdgeInsetsDirectional.fromSTEB(14.rpx, 0, 14.rpx, 0),
|
||||||
|
borderRadius: 0.rpx,
|
||||||
|
onTap: () {
|
||||||
|
showTipDialog(
|
||||||
|
context,
|
||||||
|
Text(
|
||||||
|
tooltip!.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
color: themeController.currentColor.sc3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 30.rpx,
|
||||||
|
height: 30.rpx,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
'assets/img/icon/explain.svg',
|
||||||
|
color: Color(0xFFD3D3D3),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: spacing.rpx),
|
||||||
|
|
||||||
|
/// 表头
|
||||||
|
DataShowWidget(
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
widget1: _buildHeader(headers, 0),
|
||||||
|
widget2: _buildHeader(headers, 1),
|
||||||
|
widget3: _buildHeader(headers, 2),
|
||||||
|
widget4: _buildHeader(headers, 3),
|
||||||
|
),
|
||||||
|
|
||||||
|
/// 数据行
|
||||||
|
Column(
|
||||||
|
children: rows.map((row) {
|
||||||
|
return DataShowWidget(
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
widget1: row[0],
|
||||||
|
widget2: row[1],
|
||||||
|
widget3: row[2],
|
||||||
|
widget4: row[3],
|
||||||
|
).paddingOnly(bottom: 0.rpx);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(List<String> headers, int index) {
|
||||||
|
return Text(
|
||||||
|
headers.length > index ? headers[index].tr : "",
|
||||||
|
style: TextStyle(
|
||||||
|
color: themeController.currentColor.sc4,
|
||||||
|
fontSize: AppConstants().normal_text_fontSize,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
852
lib/pages/sleep_report/component/WeekDataWidget.dart
Normal file
852
lib/pages/sleep_report/component/WeekDataWidget.dart
Normal file
@@ -0,0 +1,852 @@
|
|||||||
|
import 'package:ef/base/chart/drawer.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vbvs_app/common/util/FitTool.dart';
|
||||||
|
import 'package:vbvs_app/pages/mh_page/component/easychart.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepCard.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/SleepChartWidget.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTablePage.dart';
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/TrendDataTextPage.dart';
|
||||||
|
|
||||||
|
import 'package:vbvs_app/pages/sleep_report/component/WeekSleepScoreWidget.dart';
|
||||||
|
|
||||||
|
Widget WeekDataWidget(
|
||||||
|
Map<dynamic, dynamic> sleepReport,
|
||||||
|
dynamic data,
|
||||||
|
) {
|
||||||
|
List<Widget> _buildSectionList() {
|
||||||
|
EdgeInsetsDirectional padding =
|
||||||
|
EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 25.rpx);
|
||||||
|
|
||||||
|
return [
|
||||||
|
AvgSleepScoreWidget(
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
), //睡眠评分
|
||||||
|
SleepChartContainer(
|
||||||
|
title: "每日得分",
|
||||||
|
tipText: "睡眠规律性介绍",
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: sleepReport['scoreList']['yRange']['min'] ?? 0.0,
|
||||||
|
max: sleepReport['scoreList']['yRange']['max'] ?? 100.0,
|
||||||
|
q: 100,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['scoreList']['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['scoreList']['yLable'].length,
|
||||||
|
points: buildWeekDatesAndPoints(sleepReport['scoreList'])['points'],
|
||||||
|
displayMode: ChartDisplayMode.bar,
|
||||||
|
barColors:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['colors'],
|
||||||
|
tips: buildValueTexts(sleepReport['scoreList']['data'], '分', 1),
|
||||||
|
xUnit: sleepReport['scoreList']['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
),
|
||||||
|
showLabel: sleepReport['scoreList']['type'],
|
||||||
|
),
|
||||||
|
|
||||||
|
SleepCard(
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
highlightItem: data['itemName'],
|
||||||
|
),
|
||||||
|
IndicatorCompareCard(
|
||||||
|
title: "与上周对比",
|
||||||
|
headers: ["名称", "上周", "本周", "参考范围"],
|
||||||
|
tooltip: "与上周对比提示",
|
||||||
|
rows: (sleepReport['cwl'] ?? []).map<List<Widget>>((item) {
|
||||||
|
return [
|
||||||
|
Text(
|
||||||
|
item['name']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['lastCurr']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['curr']?.toString() ?? '-',
|
||||||
|
style: TextStyle(
|
||||||
|
color: item["currColor"] ?? Color(0xFFFFFFFF),
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['range']?.toString() ?? '-',
|
||||||
|
style: TextStyle(color: Color(0xFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
SleepChartContainer(
|
||||||
|
title: "本周睡眠时长",
|
||||||
|
tipText: "本周睡眠时长介绍",
|
||||||
|
sleepReport: sleepReport,
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: sleepReport['csd']['yRange']['min'] ?? 0.0,
|
||||||
|
max: sleepReport['csd']['yRange']['max'] ?? 10.0,
|
||||||
|
q: 10,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['csd']['yLable'].map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['csd']['yLable'].length,
|
||||||
|
points: [],
|
||||||
|
dualBarPoints: buildTripleBarData(sleepReport['csd']),
|
||||||
|
displayMode: ChartDisplayMode.dualBar,
|
||||||
|
xUnit: sleepReport['csd']['yUnit'],
|
||||||
|
tips: buildSleepValueTexts(sleepReport['csd']['data'], '小时', 1),
|
||||||
|
barWidth: 0.1,
|
||||||
|
),
|
||||||
|
showLabel: sleepReport['csd']['type'],
|
||||||
|
),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][0]['name'],
|
||||||
|
tipText: "入睡时间趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][0]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-30.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][0]['value'], '入睡时间:', 0),
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['dysp'][0]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][0]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][0]['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
bottomPadding: 50,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][1]['name'],
|
||||||
|
tipText: "起床时间趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 4,
|
||||||
|
q: 4,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][1]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-30.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][1]['value'], '起床时间:', 0),
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['dysp'][1]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][1]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][1]['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
bottomPadding: 50,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][2]['name'],
|
||||||
|
tipText: "心率基准趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 4,
|
||||||
|
q: 4,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][2]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][2]['value'], '次', 1),
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['dysp'][2]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][2]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][2]['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
bottomPadding: 50,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][3]['name'],
|
||||||
|
tipText: "心率变异性趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][3]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-15.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][3]['value'], '毫秒', 1),
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['dysp'][3]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][3]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][3]['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
bottomPadding: 50,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
TrendDataTablePage(
|
||||||
|
title: sleepReport['dysp'][4]['name'],
|
||||||
|
tipText: "呼吸基准趋势提示",
|
||||||
|
chartContent: LineView(
|
||||||
|
xLabels: [
|
||||||
|
ChartLables(
|
||||||
|
//标签系列1
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels:
|
||||||
|
buildWeekDatesAndPoints(sleepReport['scoreList'])['dates'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -16.rpx), //标签相对于原点的偏移,下方20像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XffD3D3D3), fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ChartLables(
|
||||||
|
//X轴标签系列2
|
||||||
|
min: 0, //最小值0
|
||||||
|
max: 7, //最大值30
|
||||||
|
q: 6, //labels第一个与最后一个的真实距离,也就是30-1 = 29
|
||||||
|
labels: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
|
indexs: [0, 1, 2, 3, 4, 5, 6], //每一个标签的对应在X轴的真实位置
|
||||||
|
offset: Offset(0, -50.rpx), //标签相对于原点的偏移,下方60像素位置
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Color(0XFFFFFFFF), fontSize: 26.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
yLabels: [
|
||||||
|
ChartLables(
|
||||||
|
min: 0,
|
||||||
|
max: 5,
|
||||||
|
q: 5,
|
||||||
|
labels: List<String>.from(
|
||||||
|
sleepReport['dysp'][4]['yLable']
|
||||||
|
.map((e) => e['name'].toString()),
|
||||||
|
),
|
||||||
|
offset: Offset(-20.rpx, 0),
|
||||||
|
ondrawer: (canvas, offset, index, label, align, style) {
|
||||||
|
ChartLables.drawText(
|
||||||
|
canvas,
|
||||||
|
label,
|
||||||
|
offset,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFFFFFFF).withOpacity(0.6),
|
||||||
|
fontSize: 18.rpx),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tips: buildValueTexts(sleepReport['dysp'][4]['value'], '次/分', 1),
|
||||||
|
xCount: 7,
|
||||||
|
yCount: sleepReport['dysp'][4]['yLable'].length,
|
||||||
|
points: buildGeneralPoints(sleepReport['dysp'][4]),
|
||||||
|
displayMode: ChartDisplayMode.line,
|
||||||
|
xUnit: sleepReport['dysp'][4]['yUnit'],
|
||||||
|
barWidth: 0.1,
|
||||||
|
bottomPadding: 50,
|
||||||
|
),
|
||||||
|
padding: 45.rpx),
|
||||||
|
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text("MAC号:${data['mac']}",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFD3D3D3).withOpacity(0.2), fontSize: 18.rpx))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
.map((widget) => Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: SizedBox(width: double.infinity, child: widget),
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: _buildSectionList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算非均匀标签的 y 坐标
|
||||||
|
double getYPositionBySegmentedLabels(List<double> labels, double value) {
|
||||||
|
if (value <= labels.first) return 0.0;
|
||||||
|
if (value >= labels.last) return labels.length - 1.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < labels.length - 1; i++) {
|
||||||
|
double low = labels[i];
|
||||||
|
double high = labels[i + 1];
|
||||||
|
if (value >= low && value <= high) {
|
||||||
|
double ratio = (value - low) / (high - low);
|
||||||
|
return i + ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 不应该走到这里,默认返回0
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//关键点标签
|
||||||
|
List<String> buildValueTexts(
|
||||||
|
List<dynamic> data,
|
||||||
|
String unit,
|
||||||
|
int direction, // 0 表示左侧,1 表示右侧
|
||||||
|
) {
|
||||||
|
if (data.isEmpty) return [];
|
||||||
|
|
||||||
|
return data.where((item) => item.containsKey('value')).map((item) {
|
||||||
|
final val = item['value'].toString();
|
||||||
|
return direction == 1 ? "$val$unit" : "$unit$val";
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
//多个关键点标签
|
||||||
|
List<String> buildSleepValueTexts(
|
||||||
|
List<dynamic> data,
|
||||||
|
String unit,
|
||||||
|
int direction, // 0 左侧,1 右侧
|
||||||
|
) {
|
||||||
|
if (data.isEmpty) return [];
|
||||||
|
|
||||||
|
return data.map((item) {
|
||||||
|
final dst = (item['dst'] ?? 0).toString();
|
||||||
|
final lst = (item['lst'] ?? 0).toString();
|
||||||
|
final slt = (item['slt'] ?? 0).toString();
|
||||||
|
|
||||||
|
final prefix = direction == 1 ? '' : unit;
|
||||||
|
final suffix = direction == 1 ? unit : '';
|
||||||
|
|
||||||
|
var q = [
|
||||||
|
"睡眠时长:$prefix$slt$suffix",
|
||||||
|
"深睡:$prefix$dst$suffix",
|
||||||
|
"浅睡:$prefix$lst$suffix",
|
||||||
|
].join("\n");
|
||||||
|
print(q);
|
||||||
|
return q;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getWeekdayX(DateTime current, DateTime first) {
|
||||||
|
final firstMonday = first.subtract(Duration(days: first.weekday - 1));
|
||||||
|
final currentMonday = current.subtract(Duration(days: current.weekday - 1));
|
||||||
|
final weekOffset = currentMonday.difference(firstMonday).inDays ~/ 7;
|
||||||
|
return weekOffset * 7 + (current.weekday - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> buildWeekDatesAndPoints(Map<String, dynamic> scoreList) {
|
||||||
|
if (!scoreList.containsKey('data') || (scoreList['data'] as List).isEmpty) {
|
||||||
|
return {
|
||||||
|
'dates': [],
|
||||||
|
'points': [],
|
||||||
|
'colors': [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, dynamic>> data = (scoreList['data'] as List)
|
||||||
|
.where((item) => item is Map<String, dynamic>)
|
||||||
|
.cast<Map<String, dynamic>>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// 提取 level -> color 映射表
|
||||||
|
Map<int, String> levelColorMap = {};
|
||||||
|
if (scoreList.containsKey('type')) {
|
||||||
|
List typeList = scoreList['type'];
|
||||||
|
for (var item in typeList) {
|
||||||
|
if (item is Map &&
|
||||||
|
item.containsKey('level') &&
|
||||||
|
item.containsKey('color')) {
|
||||||
|
levelColorMap[item['level']] = item['color'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime baseDate = DateTime.fromMillisecondsSinceEpoch(data.first['st']);
|
||||||
|
int weekday = baseDate.weekday;
|
||||||
|
DateTime monday = baseDate.subtract(Duration(days: weekday - 1));
|
||||||
|
List<String> dates = List.generate(7, (i) {
|
||||||
|
DateTime d = monday.add(Duration(days: i));
|
||||||
|
String month = d.month.toString().padLeft(2, '0');
|
||||||
|
String day = d.day.toString().padLeft(2, '0');
|
||||||
|
return "$month/$day";
|
||||||
|
});
|
||||||
|
|
||||||
|
List<Offset> points = [];
|
||||||
|
List<String> colors = [];
|
||||||
|
|
||||||
|
for (var item in data) {
|
||||||
|
DateTime dt = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
double x = getWeekdayX(dt, baseDate).toDouble();
|
||||||
|
double y = (item['value'] as num?)?.toDouble() ?? 0;
|
||||||
|
int level = item['level'];
|
||||||
|
String color = levelColorMap[level] ?? "#FFFF00";
|
||||||
|
|
||||||
|
points.add(Offset(x, y));
|
||||||
|
colors.add(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'dates': dates,
|
||||||
|
'points': points,
|
||||||
|
'colors': colors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Offset> buildGeneralPoints(Map<String, dynamic> dyspData) {
|
||||||
|
final values =
|
||||||
|
(dyspData['value'] as List?)?.whereType<Map<String, dynamic>>().toList();
|
||||||
|
final yLabels =
|
||||||
|
(dyspData['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList();
|
||||||
|
if (values == null || values.isEmpty || yLabels == null || yLabels.length < 2)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
bool isTimeAxis = yLabels.first['name'].toString().contains(":");
|
||||||
|
|
||||||
|
double labelToValue(String name) {
|
||||||
|
if (isTimeAxis) {
|
||||||
|
final parts = name.split(':');
|
||||||
|
int hour = int.parse(parts[0]);
|
||||||
|
int minute = int.parse(parts[1]);
|
||||||
|
return hour * 60 + minute * 1.0;
|
||||||
|
} else {
|
||||||
|
return double.tryParse(name) ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<double> labelValues =
|
||||||
|
yLabels.map((e) => labelToValue(e['name'])).toList();
|
||||||
|
|
||||||
|
bool crossMidnight = false;
|
||||||
|
if (isTimeAxis && labelValues.last < labelValues.first) {
|
||||||
|
crossMidnight = true;
|
||||||
|
double base = labelValues.first;
|
||||||
|
for (int i = 1; i < labelValues.length; i++) {
|
||||||
|
if (labelValues[i] < base) {
|
||||||
|
labelValues[i] += 1440;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime baseDate = DateTime.fromMillisecondsSinceEpoch(values.first['st']);
|
||||||
|
List<Offset> points = [];
|
||||||
|
|
||||||
|
for (var item in values) {
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
double x = getWeekdayX(date, baseDate).toDouble();
|
||||||
|
|
||||||
|
dynamic rawValue = item['value'];
|
||||||
|
double valueMinBased;
|
||||||
|
|
||||||
|
if (isTimeAxis && rawValue is String) {
|
||||||
|
final parts = rawValue.split(':');
|
||||||
|
int hour = int.parse(parts[0]);
|
||||||
|
int minute = int.parse(parts[1]);
|
||||||
|
valueMinBased = hour * 60 + minute * 1.0;
|
||||||
|
if (crossMidnight && valueMinBased < labelValues.first) {
|
||||||
|
valueMinBased += 1440;
|
||||||
|
}
|
||||||
|
} else if (!isTimeAxis && rawValue is num) {
|
||||||
|
valueMinBased = rawValue.toDouble();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueMinBased < labelValues.first) valueMinBased = labelValues.first;
|
||||||
|
if (valueMinBased > labelValues.last) valueMinBased = labelValues.last;
|
||||||
|
|
||||||
|
double y = getYPositionBySegmentedLabels(labelValues, valueMinBased);
|
||||||
|
points.add(Offset(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<Offset>> buildTripleBarData(Map<String, dynamic> csd) {
|
||||||
|
final data =
|
||||||
|
(csd['data'] as List?)?.whereType<Map<String, dynamic>>().toList() ?? [];
|
||||||
|
final yLabels =
|
||||||
|
(csd['yLable'] as List?)?.whereType<Map<String, dynamic>>().toList() ??
|
||||||
|
[];
|
||||||
|
|
||||||
|
if (data.isEmpty || yLabels.isEmpty) return [];
|
||||||
|
|
||||||
|
double minY = double.tryParse(yLabels.first['name'].toString()) ?? 0;
|
||||||
|
double maxY = double.tryParse(yLabels.last['name'].toString()) ?? 10;
|
||||||
|
|
||||||
|
DateTime firstDate = DateTime.fromMillisecondsSinceEpoch(data.first['st']);
|
||||||
|
|
||||||
|
List<List<Offset>> points = [];
|
||||||
|
|
||||||
|
for (var item in data) {
|
||||||
|
double dst = (item['dst'] as num?)?.toDouble() ?? 0;
|
||||||
|
double lst = (item['lst'] as num?)?.toDouble() ?? 0;
|
||||||
|
double slt = (item['slt'] as num?)?.toDouble() ?? 0;
|
||||||
|
|
||||||
|
dst = dst.clamp(minY, maxY);
|
||||||
|
lst = lst.clamp(minY, maxY);
|
||||||
|
slt = slt.clamp(minY, maxY);
|
||||||
|
|
||||||
|
DateTime date = DateTime.fromMillisecondsSinceEpoch(item['st']);
|
||||||
|
double x = getWeekdayX(date, firstDate).toDouble();
|
||||||
|
|
||||||
|
points.add([
|
||||||
|
Offset(x, dst),
|
||||||
|
Offset(x, dst + lst),
|
||||||
|
Offset(x, slt),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
217
lib/pages/sleep_report/component/WeekSleepScoreWidget.dart
Normal file
217
lib/pages/sleep_report/component/WeekSleepScoreWidget.dart
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
import 'package:ef/ef.dart';
|
||||||
|
import 'package:flutter/material.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/pages/sleep_report/chart/SegmentedCirclePainter.dart';
|
||||||
|
import 'package:EasyDartModule/EasyDartModule.dart' as es;
|
||||||
|
|
||||||
|
class AvgSleepScoreWidget extends StatelessWidget {
|
||||||
|
final Map sleepReport;
|
||||||
|
final String leftLabel;
|
||||||
|
final String mediumLabel;
|
||||||
|
final String rightLabel;
|
||||||
|
final String unknownText;
|
||||||
|
|
||||||
|
const AvgSleepScoreWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.sleepReport,
|
||||||
|
this.leftLabel = '最低分',
|
||||||
|
this.mediumLabel = '本周评分',
|
||||||
|
this.rightLabel = '最高分',
|
||||||
|
this.unknownText = '未知数据',
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
try {
|
||||||
|
if (sleepReport == null || sleepReport is! Map || sleepReport.isEmpty) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
|
||||||
|
List showLabel = sleepReport['score']['type'];
|
||||||
|
List<SegmentData> segments = parseSegments(sleepReport);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsetsDirectional.fromSTEB(26.rpx, 29.rpx, 26.rpx, 45.rpx),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
// height: 300.rpx, // 自定义一个高度,确保有空间放置中间内容
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
// 左侧 min
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints: BoxConstraints(maxWidth: 150.rpx),
|
||||||
|
child: Text(
|
||||||
|
leftLabel.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFD3D3D3),
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
sleepReport['score']?['min']?.toString() ?? '--',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 48.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
].divide(SizedBox(height: 56.rpx)),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 右侧 max
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
rightLabel.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Color(0xFFD3D3D3),
|
||||||
|
fontSize: 26.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
sleepReport['score']?['max']?.toString() ?? '--',
|
||||||
|
style: TextStyle(
|
||||||
|
color: stringToColor("#FF9F66"),
|
||||||
|
fontSize: 48.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
].divide(SizedBox(height: 56.rpx)),
|
||||||
|
),
|
||||||
|
].divide(SizedBox(width: 33.rpx)),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 中间圆环绝对居中
|
||||||
|
SizedBox(
|
||||||
|
width: 256.rpx,
|
||||||
|
height: 256.rpx,
|
||||||
|
child: SegmentedCircleWithCenterWidget(
|
||||||
|
segments: segments,
|
||||||
|
strokeWidth: 6.rpx,
|
||||||
|
gapAngle: 0,
|
||||||
|
centerWidget: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
mediumLabel.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: AppConstants().normal_text_fontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
sleepReport['score']?['avg']?.toString() ?? '--',
|
||||||
|
style: TextStyle(
|
||||||
|
color: segments[0].color,
|
||||||
|
fontSize: 99.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trend: sleepReport['score']?['trend'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 50.rpx),
|
||||||
|
Wrap(
|
||||||
|
spacing: 32.rpx,
|
||||||
|
runSpacing: 20.rpx,
|
||||||
|
children: showLabel.map<Widget>((item) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(5),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 20.rpx,
|
||||||
|
height: 20.rpx,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: stringToColor("${item["color"]}"),
|
||||||
|
borderRadius: BorderRadius.circular(10.rpx),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 17.rpx),
|
||||||
|
Text(
|
||||||
|
item["name"] + '(' + item['range'] + ")",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 24.rpx,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print("SleepScoreWidget Error: $e");
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSleepLevel(Map sleepReport) {
|
||||||
|
if (sleepReport['score'] == null ||
|
||||||
|
sleepReport['score']['level'] == null ||
|
||||||
|
sleepReport['score']['type'] == null) {
|
||||||
|
return unknownText.tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int level = sleepReport['score']['level'];
|
||||||
|
List<dynamic> typeList = sleepReport['score']['type'];
|
||||||
|
|
||||||
|
var matched = typeList.firstWhere(
|
||||||
|
(item) => item['level'] == level,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matched != null && matched['name'] != null) {
|
||||||
|
return matched['name'];
|
||||||
|
}
|
||||||
|
return unknownText.tr;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SegmentData> parseSegments(Map sleepReport) {
|
||||||
|
try {
|
||||||
|
final score = sleepReport['score'] ?? {};
|
||||||
|
final int level = score['level'] ?? 4;
|
||||||
|
final typeList = score['type'] ?? [];
|
||||||
|
|
||||||
|
// 尝试获取匹配 level 的颜色
|
||||||
|
final matchedType = typeList.firstWhere(
|
||||||
|
(item) => item['level'] == level,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
final color = matchedType != null && matchedType['color'] != null
|
||||||
|
? stringToColor(matchedType['color'])
|
||||||
|
: Colors.grey;
|
||||||
|
|
||||||
|
return [SegmentData(color: color, value: 100)];
|
||||||
|
} catch (e, st) {
|
||||||
|
print('parseSegments error: $e\n$st');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -122,7 +122,7 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
|
|
||||||
/// 左边返回按钮
|
/// 左边返回按钮
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 20,
|
left: 0,
|
||||||
child: returnIconButtomNew,
|
child: returnIconButtomNew,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -199,103 +199,94 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Obx(() {
|
||||||
// Obx(() {
|
return ClickableContainer(
|
||||||
// return ClickableContainer(
|
backgroundColor: Colors.transparent,
|
||||||
// backgroundColor: Colors.transparent,
|
highlightColor: themeController
|
||||||
// highlightColor:
|
.currentColor.sc3,
|
||||||
// themeController.currentColor.sc3,
|
borderRadius: 8.rpx,
|
||||||
// borderRadius: 8.rpx,
|
padding: EdgeInsets.all(0),
|
||||||
// padding: EdgeInsets.all(0),
|
onTap: () async {
|
||||||
// onTap: () async {
|
sleepReportController.model.type =
|
||||||
// sleepReportController.model.type =
|
2;
|
||||||
// 2;
|
sleepReportController.updateAll();
|
||||||
// sleepReportController.updateAll();
|
},
|
||||||
// },
|
child: Column(
|
||||||
// child: Column(
|
mainAxisSize: MainAxisSize.max,
|
||||||
// mainAxisSize: MainAxisSize.max,
|
children: [
|
||||||
// children: [
|
Container(
|
||||||
// Container(
|
width:
|
||||||
// width: 115.rpx, // 固定宽度为 160.rpx
|
115.rpx, // 固定宽度为 160.rpx
|
||||||
// alignment:
|
alignment:
|
||||||
// Alignment.center, // 文字居中
|
Alignment.center, // 文字居中
|
||||||
// child: Text(
|
child: Text('周报'.tr,
|
||||||
// '周报'.tr,
|
style: TextStyle(
|
||||||
// style: FlutterFlowTheme.of(
|
fontFamily: 'Inter',
|
||||||
// context)
|
fontSize: AppConstants()
|
||||||
// .bodyMedium
|
.title_text_fontSize,
|
||||||
// .override(
|
letterSpacing: 0.0,
|
||||||
// fontFamily: 'Inter',
|
color:
|
||||||
// fontSize: AppConstants()
|
sleepReportController
|
||||||
// .title_text_fontSize,
|
.model
|
||||||
// letterSpacing: 0.0,
|
.type ==
|
||||||
// color:
|
2
|
||||||
// sleepReportController
|
? themeController
|
||||||
// .model
|
.currentColor
|
||||||
// .type ==
|
.sc2
|
||||||
// 2
|
: themeController
|
||||||
// ? themeController
|
.currentColor
|
||||||
// .currentColor
|
.sc3,
|
||||||
// .sc2
|
)),
|
||||||
// : themeController
|
),
|
||||||
// .currentColor
|
SizedBox(height: 10.rpx),
|
||||||
// .sc3,
|
],
|
||||||
// ),
|
),
|
||||||
// ),
|
);
|
||||||
// ),
|
}),
|
||||||
// SizedBox(height: 10.rpx),
|
Obx(() {
|
||||||
// ],
|
return ClickableContainer(
|
||||||
// ),
|
backgroundColor: Colors.transparent,
|
||||||
// );
|
highlightColor: themeController
|
||||||
// }),
|
.currentColor.sc3,
|
||||||
// Obx(() {
|
borderRadius: 8.rpx,
|
||||||
// return ClickableContainer(
|
padding: EdgeInsets.all(0),
|
||||||
// backgroundColor: Colors.transparent,
|
onTap: () async {
|
||||||
// highlightColor:
|
sleepReportController.model.type =
|
||||||
// themeController.currentColor.sc3,
|
3;
|
||||||
// borderRadius: 8.rpx,
|
sleepReportController.updateAll();
|
||||||
// padding: EdgeInsets.all(0),
|
},
|
||||||
// onTap: () async {
|
child: Column(
|
||||||
// sleepReportController.model.type =
|
mainAxisSize: MainAxisSize.max,
|
||||||
// 3;
|
children: [
|
||||||
// sleepReportController.updateAll();
|
Container(
|
||||||
// },
|
width:
|
||||||
// child: Column(
|
115.rpx, // 固定宽度为 160.rpx
|
||||||
// mainAxisSize: MainAxisSize.max,
|
alignment:
|
||||||
// children: [
|
Alignment.center, // 文字居中
|
||||||
// Container(
|
child: Text('月报'.tr,
|
||||||
// width: 115.rpx, // 固定宽度为 160.rpx
|
style: TextStyle(
|
||||||
// alignment:
|
fontFamily: 'Inter',
|
||||||
// Alignment.center, // 文字居中
|
fontSize: AppConstants()
|
||||||
// child: Text(
|
.title_text_fontSize,
|
||||||
// '月报'.tr,
|
letterSpacing: 0.0,
|
||||||
// style: FlutterFlowTheme.of(
|
color:
|
||||||
// context)
|
sleepReportController
|
||||||
// .bodyMedium
|
.model
|
||||||
// .override(
|
.type ==
|
||||||
// fontFamily: 'Inter',
|
3
|
||||||
// fontSize: AppConstants()
|
? themeController
|
||||||
// .title_text_fontSize,
|
.currentColor
|
||||||
// letterSpacing: 0.0,
|
.sc2
|
||||||
// color:
|
: themeController
|
||||||
// sleepReportController
|
.currentColor
|
||||||
// .model
|
.sc3,
|
||||||
// .type ==
|
)),
|
||||||
// 3
|
),
|
||||||
// ? themeController
|
SizedBox(height: 10.rpx),
|
||||||
// .currentColor
|
],
|
||||||
// .sc2
|
),
|
||||||
// : themeController
|
);
|
||||||
// .currentColor
|
}),
|
||||||
// .sc3,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10.rpx),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AnimatedPositioned(
|
AnimatedPositioned(
|
||||||
|
|||||||
@@ -14,23 +14,12 @@ import 'package:vbvs_app/controller/sleep/sleep_report_controller.dart';
|
|||||||
import 'package:vbvs_app/language/AppLanguage.dart';
|
import 'package:vbvs_app/language/AppLanguage.dart';
|
||||||
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
import 'package:vbvs_app/pages/common/selectDialog.dart';
|
||||||
import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart';
|
import 'package:vbvs_app/pages/main_bottom/component/main_page_b_bottom_change.dart';
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/AIAdviceWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/BreatheCard.dart';
|
import 'package:vbvs_app/pages/sleep_report/component/DailyDataWidget.dart';
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/BreathePauseNewWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/BreatheStandardWidget.dart';
|
import 'package:vbvs_app/pages/sleep_report/component/MonthDataWidget.dart';
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/CompareSleepWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/DiseasePercentsWidget.dart';
|
import 'package:vbvs_app/pages/sleep_report/component/WeekDataWidget.dart';
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/HeartChangeWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/HeartHealthWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/HeartPointWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/HeartRateCard.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/HeartRateStandardWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/SkinPercentWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/SleepCard.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/SleepScoreWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/SleepView.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/SnoreViewWidget.dart';
|
|
||||||
import 'package:vbvs_app/pages/sleep_report/component/ZiZhuShenJingPercentWidget.dart';
|
|
||||||
|
|
||||||
class NewSleepReportPage extends StatefulWidget {
|
class NewSleepReportPage extends StatefulWidget {
|
||||||
var data;
|
var data;
|
||||||
@@ -267,6 +256,53 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
sleepReportController.model.type =
|
sleepReportController.model.type =
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
String data = MyUtils.formatDate(
|
||||||
|
calendarController
|
||||||
|
.selectedDate.value!);
|
||||||
|
await requestWithLog(
|
||||||
|
logTitle: "查询睡眠报告",
|
||||||
|
method: MyHttpMethod.get,
|
||||||
|
queryUrl:
|
||||||
|
"https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.data['mac']}&time=${data}&type=${sleepReportController.model.type}",
|
||||||
|
onSuccess: (res) {
|
||||||
|
print(res);
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport
|
||||||
|
.value = res.data;
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
},
|
||||||
|
onFailure: (res) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() !=
|
||||||
|
null) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() ==
|
||||||
|
1) {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport.value = {};
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
print(res);
|
||||||
|
});
|
||||||
sleepReportController.updateAll();
|
sleepReportController.updateAll();
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -297,103 +333,185 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Obx(() {
|
||||||
|
return ClickableContainer(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
highlightColor: themeController
|
||||||
|
.currentColor.sc3,
|
||||||
|
borderRadius: 8.rpx,
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
onTap: () async {
|
||||||
|
sleepReportController.model.type =
|
||||||
|
2;
|
||||||
|
String data = MyUtils.formatDate(
|
||||||
|
calendarController
|
||||||
|
.selectedDate.value!);
|
||||||
|
await requestWithLog(
|
||||||
|
logTitle: "查询睡眠报告",
|
||||||
|
method: MyHttpMethod.get,
|
||||||
|
queryUrl:
|
||||||
|
"https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.data['mac']}&time=${data}&type=${sleepReportController.model.type}",
|
||||||
|
onSuccess: (res) {
|
||||||
|
print(res);
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport
|
||||||
|
.value = res.data;
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
},
|
||||||
|
onFailure: (res) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() !=
|
||||||
|
null) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() ==
|
||||||
|
1) {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport.value = {};
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
print(res);
|
||||||
|
});
|
||||||
|
sleepReportController.updateAll();
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width:
|
||||||
|
115.rpx, // 固定宽度为 160.rpx
|
||||||
|
alignment:
|
||||||
|
Alignment.center, // 文字居中
|
||||||
|
child: Text('周报'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontSize: AppConstants()
|
||||||
|
.title_text_fontSize,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
color:
|
||||||
|
sleepReportController
|
||||||
|
.model
|
||||||
|
.type ==
|
||||||
|
2
|
||||||
|
? themeController
|
||||||
|
.currentColor
|
||||||
|
.sc2
|
||||||
|
: themeController
|
||||||
|
.currentColor
|
||||||
|
.sc3,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
SizedBox(height: 10.rpx),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
Obx(() {
|
||||||
|
return ClickableContainer(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
highlightColor: themeController
|
||||||
|
.currentColor.sc3,
|
||||||
|
borderRadius: 8.rpx,
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
onTap: () async {
|
||||||
|
sleepReportController.model.type =
|
||||||
|
3;
|
||||||
|
String data = MyUtils.formatDate(
|
||||||
|
calendarController
|
||||||
|
.selectedDate.value!);
|
||||||
|
await requestWithLog(
|
||||||
|
logTitle: "查询睡眠报告",
|
||||||
|
method: MyHttpMethod.get,
|
||||||
|
queryUrl:
|
||||||
|
"https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.data['mac']}&time=${data}&type=${sleepReportController.model.type}",
|
||||||
|
onSuccess: (res) {
|
||||||
|
print(res);
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport
|
||||||
|
.value = res.data;
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
},
|
||||||
|
onFailure: (res) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() !=
|
||||||
|
null) {
|
||||||
|
if (MainPageBBottomChange
|
||||||
|
.getCurrentIndex() ==
|
||||||
|
1) {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopSlideNotification.show(
|
||||||
|
context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor:
|
||||||
|
themeController
|
||||||
|
.currentColor
|
||||||
|
.sc9);
|
||||||
|
}
|
||||||
|
sleepReportController
|
||||||
|
.sleepReport.value = {};
|
||||||
|
sleepReportController
|
||||||
|
.updateAll();
|
||||||
|
print(res);
|
||||||
|
});
|
||||||
|
|
||||||
// Obx(() {
|
sleepReportController.updateAll();
|
||||||
// return ClickableContainer(
|
},
|
||||||
// backgroundColor: Colors.transparent,
|
child: Column(
|
||||||
// highlightColor:
|
mainAxisSize: MainAxisSize.max,
|
||||||
// themeController.currentColor.sc3,
|
children: [
|
||||||
// borderRadius: 8.rpx,
|
Container(
|
||||||
// padding: EdgeInsets.all(0),
|
width:
|
||||||
// onTap: () async {
|
115.rpx, // 固定宽度为 160.rpx
|
||||||
// sleepReportController.model.type =
|
alignment:
|
||||||
// 2;
|
Alignment.center, // 文字居中
|
||||||
// sleepReportController.updateAll();
|
child: Text(
|
||||||
// },
|
'月报'.tr,
|
||||||
// child: Column(
|
style: TextStyle(
|
||||||
// mainAxisSize: MainAxisSize.max,
|
fontFamily: 'Inter',
|
||||||
// children: [
|
fontSize: AppConstants()
|
||||||
// Container(
|
.title_text_fontSize,
|
||||||
// width: 115.rpx, // 固定宽度为 160.rpx
|
letterSpacing: 0.0,
|
||||||
// alignment:
|
color: sleepReportController
|
||||||
// Alignment.center, // 文字居中
|
.model.type ==
|
||||||
// child: Text(
|
3
|
||||||
// '周报'.tr,
|
? themeController
|
||||||
// style: FlutterFlowTheme.of(
|
.currentColor.sc2
|
||||||
// context)
|
: themeController
|
||||||
// .bodyMedium
|
.currentColor.sc3,
|
||||||
// .override(
|
),
|
||||||
// fontFamily: 'Inter',
|
),
|
||||||
// fontSize: AppConstants()
|
),
|
||||||
// .title_text_fontSize,
|
SizedBox(height: 10.rpx),
|
||||||
// letterSpacing: 0.0,
|
],
|
||||||
// color:
|
),
|
||||||
// sleepReportController
|
);
|
||||||
// .model
|
}),
|
||||||
// .type ==
|
|
||||||
// 2
|
|
||||||
// ? themeController
|
|
||||||
// .currentColor
|
|
||||||
// .sc2
|
|
||||||
// : themeController
|
|
||||||
// .currentColor
|
|
||||||
// .sc3,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10.rpx),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
// Obx(() {
|
|
||||||
// return ClickableContainer(
|
|
||||||
// backgroundColor: Colors.transparent,
|
|
||||||
// highlightColor:
|
|
||||||
// themeController.currentColor.sc3,
|
|
||||||
// borderRadius: 8.rpx,
|
|
||||||
// padding: EdgeInsets.all(0),
|
|
||||||
// onTap: () async {
|
|
||||||
// sleepReportController.model.type =
|
|
||||||
// 3;
|
|
||||||
// sleepReportController.updateAll();
|
|
||||||
// },
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Container(
|
|
||||||
// width: 115.rpx, // 固定宽度为 160.rpx
|
|
||||||
// alignment:
|
|
||||||
// Alignment.center, // 文字居中
|
|
||||||
// child: Text(
|
|
||||||
// '月报'.tr,
|
|
||||||
// style: FlutterFlowTheme.of(
|
|
||||||
// context)
|
|
||||||
// .bodyMedium
|
|
||||||
// .override(
|
|
||||||
// fontFamily: 'Inter',
|
|
||||||
// fontSize: AppConstants()
|
|
||||||
// .title_text_fontSize,
|
|
||||||
// letterSpacing: 0.0,
|
|
||||||
// color:
|
|
||||||
// sleepReportController
|
|
||||||
// .model
|
|
||||||
// .type ==
|
|
||||||
// 3
|
|
||||||
// ? themeController
|
|
||||||
// .currentColor
|
|
||||||
// .sc2
|
|
||||||
// : themeController
|
|
||||||
// .currentColor
|
|
||||||
// .sc3,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// SizedBox(height: 10.rpx),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AnimatedPositioned(
|
AnimatedPositioned(
|
||||||
@@ -420,21 +538,6 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// Padding(
|
|
||||||
// padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
// 0, 0.rpx, 0.rpx, 0),
|
|
||||||
// child: Container(
|
|
||||||
// width: 28.rpx,
|
|
||||||
// height: 28.rpx,
|
|
||||||
// // width: double.infinity,
|
|
||||||
// decoration: BoxDecoration(),
|
|
||||||
// child: SvgPicture.asset(
|
|
||||||
// 'assets/img/icon/share.svg',
|
|
||||||
// fit: BoxFit.cover,
|
|
||||||
// color: themeController.currentColor.sc3,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -450,7 +553,7 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
padding: EdgeInsetsDirectional.fromSTEB(
|
||||||
30.rpx, 0.rpx, 30.rpx, 58.rpx),
|
30.rpx, 0.rpx, 30.rpx, 51.rpx),
|
||||||
child: ClickableContainer(
|
child: ClickableContainer(
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
widget.data['backgroundColor'] != null
|
widget.data['backgroundColor'] != null
|
||||||
@@ -616,176 +719,25 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (sleepReport.value == null ||
|
(sleepReport.value == null || sleepReport.value.isEmpty)
|
||||||
sleepReport.value.isEmpty)
|
? Container(
|
||||||
Container(
|
|
||||||
height: 500.rpx,
|
|
||||||
child: NullDataWidget(),
|
child: NullDataWidget(),
|
||||||
),
|
)
|
||||||
Padding(
|
: (sleepReportController.model.type == 1
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
? DailyDataWidget(sleepReport, sleepCardKey,
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
heartRateCardKey, breatheCardKey, widget.data)
|
||||||
child: Container(
|
: sleepReportController.model.type == 2
|
||||||
width: double.infinity,
|
? WeekDataWidget(sleepReport, widget.data)
|
||||||
child:
|
: sleepReportController.model.type == 3
|
||||||
SleepScoreWidget(sleepReport: sleepReport.value),
|
? MonthDataWidget(
|
||||||
),
|
sleepReport, widget.data)
|
||||||
),
|
: NullDataWidget()),
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: SleepViewWidget(
|
|
||||||
sleepReport: sleepReport,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
key: sleepCardKey,
|
|
||||||
width: double.infinity,
|
|
||||||
child: SleepCard(
|
|
||||||
sleepReport: sleepReport,
|
|
||||||
highlightItem: widget.data['itemName'] ?? null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: CompareSleepWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: HeartPointWidget(
|
|
||||||
sleepReport: sleepReport,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: AIAdviceWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child:
|
|
||||||
HeartRateStandardWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
key: heartRateCardKey,
|
|
||||||
width: double.infinity,
|
|
||||||
child: HeartRateCard(
|
|
||||||
sleepReport: sleepReport,
|
|
||||||
highlightItem: widget.data['itemName'] ?? null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: HeartChangeWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child:
|
|
||||||
BreatheStandardWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
key: breatheCardKey,
|
|
||||||
width: double.infinity,
|
|
||||||
child: BreatheCard(
|
|
||||||
sleepReport: sleepReport,
|
|
||||||
highlightItem: widget.data['itemName'] ?? null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child:
|
|
||||||
SnoreViewWidgetWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child:
|
|
||||||
BreathePauseNewWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: HeartHealthWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child:
|
|
||||||
DiseasePercentsWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: SkinPercentWidget(sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsetsDirectional.fromSTEB(
|
|
||||||
30.rpx, 0.rpx, 30.rpx, 0),
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
child: ZiZhuShenJingPercentWidget(
|
|
||||||
sleepReport: sleepReport),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
].divide(SizedBox(
|
].divide(SizedBox(
|
||||||
height: 25.rpx,
|
height: 25.rpx,
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -929,6 +881,57 @@ class _NewSleepReportPageState extends State<NewSleepReportPage> {
|
|||||||
calendarController.updateAll();
|
calendarController.updateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void onChangeArrowTap() {
|
||||||
|
if (type == 1) {
|
||||||
|
sleepReportController.selectedDate.value =
|
||||||
|
selectedDate.subtract(const Duration(days: 1));
|
||||||
|
} else if (type == 2) {
|
||||||
|
sleepReportController.selectedDate.value =
|
||||||
|
selectedDate.subtract(const Duration(days: 7));
|
||||||
|
} else if (type == 3) {
|
||||||
|
sleepReportController.selectedDate.value = DateTime(
|
||||||
|
selectedDate.year,
|
||||||
|
selectedDate.month - 1,
|
||||||
|
selectedDate.day,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
calendarController.selectedDate.value =
|
||||||
|
sleepReportController.selectedDate.value;
|
||||||
|
// String date = MyUtils.formatToDate(widget.data['date']);
|
||||||
|
String data = MyUtils.formatDate(calendarController.selectedDate.value!);
|
||||||
|
requestWithLog(
|
||||||
|
logTitle: "查询睡眠报告",
|
||||||
|
method: MyHttpMethod.get,
|
||||||
|
queryUrl:
|
||||||
|
"https://sleepdata.he-info.com/api/analysis/sleep/analysis?mac=${widget.data['mac']}&time=${data}&type=${sleepReportController.model.type}",
|
||||||
|
onSuccess: (res) {
|
||||||
|
print(res);
|
||||||
|
sleepReportController.sleepReport.value = res.data;
|
||||||
|
sleepReportController.updateAll();
|
||||||
|
},
|
||||||
|
onFailure: (res) {
|
||||||
|
if (MainPageBBottomChange.getCurrentIndex() != null) {
|
||||||
|
if (MainPageBBottomChange.getCurrentIndex() == 1) {
|
||||||
|
TopSlideNotification.show(context,
|
||||||
|
text: res.msg!,
|
||||||
|
textColor: themeController.currentColor.sc9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TopSlideNotification.show(context,
|
||||||
|
text: res.msg!, textColor: themeController.currentColor.sc9);
|
||||||
|
}
|
||||||
|
sleepReportController.sleepReport.value = {};
|
||||||
|
sleepReportController.updateAll();
|
||||||
|
print(res);
|
||||||
|
});
|
||||||
|
|
||||||
|
sleepReportController.updateAll();
|
||||||
|
calendarController.updateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
// import 'package:ef/ef.dart';
|
|
||||||
// import 'package:flutter/material.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/CustomCard.dart';
|
|
||||||
// import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
|
|
||||||
// import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
|
|
||||||
// import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
|
|
||||||
// import 'package:vbvs_app/controller/user_info_controller.dart';
|
|
||||||
// // import 'package:easydevice/easydevice.dart';
|
|
||||||
|
|
||||||
|
|
||||||
// class AboutUsPage extends StatefulWidget {
|
|
||||||
// const AboutUsPage({super.key});
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// State<AboutUsPage> createState() => _AboutUsPageState();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// class _AboutUsPageState extends State<AboutUsPage> {
|
|
||||||
// GlobalController globalController = Get.find();
|
|
||||||
// UserInfoController userInfoController = Get.find();
|
|
||||||
// BlueteethBindController blueteethBindController = Get.find();
|
|
||||||
// ThemeController themeController = Get.find();
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// void initState() {
|
|
||||||
// super.initState();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @override
|
|
||||||
// Widget build(BuildContext context) {
|
|
||||||
// return LayoutBuilder(
|
|
||||||
// builder: (context, bodySize) => GestureDetector(
|
|
||||||
// onTap: () => FocusScope.of(context).unfocus(),
|
|
||||||
// child: Container(
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// image: DecorationImage(
|
|
||||||
// image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
|
|
||||||
// fit: BoxFit.fill, // 填满整个 Container
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// child: Scaffold(
|
|
||||||
// backgroundColor: Colors.transparent, // 加上这一行
|
|
||||||
// appBar: AppBar(
|
|
||||||
// backgroundColor: themeController.currentColor.sc17,
|
|
||||||
// automaticallyImplyLeading: false,
|
|
||||||
// iconTheme: IconThemeData(
|
|
||||||
// color: themeController.currentColor.sc3,
|
|
||||||
// ),
|
|
||||||
// titleSpacing: 0,
|
|
||||||
// // leading: returnIconButtom,
|
|
||||||
// title: Container(
|
|
||||||
// width: double.infinity,
|
|
||||||
// height: 180.rpx,
|
|
||||||
// child: Stack(
|
|
||||||
// alignment: Alignment.center,
|
|
||||||
// children: [
|
|
||||||
// /// 居中标题
|
|
||||||
// Text(
|
|
||||||
// '关于我们.标题'.tr,
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontFamily: 'Readex Pro',
|
|
||||||
// color: themeController.currentColor.sc3,
|
|
||||||
// letterSpacing: 0,
|
|
||||||
// fontSize: 30.rpx,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
// /// 左边返回按钮
|
|
||||||
// Positioned(
|
|
||||||
// left: 0,
|
|
||||||
// child: returnIconButtom,
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
|
|
||||||
// actions: [],
|
|
||||||
// centerTitle: false,
|
|
||||||
// ),
|
|
||||||
// body: SafeArea(
|
|
||||||
// top: true,
|
|
||||||
// child: Padding(
|
|
||||||
// padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
|
|
||||||
// SizedBox(
|
|
||||||
// height: 30.rpx,
|
|
||||||
// ),
|
|
||||||
// Text(
|
|
||||||
// "企业简介\n\n\n嘉兴太和信息技术有限公司成立于2013年,是一家以传感技术、室内定位技术和人工智能技术为基础的国家高新技术企业,AI非接触生命体征传感器、高精度室内外一体定位平台、AI视频分析系统、射频消融等技术成果,目前已经拥有30多类知识产权证书,多项专利技术处于行业领先水平。\n\n\n我司研发的“非接触式生命体征传感器”是一款基于BCG信号原理,通过检测人体心脏搏动引起的微小振动的传感器系统。传感器系统通过将人体微弱的心跳、呼吸信号转换未电信号,进行相关生命体征分析。该传感器可为用户提供高灵敏度和精确度检测结构,适用于需要非接触式、高分辨率的监测场景。该系统的硬件、软件及生产维护均由我司自主开发和管理,拥有完全自主知识产权,并已申请多项国家专利,可依据用户需求定制个性化方案。\n\n\n该产品置于床垫下方使用,全程完全无感。采集的体征数据通过睡眠健康管理平台实时显示用户的健康状态,并对每次的睡眠报告进行系统化归档管理,支持长期查询。一旦用户在使用过程中出现异常情况,系统可及时做出判断并反馈预警信息和建议。目前,心率监测的准确度可达97%以上,呼吸监测的准确度可达95%以上,其他生理指标的监测精度也显著优于同类产品。该产品主体材质均采用符合国家标准的环保材料,部分硅胶配件达到食品级安全标准。产品尺寸可根据需求进行定制,适用于单人床、双人床、婴儿床、椅子及枕头等多种场景。\n\n\n睡眠健康管理平台通过实时预警与远程管理,提升睡眠质量及慢病干预效率,助力养老院、月子中心、康复中心、智能寝具等行业降本增效,实现精准健康的科学管理。",
|
|
||||||
// style: TextStyle(
|
|
||||||
// fontSize: AppConstants().normal_text_fontSize,
|
|
||||||
// color: themeController.currentColor.sc3),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
@@ -20,7 +20,8 @@ class _AboutUsPageState extends State<AboutUsPage> {
|
|||||||
// pdfController.loadPdf();
|
// pdfController.loadPdf();
|
||||||
|
|
||||||
widget.webView = MyWebView(
|
widget.webView = MyWebView(
|
||||||
url: AppConstants().ent_type == 1
|
url:
|
||||||
|
AppConstants().ent_type == 1
|
||||||
? "https://mp.weixin.qq.com/s/IAr4RNBy0hGJXGKyMxe7eQ"
|
? "https://mp.weixin.qq.com/s/IAr4RNBy0hGJXGKyMxe7eQ"
|
||||||
: "https://mp.weixin.qq.com/s/7BvvprVDqX1eOzM3Lms8dg",
|
: "https://mp.weixin.qq.com/s/7BvvprVDqX1eOzM3Lms8dg",
|
||||||
onLoad: () {
|
onLoad: () {
|
||||||
|
|||||||
Reference in New Issue
Block a user