From 5dcebb77fd48656e927362f2aab86e7f3cf9a774 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Mon, 29 Jun 2026 07:04:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(stats):=20=E7=BB=9F=E8=AE=A1=E9=A1=B5?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E4=B8=8B=E6=8B=89=E6=8E=A5=E7=9C=9F=E5=AE=9E?= =?UTF-8?q?=E8=AE=BE=E5=A4=87(#10=20=E7=AC=AC=E2=91=A0=E5=B1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _DeviceDropdown 改 ConsumerWidget:菜单 = 「全部」+ 近 30 天有用量的真实设备名 (来自 deviceUsageProvider);选中态存 statsDeviceProvider。无每设备用量时只显示 「全部」(稀疏,正常)。第②层「按设备过滤月/周/日卡+折线」待 per-device 每日曲线 API(#10 第②层),本提交只接下拉内容。tablet 统计 golden 无变化(空设备时同旧观感)。 Co-Authored-By: Claude Opus 4.8 --- client/lib/screens/stats_page.dart | 38 +++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/client/lib/screens/stats_page.dart b/client/lib/screens/stats_page.dart index f7649ee..b9b319b 100644 --- a/client/lib/screens/stats_page.dart +++ b/client/lib/screens/stats_page.dart @@ -3,6 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../l10n/app_text.dart'; +import '../models/device_usage.dart'; import '../models/usage_point.dart'; import '../pangolin_theme.dart'; import '../state/account_providers.dart'; @@ -74,7 +76,7 @@ class StatsPage extends ConsumerWidget { style: PangolinText.display.copyWith(color: c.fg1, fontSize: 20, fontWeight: FontWeight.w700)) else const SizedBox.shrink(), - _DeviceDropdown(label: t.allDevices), + _DeviceDropdown(t: t), ]), const SizedBox(height: 14), PeriodCard(period: t.periodMonth, metrics: metricsFor(30)), @@ -129,16 +131,28 @@ class StatsPage extends ConsumerWidget { return ((bytes / mb).toStringAsFixed(1), 'MB'); } -/// 设备下拉(右上角):全部 + 近30天用过的设备名。 -/// 当前每设备归因未打通(usage_device_daily 常空),先只挂「全部」; -/// 设备注册打通后(TODO #9/#10)填充设备名 + 按设备过滤。 -class _DeviceDropdown extends StatelessWidget { - const _DeviceDropdown({required this.label}); - final String label; +/// 统计页选中的设备(null = 全部;否则 device uuid)。第②层(按设备过滤卡/图) +/// 待 per-device 每日曲线 API(#10);本层先把下拉内容接成真实设备。 +final statsDeviceProvider = StateProvider((ref) => null); + +/// 设备下拉(右上角):「全部」+ 近 30 天有用量的真实设备名(来自 deviceUsageProvider)。 +/// 无每设备用量时只显示「全部」(稀疏,正常)。 +class _DeviceDropdown extends ConsumerWidget { + const _DeviceDropdown({required this.t}); + final AppText t; @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final c = context.pangolin; + final devices = ref.watch(deviceUsageProvider(30)).valueOrNull ?? const []; + final selected = ref.watch(statsDeviceProvider); + String nameOf(String uuid) => devices + .firstWhere((d) => d.uuid == uuid, orElse: () => const DeviceUsage(uuid: '', name: '', platform: '', bytesUp: 0, bytesDown: 0, minutesUsed: 0)) + .name; + final label = selected == null + ? t.allDevices + : (nameOf(selected).isNotEmpty ? nameOf(selected) : t.allDevices); + return PopupMenuButton( tooltip: '', offset: const Offset(0, 38), @@ -147,8 +161,14 @@ class _DeviceDropdown extends StatelessWidget { borderRadius: BorderRadius.circular(PangolinRadius.md), side: BorderSide(color: c.border), ), + onSelected: (v) => ref.read(statsDeviceProvider.notifier).state = v.isEmpty ? null : v, itemBuilder: (_) => [ - PopupMenuItem(value: '', child: Text(label, style: PangolinText.sm.copyWith(color: c.fg1))), + PopupMenuItem(value: '', child: Text(t.allDevices, style: PangolinText.sm.copyWith(color: c.fg1))), + for (final d in devices) + PopupMenuItem( + value: d.uuid, + child: Text(d.name.isNotEmpty ? d.name : d.platform, style: PangolinText.sm.copyWith(color: c.fg1)), + ), ], child: Container( padding: const EdgeInsets.fromLTRB(12, 6, 10, 6),