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:
@@ -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),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user