feat(stats): 统计页设备下拉接真实设备(#10 第①层)
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 4s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m6s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
ci-pangolin / Lint — shellcheck (push) Successful in 5s
ci-pangolin / OpenAPI Sync Check (push) Successful in 21s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (push) Successful in 6s
ci-pangolin / Flutter — analyze + test (push) Successful in 24s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (push) Successful in 5s
ci-pangolin / Codegen Drift — token 生成物未漂移 (push) Successful in 4s
ci-pangolin / Go — build + test (push) Successful in 9s
ci-pangolin / E2E Smoke — L4 进程级端到端 (push) Successful in 14s
ci-pangolin / Go — integration (mysql/redis testcontainers) (push) Failing after 4m6s
ci-pangolin / Golden — 视觉回归 (components + auth) (push) Successful in 14s
_DeviceDropdown 改 ConsumerWidget:菜单 = 「全部」+ 近 30 天有用量的真实设备名 (来自 deviceUsageProvider);选中态存 statsDeviceProvider。无每设备用量时只显示 「全部」(稀疏,正常)。第②层「按设备过滤月/周/日卡+折线」待 per-device 每日曲线 API(#10 第②层),本提交只接下拉内容。tablet 统计 golden 无变化(空设备时同旧观感)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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<String?>((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 <DeviceUsage>[];
|
||||
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<String>(
|
||||
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),
|
||||
|
||||
Reference in New Issue
Block a user