refactor: 死代码清理 + 趋势图重构 + 侧边栏交互优化 + 智能体卡片去延迟

- 死代码清理: 删除 AppCard/AppMenuItem/AppTabChip/AppButtons 等 4 个未使用 Widget 文件; 清理 common_widgets/enterprise_widgets/app_status_badge 中未使用 class; 移除 HealthService 等未使用方法; 后端移除 ExerciseService.CreateFromItemsAsync/HealthArchiveService.GetOrCreateAsync/MedicationAgentHandler 死分支/ChatRequest DTO
- 趋势图: 直线折线 + 渐变填充 + 阴影; 去网格; X 轴日+月份; Y 轴整数刻度最多 4 个; 点击数据点/录入记录显示上方浮层; 选中点变实心
- 侧边栏: 对话记录改单选删除(灰底高亮); 操作栏浮层修复触摸问题; 常用功能/健康仪表盘间距收紧; _Panel 去外框
- 智能体: 欢迎卡片去掉 400ms 延迟; 胶囊去阴影
- 其他: api_client IP 适配; 多页面 UI 微调; 新增 chat_provider/prelaunch_guardrails 测试
This commit is contained in:
MingNian
2026-07-09 15:04:02 +08:00
parent 1c020b8ae5
commit d82e006cf4
37 changed files with 1842 additions and 2096 deletions

View File

@@ -7,11 +7,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import '../../core/app_colors.dart';
import '../../core/app_design_tokens.dart';
import '../../core/app_module_visuals.dart';
import '../../core/app_theme.dart';
import '../../core/api_client.dart' show baseUrl;
import '../../core/navigation_provider.dart';
import '../../providers/auth_provider.dart';
import '../../widgets/common_widgets.dart';
import '../../widgets/enterprise_widgets.dart';
final reportProvider = NotifierProvider<ReportNotifier, ReportState>(
@@ -447,8 +449,26 @@ class ReportListPage extends ConsumerWidget {
if (state.reports.isEmpty)
_buildEmptyState(context)
else
...state.reports.map(
(report) => _buildReportCard(context, ref, report),
_ReportListGroup(
children: [
for (var i = 0; i < state.reports.length; i++)
SwipeDeleteTile(
key: Key(state.reports[i].id),
onDelete: () => ref
.read(reportProvider.notifier)
.deleteReport(state.reports[i].id),
onTap: () => pushRoute(
ref,
'aiAnalysis',
params: {'id': state.reports[i].id},
),
margin: EdgeInsets.zero,
child: _buildReportRow(
state.reports[i],
showDivider: i < state.reports.length - 1,
),
),
],
),
],
),
@@ -461,7 +481,7 @@ class ReportListPage extends ConsumerWidget {
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.error.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(14),
borderRadius: AppRadius.mdBorder,
border: Border.all(color: AppColors.error.withValues(alpha: 0.22)),
),
child: Row(
@@ -576,7 +596,7 @@ class ReportListPage extends ConsumerWidget {
height: 100,
decoration: BoxDecoration(
color: _reportBlue.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(50),
borderRadius: AppRadius.pillBorder,
),
child: Icon(_reportVisual.icon, size: 44, color: _reportBlue),
),
@@ -599,126 +619,93 @@ class ReportListPage extends ConsumerWidget {
);
}
Widget _buildReportCard(
BuildContext context,
WidgetRef ref,
ReportItem report,
) {
Widget _buildReportRow(ReportItem report, {required bool showDivider}) {
final displayTitle = (report.title == 'Other' || report.title == 'other')
? '检查报告'
: report.title;
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.border, width: 1.1),
boxShadow: [AppTheme.shadowLight],
),
child: Material(
color: Colors.transparent,
borderRadius: BorderRadius.circular(16),
child: InkWell(
onTap: () => pushRoute(ref, 'aiAnalysis', params: {'id': report.id}),
borderRadius: BorderRadius.circular(16),
child: Padding(
padding: const EdgeInsets.all(15),
child: Row(
children: [
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: _reportBlue.withValues(alpha: 0.10),
borderRadius: BorderRadius.circular(14),
border: Border.all(
return Material(
color: Colors.white,
child: InkWell(
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 13),
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: _reportBlue.withValues(alpha: 0.10),
borderRadius: AppRadius.mdBorder,
),
child: Icon(
_reportVisual.icon,
size: 24,
color: _reportBlue,
),
),
child: Icon(_reportVisual.icon, size: 26, color: _reportBlue),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
displayTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w800,
color: AppColors.textPrimary,
),
),
const SizedBox(height: 4),
Text(
_formatDate(report.uploadedAt),
style: const TextStyle(
fontSize: 14,
color: AppColors.textHint,
),
),
if (report.aiStatus == 'Failed') ...[
const SizedBox(height: 4),
const SizedBox(width: 13),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_failureSummary(report),
maxLines: 2,
displayTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 13,
height: 1.35,
color: AppColors.error,
fontWeight: FontWeight.w600,
style: AppTextStyles.listTitle.copyWith(
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 3),
Text(
_formatDate(report.uploadedAt),
style: AppTextStyles.listSubtitle,
),
if (report.aiStatus == 'Failed') ...[
const SizedBox(height: 4),
Text(
_failureSummary(report),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 13,
height: 1.35,
color: AppColors.error,
fontWeight: FontWeight.w600,
),
),
],
],
],
),
),
),
_buildStatusBadge(report),
const SizedBox(width: 4),
IconButton(
onPressed: () => _confirmDelete(context, ref, report.id),
visualDensity: VisualDensity.compact,
icon: const Icon(
Icons.delete_outline,
size: 20,
const SizedBox(width: 8),
_buildStatusBadge(report),
const SizedBox(width: 8),
const Icon(
Icons.chevron_right,
size: 21,
color: AppColors.textHint,
),
),
],
],
),
),
),
if (showDivider)
const Padding(
padding: EdgeInsets.only(left: 71),
child: Divider(
height: 1,
thickness: 0.7,
color: Color(0xFFE8ECF2),
),
),
],
),
),
);
}
void _confirmDelete(BuildContext context, WidgetRef ref, String id) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('删除报告'),
content: const Text('确定要删除这份报告吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(ctx);
ref.read(reportProvider.notifier).deleteReport(id);
},
child: const Text('删除', style: TextStyle(color: AppColors.error)),
),
],
),
);
}
Widget _buildStatusBadge(ReportItem report) {
final (label, bg, fg) = switch (report.status) {
_ when report.reviewStatus == 'Reviewed' => (
@@ -748,7 +735,7 @@ class ReportListPage extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(999),
borderRadius: AppRadius.pillBorder,
border: Border.all(color: fg.withValues(alpha: 0.12)),
),
child: Text(
@@ -776,6 +763,24 @@ class ReportListPage extends ConsumerWidget {
}
}
class _ReportListGroup extends StatelessWidget {
final List<Widget> children;
const _ReportListGroup({required this.children});
@override
Widget build(BuildContext context) {
return Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: AppRadius.xlBorder,
),
child: Column(children: children),
);
}
}
String _catTitle(String c) => switch (c) {
'BloodTest' => '抽血化验单',
'Biochemistry' => '生化检验报告',
@@ -786,20 +791,20 @@ String _catTitle(String c) => switch (c) {
_ => '检查报告',
};
class ReportOriginalPage extends StatelessWidget {
class ReportOriginalPage extends ConsumerWidget {
final String url;
final String title;
const ReportOriginalPage({super.key, required this.url, this.title = '原始报告'});
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final imageUrl = _absoluteUrl(url);
return GradientScaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
onPressed: () => popRoute(ref),
),
title: Text(title),
),