- 安全:AI聊天历史、患者档案、设备同步数据统一走 secureSet/secureGet 加密存储 - 合规:TabBar "消息" 改为 "助手" 消除命名误导 - 合规:新增 .env.production 模板配置 HTTPS API URL - UX:AI发送按钮 40→44px、反馈按钮 32→44px、协议勾选框 44px 点击热区 - UX:5处硬编码 10-12px 字号替换为 design token(DoctorTabBar/ShortcutButton/TodoAlert/mall) - UX:6处安全区域写法统一(全部使用 --tk-page-padding/--tk-tabbar-space + env fallback) - 新增 safe-bottom-padded / safe-bottom-tabbar 两个 mixin Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
95 lines
2.8 KiB
TypeScript
95 lines
2.8 KiB
TypeScript
import { api } from './request';
|
|
import { secureGet, secureSet } from '@/utils/secure-storage';
|
|
|
|
export interface AiChatMessage {
|
|
id: string;
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface AiChatResponse {
|
|
reply: string;
|
|
message_id: string;
|
|
display_hints?: DisplayHint[];
|
|
}
|
|
|
|
export type DisplayHint =
|
|
| { type: 'vital_card'; indicator_type: string; values: [string, number][]; unit: string }
|
|
| { type: 'lab_report_card'; report_date: string; abnormal_count: number }
|
|
| { type: 'action_confirm'; action_type: string; summary: string; confirm_payload: unknown }
|
|
| { type: 'risk_alert'; level: string; message: string }
|
|
| { type: 'trend_chart'; metrics: string[]; period: string; summary: string }
|
|
| { type: 'insight_card'; title: string; severity: string; items: string[] }
|
|
| { type: 'patient_profile'; chronic_conditions: string[]; medication_count: number }
|
|
| { type: 'text' };
|
|
|
|
/** 发送消息给 AI 客服 */
|
|
export async function sendAiMessage(
|
|
message: string,
|
|
history?: AiChatMessage[],
|
|
patientId?: string,
|
|
sessionId?: string,
|
|
): Promise<AiChatResponse> {
|
|
const body: Record<string, unknown> = {
|
|
message,
|
|
history: history?.slice(-10),
|
|
};
|
|
if (patientId) {
|
|
body.patient_id = patientId;
|
|
}
|
|
if (sessionId) {
|
|
body.session_id = sessionId;
|
|
}
|
|
const resp = await api.post<AiChatResponse>('/ai/chat', body);
|
|
return resp;
|
|
}
|
|
|
|
// === 会话 API ===
|
|
|
|
export interface AiChatSession {
|
|
id: string;
|
|
title: string | null;
|
|
patient_id: string | null;
|
|
status: string;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export async function createSession(patientId?: string, title?: string): Promise<AiChatSession> {
|
|
const body: Record<string, unknown> = {};
|
|
if (patientId) body.patient_id = patientId;
|
|
if (title) body.title = title;
|
|
return api.post<AiChatSession>('/ai/chat/sessions', body);
|
|
}
|
|
|
|
export async function listSessions(): Promise<AiChatSession[]> {
|
|
return api.get<AiChatSession[]>('/ai/chat/sessions');
|
|
}
|
|
|
|
export async function renameSession(sessionId: string, title: string): Promise<void> {
|
|
await api.put(`/ai/chat/sessions/${sessionId}/rename`, { title });
|
|
}
|
|
|
|
export async function closeSession(sessionId: string): Promise<void> {
|
|
await api.post(`/ai/chat/sessions/${sessionId}/close`, {});
|
|
}
|
|
|
|
/** 获取聊天历史(本地加密缓存) */
|
|
export function getLocalHistory(): AiChatMessage[] {
|
|
try {
|
|
const raw = secureGet('ai_chat_history');
|
|
return raw ? JSON.parse(raw) : [];
|
|
} catch (err) {
|
|
console.warn('[ai-chat] 读取本地聊天历史失败:', err);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** 保存聊天历史到本地(加密存储) */
|
|
export function saveLocalHistory(messages: AiChatMessage[]): void {
|
|
try {
|
|
secureSet('ai_chat_history', JSON.stringify(messages.slice(-100)));
|
|
} catch (err) { console.warn('[ai-chat] 保存本地聊天历史失败:', err); }
|
|
}
|