feat: AI 提示词模块化 + AI 同意门控 + AI 草稿存储 + 意图路由 + 医疗引用知识库 + 多页面 UI 优化
- AI 提示词拆分为 markdown 模块(Prompts/global + modules + rag + router) - 新增 AI 同意门控(用户需同意后才能使用 AI 功能) - 新增 AI 录入草稿存储(AiEntryDraftContracts/EfAiEntryDraftStore/AiEntryDraftRecord) - 新增 AI 意图路由(ai_intent_router) - 新增医疗引用知识库(MedicalCitationKnowledge) - 重构 prompt_manager 和 ai_chat_endpoints - 优化聊天/趋势/档案/抽屉/医生端等多个页面 UI - 新增 agent 插画和趋势指标图标资源 - 删除 HANDOFF-2026-07-17.md
This commit is contained in:
133
health_app/lib/pages/settings/ai_consent_details_page.dart
Normal file
133
health_app/lib/pages/settings/ai_consent_details_page.dart
Normal file
@@ -0,0 +1,133 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../core/app_colors.dart';
|
||||
import '../../core/app_design_tokens.dart';
|
||||
import '../../providers/ai_consent_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
|
||||
class AiConsentDetailsPage extends ConsumerWidget {
|
||||
const AiConsentDetailsPage({super.key});
|
||||
|
||||
Future<void> _revoke(BuildContext context, WidgetRef ref) async {
|
||||
final userId = ref.read(authProvider).user?.id;
|
||||
if (userId == null || userId.isEmpty) return;
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('撤回 AI 授权?'),
|
||||
content: const Text('撤回后将退出当前账号,重新使用 App 前需要再次完成 AI 数据授权。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: const Text('撤回并退出'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (confirmed != true || !context.mounted) return;
|
||||
|
||||
await ref.read(aiConsentProvider.notifier).revoke(userId);
|
||||
await ref.read(authProvider.notifier).logout();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final consent = ref.watch(aiConsentProvider);
|
||||
final canRevoke = consent.granted && ref.watch(authProvider).user != null;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
),
|
||||
title: const Text(
|
||||
'AI 数据授权说明',
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 32),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_section(
|
||||
'我们会处理什么数据',
|
||||
'当你使用 AI 对话、饮食识别或报告预整理功能时,可能会处理你主动输入的文字、健康数据、健康档案摘要、图片和 PDF 报告。',
|
||||
),
|
||||
_section(
|
||||
'发送给哪些服务商',
|
||||
'DeepSeek:用于生成 AI 对话和健康信息整理。\n\n通义千问视觉服务:用于识别你主动上传的饮食图片、报告图片或 PDF 内容。\n\nFastGPT:用于检索与问题相关的医学参考资料。',
|
||||
),
|
||||
_section(
|
||||
'使用目的',
|
||||
'上述数据仅用于提供 AI 对话、健康记录辅助、饮食图片识别、医学报告预整理和医学资料检索功能。AI 内容仅供健康管理参考,不能替代医生诊断或治疗。',
|
||||
),
|
||||
_section(
|
||||
'你的选择',
|
||||
'你可以在首次进入首页时选择是否授权。未授权时无法进入本 App 的主要服务。你也可以在设置中撤回授权;撤回后将退出当前账号,再次使用前需要重新授权。',
|
||||
),
|
||||
_section(
|
||||
'更多信息',
|
||||
'数据保存、删除、第三方服务保护措施和联系方式,请同时查看 App 内《隐私政策》和《第三方 SDK 共享清单》。',
|
||||
),
|
||||
if (canRevoke) ...[
|
||||
const SizedBox(height: 12),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton(
|
||||
onPressed: () => _revoke(context, ref),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.error,
|
||||
side: BorderSide(
|
||||
color: AppColors.error.withValues(alpha: 0.4),
|
||||
),
|
||||
minimumSize: const Size.fromHeight(50),
|
||||
),
|
||||
child: const Text('撤回 AI 数据授权'),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _section(String title, String body) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 22),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: AppTextStyles.sectionTitle),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
body,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
height: 1.65,
|
||||
color: AppColors.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user