日报周报月报

This commit is contained in:
czz
2025-07-07 14:58:54 +08:00
parent a4aa105a3a
commit 92e936d57f
22 changed files with 3209 additions and 723 deletions

View 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;
}