后端 - 改用 EF Core Migrations(InitialCreate),Program.cs 用 MigrateAsync + AUTO_MIGRATE 开关 + 显式 MigrationsAssembly;移除 EnsureCreated、删除手写 DatabaseSchemaMigrator - 修复后台任务永久卡 Processing:三个队列对超时且达上限的任务原子标记 Failed - 修复报告重试失效:基础设施异常向上抛激活 RetryAsync,停机取消不计失败 - 修复 AI 用药天数 off-by-one(结束日为包含式) - AI 报告日志不再记录 VLM 健康正文 - 新增统一输入校验(ValidationException + 中间件映射 400):血压/心率/血糖/血氧/体重范围、药名/日期/服药时间去重、饮食热量与评分、运动时长上限 - 删除健康数据 AI 录入的影子直写路径,统一走 Service 校验 前端 - 新增 AppErrorState / AppFutureView,用药列表、服药打卡、运动计划、随访列表改为 加载/失败/空/数据 四态,失败可重试 瘦身 - 删除过时的 DataSeeder / DevDataSeeder 及调用 - 删除依赖实时服务的 ai_agent_tests 集成测试
55 lines
1.6 KiB
Dart
55 lines
1.6 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../core/app_colors.dart';
|
||
import 'app_error_state.dart';
|
||
|
||
/// 基于 FutureBuilder 的四态封装:统一处理"加载中 / 失败",
|
||
/// "空数据 / 有数据"交由 onData 自行判断(不同页面空态文案不同)。
|
||
///
|
||
/// onData 拿到非空数据后,自行判断 isEmpty 决定显示空态还是列表。
|
||
class AppFutureView<T> extends StatelessWidget {
|
||
final Future<T>? future;
|
||
final Widget Function(BuildContext context, T data) onData;
|
||
final VoidCallback? onRetry;
|
||
final Widget? loading;
|
||
final String? errorTitle;
|
||
|
||
const AppFutureView({
|
||
super.key,
|
||
required this.future,
|
||
required this.onData,
|
||
this.onRetry,
|
||
this.loading,
|
||
this.errorTitle,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return FutureBuilder<T>(
|
||
future: future,
|
||
builder: (ctx, snap) {
|
||
// 失败优先于其它判断,避免把错误显示成"暂无数据"
|
||
if (snap.hasError) {
|
||
return AppErrorState(
|
||
title: errorTitle ?? '加载失败',
|
||
onRetry: onRetry,
|
||
);
|
||
}
|
||
// 首次加载、尚无数据 → 转圈
|
||
if (snap.connectionState == ConnectionState.waiting && !snap.hasData) {
|
||
return loading ??
|
||
const Center(
|
||
child: CircularProgressIndicator(color: AppColors.primary),
|
||
);
|
||
}
|
||
if (!snap.hasData) {
|
||
return loading ??
|
||
const Center(
|
||
child: CircularProgressIndicator(color: AppColors.primary),
|
||
);
|
||
}
|
||
return onData(ctx, snap.data as T);
|
||
},
|
||
);
|
||
}
|
||
}
|