feat: UI全面改造 + 后端多项修复
【后端修复】 - 删除diet/consultation agent假工具声明 - 修复DayOfWeek实体注释 - 修复Vision API content序列化 - cleanup_service级联删除修复 - 用药提醒时区偏差修复 - 统一DateTime处理(UtcNow+8) - 新增UTC DateTime JSON转换器 【前端UI重构】 - 配色体系全面更新(#8B5CF6淡紫+#F0ECFF背景) - 登录页重设计 - 首页重设计(透明顶栏、渐变背景、胶囊输入区) - 聊天卡片加白蓝边框、渐变标题 - 侧边栏重构(渐变背景、合并顶部、删除底部设置) - 确认卡片可编辑字段恢复 - 所有子页面加返回按钮 - catch异常加日志 - 删除后refresh provider缓存
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import '../../core/app_colors.dart';
|
||||
import '../../core/app_theme.dart';
|
||||
import '../../core/navigation_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
@@ -129,7 +130,9 @@ class ReportNotifier extends Notifier<ReportState> {
|
||||
);
|
||||
}).toList();
|
||||
state = state.copyWith(reports: reports);
|
||||
} catch (_) {}
|
||||
} catch (e) {
|
||||
debugPrint('[Report] 加载报告列表失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void fetchReportDetail(String reportId) async {
|
||||
@@ -234,6 +237,7 @@ class ReportListPage extends ConsumerWidget {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(icon: const Icon(Icons.arrow_back), onPressed: () => popRoute(ref)),
|
||||
title: const Text('看报告'),
|
||||
centerTitle: true,
|
||||
),
|
||||
@@ -311,7 +315,7 @@ class ReportListPage extends ConsumerWidget {
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F2FF),
|
||||
color: AppColors.primaryLight,
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
),
|
||||
child: const Icon(Icons.file_open, size: 48, color: AppTheme.primaryLight),
|
||||
@@ -319,7 +323,7 @@ class ReportListPage extends ConsumerWidget {
|
||||
const SizedBox(height: 20),
|
||||
const Text('暂无检查报告', style: TextStyle(fontSize: 21, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(height: 8),
|
||||
const Text('点击下方按钮上传报告', style: TextStyle(fontSize: 17, color: Color(0xFF999999))),
|
||||
const Text('点击下方按钮上传报告', style: TextStyle(fontSize: 17, color: AppColors.textHint)),
|
||||
]),
|
||||
);
|
||||
}
|
||||
@@ -328,24 +332,24 @@ class ReportListPage extends ConsumerWidget {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [BoxShadow(color: AppTheme.primary.withAlpha(10), blurRadius: 4, offset: const Offset(0, 2))],
|
||||
color: AppTheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rLg),
|
||||
boxShadow: [AppTheme.shadowCard],
|
||||
),
|
||||
child: ListTile(
|
||||
leading: Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F2FF),
|
||||
color: AppColors.primaryLight,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: _getReportIcon(report.type),
|
||||
),
|
||||
title: Text(report.title, style: const TextStyle(fontSize: 19, fontWeight: FontWeight.w600)),
|
||||
subtitle: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(report.type, style: TextStyle(fontSize: 17, color: Colors.grey[500])),
|
||||
Text(_formatDate(report.uploadedAt), style: TextStyle(fontSize: 15, color: Colors.grey[400])),
|
||||
Text(report.type, style: TextStyle(fontSize: 17, color: AppColors.textSecondary)),
|
||||
Text(_formatDate(report.uploadedAt), style: TextStyle(fontSize: 15, color: AppColors.textHint)),
|
||||
]),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -354,38 +358,36 @@ class ReportListPage extends ConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDCFCE7),
|
||||
color: AppColors.successLight,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: const Text('已审核', style: TextStyle(fontSize: 13, color: Color(0xFF43A047))),
|
||||
child: const Text('已审核', style: TextStyle(fontSize: 13, color: AppColors.success)),
|
||||
)
|
||||
else if (!report.hasAnalysis)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE3F2FD),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: AppTheme.primaryLight,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rXs),
|
||||
),
|
||||
child: const Text('分析中', style: TextStyle(fontSize: 13, color: Color(0xFF2196F3))),
|
||||
child: Text('分析中', style: TextStyle(fontSize: 13, color: AppTheme.primary)),
|
||||
)
|
||||
else if (report.status == 'PendingDoctor')
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF3E0),
|
||||
color: AppColors.warningLight,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: const Text('待审核', style: TextStyle(fontSize: 13, color: Color(0xFFF59E0B))),
|
||||
child: const Text('待审核', style: TextStyle(fontSize: 13, color: AppColors.warning)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
report.hasAnalysis
|
||||
? const Icon(Icons.check_circle, size: 23, color: Color(0xFF43A047))
|
||||
: const Icon(Icons.arrow_forward_ios, size: 21, color: Color(0xFFCCCCCC)),
|
||||
? Icon(Icons.check_circle, size: 23, color: AppColors.success)
|
||||
: Icon(Icons.arrow_forward_ios, size: 21, color: AppColors.textHint),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
pushRoute(ref, 'reportDetail', params: {'id': report.id});
|
||||
},
|
||||
onTap: () => pushRoute(ref, 'reportDetail', params: {'id': report.id}),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -487,23 +489,23 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFDCFCE7),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFF43A047).withAlpha(50)),
|
||||
color: AppColors.successLight,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rLg),
|
||||
border: Border.all(color: AppColors.success.withAlpha(50)),
|
||||
),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Row(children: [
|
||||
const Text('✅', style: TextStyle(fontSize: 23)),
|
||||
const SizedBox(width: 8),
|
||||
const Text('医生审核意见', style: TextStyle(fontSize: 19, fontWeight: FontWeight.w600, color: Color(0xFF2E7D32))),
|
||||
const Text('医生审核意见', style: TextStyle(fontSize: 19, fontWeight: FontWeight.w600, color: AppColors.success)),
|
||||
]),
|
||||
if (report.doctorName != null) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text('审核医生:${report.doctorName}', style: const TextStyle(fontSize: 16, color: Color(0xFF4CAF50))),
|
||||
Text('审核医生:${report.doctorName}', style: TextStyle(fontSize: 16, color: AppColors.success)),
|
||||
],
|
||||
if (report.reviewedAt != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text('审核时间:${report.reviewedAt!.toLocal().toString().substring(0, 19)}', style: const TextStyle(fontSize: 15, color: Color(0xFF81C784))),
|
||||
Text('审核时间:${report.reviewedAt!.toLocal().toString().substring(0, 19)}', style: TextStyle(fontSize: 15, color: AppColors.success.withAlpha(180))),
|
||||
],
|
||||
if (report.severity != null) ...[
|
||||
const SizedBox(height: 6),
|
||||
@@ -515,10 +517,10 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: AppTheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rMd),
|
||||
),
|
||||
child: Text('💬 ${report.doctorComment!}', style: const TextStyle(fontSize: 18, color: Color(0xFF1A1A1A), height: 1.5)),
|
||||
child: Text('💬 ${report.doctorComment!}', style: TextStyle(fontSize: 18, color: AppColors.textPrimary, height: 1.5)),
|
||||
),
|
||||
],
|
||||
if (report.doctorRecommendation != null && report.doctorRecommendation!.isNotEmpty) ...[
|
||||
@@ -527,10 +529,10 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: AppTheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rMd),
|
||||
),
|
||||
child: Text('📝 ${report.doctorRecommendation!}', style: const TextStyle(fontSize: 17, color: Color(0xFF333333), height: 1.5)),
|
||||
child: Text('📝 ${report.doctorRecommendation!}', style: TextStyle(fontSize: 17, color: AppColors.textPrimary, height: 1.5)),
|
||||
),
|
||||
],
|
||||
]),
|
||||
@@ -539,15 +541,15 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
|
||||
Widget _severityBadge(String severity) {
|
||||
final (label, color, bg) = switch (severity) {
|
||||
'Normal' => ('🟢 正常', const Color(0xFF43A047), const Color(0xFFDCFCE7)),
|
||||
'Abnormal' => ('🟡 轻度异常', const Color(0xFFF59E0B), const Color(0xFFFFF3E0)),
|
||||
'Severe' => ('🟠 中度异常', const Color(0xFFEF4444), const Color(0xFFFEE2E2)),
|
||||
'Critical' => ('🔴 重度异常', const Color(0xFFDC2626), const Color(0xFFFEE2E2)),
|
||||
_ => (severity, Colors.grey, Colors.grey.withAlpha(30)),
|
||||
'Normal' => ('🟢 正常', AppColors.success, AppColors.successLight),
|
||||
'Abnormal' => ('🟡 轻度异常', AppColors.warning, AppColors.warningLight),
|
||||
'Severe' => ('🟠 中度异常', AppColors.error, AppColors.errorLight),
|
||||
'Critical' => ('🔴 重度异常', AppColors.error, AppColors.errorLight),
|
||||
_ => (severity, AppColors.textSecondary, AppColors.border),
|
||||
};
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(6)),
|
||||
decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(AppTheme.rXs)),
|
||||
child: Text(label, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, color: color)),
|
||||
);
|
||||
}
|
||||
@@ -556,8 +558,8 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F2FF),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: AppColors.primaryLight,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rLg),
|
||||
),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Row(children: [
|
||||
@@ -566,7 +568,7 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(analysis.reportType, style: const TextStyle(fontSize: 21, fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 4),
|
||||
const Text('AI 预解读结果', style: TextStyle(fontSize: 17, color: Color(0xFF666666))),
|
||||
Text('AI 预解读结果', style: TextStyle(fontSize: 17, color: AppColors.textSecondary)),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
@@ -576,9 +578,9 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
Widget _buildAnalysisSection(ReportAnalysis analysis) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: const Color(0xFFD8DCFD), width: 1.5),
|
||||
color: AppTheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rLg),
|
||||
border: Border.all(color: AppColors.border, width: 1),
|
||||
),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Padding(
|
||||
@@ -593,18 +595,18 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF3E0),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: const Color(0xFFFFE0B2)),
|
||||
color: AppColors.warningLight,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rXs),
|
||||
border: Border.all(color: AppColors.warning.withAlpha(80)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.info_outline, size: 19, color: Color(0xFFE65100)),
|
||||
Icon(Icons.info_outline, size: 19, color: AppColors.warning),
|
||||
const SizedBox(width: 6),
|
||||
const Text(
|
||||
Text(
|
||||
'AI 预解读 · 待医生确认',
|
||||
style: TextStyle(fontSize: 16, color: Color(0xFFE65100), fontWeight: FontWeight.w500),
|
||||
style: TextStyle(fontSize: 16, color: AppColors.warning, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -620,28 +622,28 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
IconData statusIcon;
|
||||
switch (ind.status) {
|
||||
case 'high':
|
||||
statusColor = const Color(0xFFE53935);
|
||||
statusColor = AppColors.error;
|
||||
statusIcon = Icons.arrow_upward;
|
||||
break;
|
||||
case 'low':
|
||||
statusColor = const Color(0xFFF9A825);
|
||||
statusColor = AppColors.warning;
|
||||
statusIcon = Icons.arrow_downward;
|
||||
break;
|
||||
default:
|
||||
statusColor = const Color(0xFF43A047);
|
||||
statusColor = AppColors.success;
|
||||
statusIcon = Icons.check_circle;
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Color(0xFFF0F0F0)))),
|
||||
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: AppColors.border))),
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(ind.name, style: const TextStyle(fontSize: 17)),
|
||||
if (ind.referenceRange != null)
|
||||
Text('参考值: ${ind.referenceRange}', style: TextStyle(fontSize: 15, color: Colors.grey[400])),
|
||||
Text('参考值: ${ind.referenceRange}', style: TextStyle(fontSize: 15, color: AppColors.textHint)),
|
||||
]),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
@@ -657,25 +659,25 @@ class _ReportDetailPageState extends ConsumerState<ReportDetailPage> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFEF3C7),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
color: AppColors.warningLight,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rLg),
|
||||
),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Row(children: [
|
||||
const Text('💡', style: TextStyle(fontSize: 23)),
|
||||
const SizedBox(width: 8),
|
||||
const Text('综合解读', style: TextStyle(fontSize: 19, fontWeight: FontWeight.w600, color: Color(0xFFD97706))),
|
||||
Text('综合解读', style: TextStyle(fontSize: 19, fontWeight: FontWeight.w600, color: AppColors.warning)),
|
||||
]),
|
||||
const SizedBox(height: 12),
|
||||
Text(analysis.summary, style: const TextStyle(fontSize: 17, color: Color(0xFF92400E), height: 1.6)),
|
||||
Text(analysis.summary, style: TextStyle(fontSize: 17, color: AppColors.warning, height: 1.6)),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: AppTheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppTheme.rSm),
|
||||
),
|
||||
child: const Text('⚠️ AI 解读仅供参考,请以医生诊断为准', style: TextStyle(fontSize: 16, color: Color(0xFFD97706))),
|
||||
child: Text('⚠️ AI 解读仅供参考,请以医生诊断为准', style: TextStyle(fontSize: 16, color: AppColors.warning)),
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user