后端 - 改用 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 集成测试
83 lines
2.6 KiB
Dart
83 lines
2.6 KiB
Dart
import 'package:flutter/material.dart';
|
||
import '../core/app_colors.dart';
|
||
|
||
/// 统一的"加载失败"状态——与 AppEmptyState 风格一致,区别在于明确表达"出错了,可重试",
|
||
/// 避免把网络/服务失败误显示成"暂无数据"。
|
||
class AppErrorState extends StatelessWidget {
|
||
final String title;
|
||
final String? subtitle;
|
||
final VoidCallback? onRetry;
|
||
|
||
const AppErrorState({
|
||
super.key,
|
||
this.title = '加载失败',
|
||
this.subtitle = '网络异常或服务暂时不可用,请稍后重试',
|
||
this.onRetry,
|
||
});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Center(
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(48),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Container(
|
||
width: 80,
|
||
height: 80,
|
||
decoration: BoxDecoration(
|
||
gradient: AppColors.softGlassGradient,
|
||
borderRadius: BorderRadius.circular(40),
|
||
border: Border.all(color: AppColors.borderLight),
|
||
boxShadow: AppColors.cardShadowLight,
|
||
),
|
||
child: const Icon(
|
||
Icons.cloud_off_outlined,
|
||
size: 34,
|
||
color: AppColors.primaryDark,
|
||
),
|
||
),
|
||
const SizedBox(height: 18),
|
||
Text(
|
||
title,
|
||
style: const TextStyle(
|
||
fontSize: 18,
|
||
fontWeight: FontWeight.w700,
|
||
color: AppColors.textPrimary,
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
if (subtitle != null) ...[
|
||
const SizedBox(height: 6),
|
||
Text(
|
||
subtitle!,
|
||
style: const TextStyle(
|
||
fontSize: 14,
|
||
color: AppColors.textSecondary,
|
||
),
|
||
textAlign: TextAlign.center,
|
||
),
|
||
],
|
||
if (onRetry != null) ...[
|
||
const SizedBox(height: 20),
|
||
OutlinedButton.icon(
|
||
onPressed: onRetry,
|
||
icon: const Icon(Icons.refresh, size: 18),
|
||
label: const Text('重试'),
|
||
style: OutlinedButton.styleFrom(
|
||
foregroundColor: AppColors.primaryDark,
|
||
side: const BorderSide(color: AppColors.borderLight),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(12),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
],
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|