feat: make core pages local-first offline

This commit is contained in:
2026-08-21 12:40:01 +08:00
parent 8828851631
commit fefa2d74e6
20 changed files with 921 additions and 188 deletions
+29 -11
View File
@@ -1,4 +1,5 @@
import 'dart:math' as math;
import 'dart:async';
import 'package:miaoji_zhang/shared/services/shanghai_time.dart';
import 'package:flutter/material.dart';
@@ -8,6 +9,7 @@ import 'package:miaoji_zhang/shared/services/current_ledger_store.dart';
import 'package:miaoji_zhang/shared/theme/app_theme.dart';
import 'package:miaoji_zhang/shared/widgets/app_controls.dart';
import 'package:miaoji_zhang/shared/widgets/async_error_view.dart';
import 'package:miaoji_zhang/shared/widgets/backend_status_icon.dart';
import 'package:miaoji_zhang/shared/widgets/category_icon.dart';
class StatsPage extends StatefulWidget {
@@ -23,12 +25,13 @@ class _StatsPageState extends State<StatsPage> {
PeriodStats? _stats;
String? _error;
bool _loading = true;
int _loadRevision = 0;
@override
void initState() {
super.initState();
CurrentLedgerStore.instance.addListener(_load);
_load();
unawaited(_load());
}
@override
@@ -38,6 +41,7 @@ class _StatsPageState extends State<StatsPage> {
}
Future<void> _load() async {
final revision = ++_loadRevision;
if (mounted) {
setState(() {
_loading = true;
@@ -45,12 +49,25 @@ class _StatsPageState extends State<StatsPage> {
});
}
try {
final stats = await TxApi.periodStats(_period, _anchor);
if (mounted) setState(() => _stats = stats);
final local = TxApi.periodStatsLocal(_period, _anchor);
if (mounted && revision == _loadRevision) {
setState(() => _stats = local);
}
} catch (error) {
if (mounted) setState(() => _error = apiErrorMessage(error));
if (mounted && revision == _loadRevision) {
setState(() => _error = apiErrorMessage(error));
}
}
try {
final remote = await TxApi.periodStatsRemote(_period, _anchor);
if (mounted && revision == _loadRevision) {
setState(() => _stats = remote);
}
} catch (_) {
} finally {
if (mounted) setState(() => _loading = false);
if (mounted && revision == _loadRevision) {
setState(() => _loading = false);
}
}
}
@@ -93,7 +110,10 @@ class _StatsPageState extends State<StatsPage> {
Widget build(BuildContext context) {
final stats = _stats;
return Scaffold(
appBar: AppBar(title: Text('统计')),
appBar: AppBar(
title: Text('统计'),
actions: [BackendStatusIcon(onRetry: _load)],
),
body: Column(
children: [
Padding(
@@ -105,7 +125,7 @@ class _StatsPageState extends State<StatsPage> {
JzOption(value: 'month', label: ''),
JzOption(value: 'year', label: ''),
],
onChanged: _loading ? null : _selectPeriod,
onChanged: _selectPeriod,
),
),
Expanded(
@@ -171,7 +191,7 @@ class _StatsPageState extends State<StatsPage> {
children: [
IconButton(
tooltip: '上一个周期',
onPressed: _loading ? null : () => _shift(-1),
onPressed: () => _shift(-1),
icon: Icon(Icons.chevron_left_rounded),
),
SizedBox(
@@ -184,9 +204,7 @@ class _StatsPageState extends State<StatsPage> {
),
IconButton(
tooltip: '下一个周期',
onPressed: _loading || _isAfterCurrentPeriod(nextAnchor)
? null
: () => _shift(1),
onPressed: _isAfterCurrentPeriod(nextAnchor) ? null : () => _shift(1),
icon: Icon(Icons.chevron_right_rounded),
),
],