|
...
|
...
|
@@ -231,16 +231,24 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
controller.toPremiumPage('今日-实时压力-立即解锁');
|
|
|
|
}
|
|
|
|
|
|
|
|
// 槽时间秒 → state 映射(与 _buildStressBarGroups 一致:取 value 最大那条 of state)
|
|
|
|
// 槽时间秒 → state 映射
|
|
|
|
// 同一 slot 内:优先"活跃"点(isWorkout / isSuspectedActivity / isMoving 任一为1),
|
|
|
|
// 活跃状态相同时取 value 更大的
|
|
|
|
const slotSec = 6 * 60;
|
|
|
|
final slotStateMap = <int, ({int value, int? state})>{};
|
|
|
|
final slotStateMap = <int, ({int value, int? state, bool active})>{};
|
|
|
|
for (final p in stressPoints) {
|
|
|
|
final sec = _tsToSecondsFromMidnight(p.time).round();
|
|
|
|
final slot = (sec / slotSec).round() * slotSec;
|
|
|
|
final v = p.value ?? 0;
|
|
|
|
final active = (p.isWorkout ?? 0) == 1 ||
|
|
|
|
(p.isSuspectedActivity ?? 0) == 1 ||
|
|
|
|
(p.isMoving ?? 0) == 1;
|
|
|
|
final existing = slotStateMap[slot];
|
|
|
|
if (existing == null || v > existing.value) {
|
|
|
|
slotStateMap[slot] = (value: v, state: p.state);
|
|
|
|
final shouldReplace = existing == null ||
|
|
|
|
(active && !existing.active) ||
|
|
|
|
(active == existing.active && v > existing.value);
|
|
|
|
if (shouldReplace) {
|
|
|
|
slotStateMap[slot] = (value: v, state: p.state, active: active);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Stack(
|
|
...
|
...
|
@@ -384,7 +392,7 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
style: TextStyle(
|
|
|
|
fontSize: 14,
|
|
|
|
color:
|
|
|
|
isWorkout == true ? Color(0xFF78787D) : _getColorByState(state),
|
|
|
|
isWorkout == true ? Color(0xFFB0B0B6) : _getColorByState(state),
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
...
|
...
|
@@ -413,7 +421,7 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
TextSpan(
|
|
|
|
text: '♥ ',
|
|
|
|
style: const TextStyle(
|
|
|
|
color: Color(0xFF78787D),
|
|
|
|
color: Color(0xFFB0B0B6),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
inherit: false,
|
|
...
|
...
|
@@ -466,7 +474,7 @@ class TodayHrvChartCard extends StatelessWidget { |
|
|
|
inherit: false,
|
|
|
|
fontFamilyFallback: null,
|
|
|
|
fontFamily: '',
|
|
|
|
color: Color(0xFF78787D),
|
|
|
|
color: Color(0xFFB0B0B6),
|
|
|
|
fontSize: 14,
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
),
|
|
...
|
...
|
@@ -939,7 +947,7 @@ class _StressBarChartState extends State<_StressBarChart> { |
|
|
|
bottom: chartHeight - gridBottom, // 修正:bottom 指的是距离 Stack 底部的像素值
|
|
|
|
child: IgnorePointer(
|
|
|
|
child: ColoredBox(
|
|
|
|
color: context.colors.textTertiary.withAlpha(125),
|
|
|
|
color: context.colors.textTertiary,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
...
|
...
|
@@ -1188,15 +1196,22 @@ class _StressBarChartState extends State<_StressBarChart> { |
|
|
|
const slotSec = 6 * 60; // 360 秒 = 6 分钟
|
|
|
|
final maxSec = widget.maxSeconds.round();
|
|
|
|
|
|
|
|
// 按槽分组,记录每槽的最大 value 及对应的 state / isWorkout
|
|
|
|
final slotMax = <int, ({int value, int? state, int? isWorkout})>{};
|
|
|
|
// 按槽分组,优先"活跃"点(isWorkout / isSuspectedActivity / isMoving 任一为1),
|
|
|
|
// 活跃状态相同时取 value 更大的
|
|
|
|
final slotMax = <int, ({int value, int? state, bool active})>{};
|
|
|
|
for (final p in stressPoints) {
|
|
|
|
final sec = TodayHrvChartCard._tsToSecondsFromMidnight(p.time).round();
|
|
|
|
final slot = (sec / slotSec).round() * slotSec;
|
|
|
|
final v = p.value ?? 0;
|
|
|
|
final active = (p.isWorkout ?? 0) == 1 ||
|
|
|
|
(p.isSuspectedActivity ?? 0) == 1 ||
|
|
|
|
(p.isMoving ?? 0) == 1;
|
|
|
|
final existing = slotMax[slot];
|
|
|
|
if (existing == null || v > existing.value) {
|
|
|
|
slotMax[slot] = (value: v, state: p.state, isWorkout: p.isWorkout);
|
|
|
|
final shouldReplace = existing == null ||
|
|
|
|
(active && !existing.active) ||
|
|
|
|
(active == existing.active && v > existing.value);
|
|
|
|
if (shouldReplace) {
|
|
|
|
slotMax[slot] = (value: v, state: p.state, active: active);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
...
|
...
|
@@ -1214,8 +1229,8 @@ class _StressBarChartState extends State<_StressBarChart> { |
|
|
|
toY: entry != null ? entry.value.toDouble() : 0,
|
|
|
|
width: barWidth,
|
|
|
|
color: entry != null
|
|
|
|
? (entry.isWorkout == 1
|
|
|
|
? const Color(0xFF78787D)
|
|
|
|
? (entry.active
|
|
|
|
? const Color(0xFFD9D9D9)
|
|
|
|
: widget.getColorByState(entry.state))
|
|
|
|
: Colors.transparent,
|
|
|
|
borderRadius: BorderRadius.circular(barWidth / 2),
|
...
|
...
|
|