01ebafc05e
布局重做(对照确认稿): - 3 张周期卡(本月/本周/今日),每张 = ↓下行 · ↑上行 · 使用时长(PeriodCard) - 右上设备下拉(_DeviceDropdown,先只挂「全部」;每设备归因打通后填设备名,见 TODO #9/#10) - 柱状图 → 折线图:最近两周(14 天)按天流量(UsageLineChart,CustomPainter 零依赖) 数据源统一 + 修刷新(根治「本月流量冻住」): - 月/周/日 + 折线全从单一源 usageProvider(30) 聚合(取最后 N 天点求和,末点即今日, 避开 UTC/本地日期坑);不再 me.weekly + usage 两条管线 - usageProvider/deviceUsageProvider 改 autoDispose(进页重取)+ 下拉刷新(invalidate) + 刷新连带 meProvider.refresh。后端每 60s 更新,无需更勤 UI 全走 PangolinColors/Text/Spacing token 单源、零硬编码;中英 l10n key 补齐。 widget 数值断言测试(刀1)按新结构重写并通过;统计/连接页 golden 重生成 (非 CI 闸;components/auth 基线未动)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.4 KiB
Dart
69 lines
2.4 KiB
Dart
// period_card.dart — 周期统计卡(本月/本周/今日):左侧周期名 + 横排 3 指标。
|
|
// 沿用 MetricCard 的 surface+border+shadow 与 mono 数值观感,3 张竖排成统计页主体。
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../pangolin_theme.dart';
|
|
|
|
/// 一个指标:数值 + 单位 + 标签(下行/上行/使用时长)。
|
|
class StatMetric {
|
|
const StatMetric(this.value, this.unit, this.label);
|
|
final String value, unit, label;
|
|
}
|
|
|
|
class PeriodCard extends StatelessWidget {
|
|
const PeriodCard({super.key, required this.period, required this.metrics});
|
|
|
|
/// 周期名(本月/本周/今日)。
|
|
final String period;
|
|
final List<StatMetric> metrics;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final c = context.pangolin;
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
|
decoration: BoxDecoration(
|
|
color: c.surface,
|
|
borderRadius: BorderRadius.circular(PangolinRadius.lg),
|
|
border: Border.all(color: c.border),
|
|
boxShadow: PangolinShadow.sm,
|
|
),
|
|
child: Row(children: [
|
|
SizedBox(
|
|
width: 52,
|
|
child: Text(period,
|
|
style: PangolinText.sm.copyWith(color: c.fg1, fontWeight: FontWeight.w600, fontSize: 14)),
|
|
),
|
|
for (var i = 0; i < metrics.length; i++)
|
|
Expanded(
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
decoration: i == 0
|
|
? null
|
|
: BoxDecoration(border: Border(left: BorderSide(color: c.border))),
|
|
child: Column(children: [
|
|
Text.rich(
|
|
TextSpan(
|
|
text: metrics[i].value,
|
|
style: PangolinText.mono.copyWith(fontSize: 19, color: c.fg1, fontWeight: FontWeight.w500),
|
|
children: [
|
|
TextSpan(
|
|
text: ' ${metrics[i].unit}',
|
|
style: PangolinText.caption.copyWith(color: c.fg3, fontSize: 11),
|
|
),
|
|
],
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(metrics[i].label,
|
|
textAlign: TextAlign.center,
|
|
style: PangolinText.caption.copyWith(color: c.fg2, fontSize: 11)),
|
|
]),
|
|
),
|
|
),
|
|
]),
|
|
);
|
|
}
|
|
}
|