import { ref, computed } from 'vue' import { requestAiStream, parsePartialBloodCarePlainText } from '../utils/aiStreamRequest.js' const emptyBloodCarePlan = () => ({ summary: '', blood_advice: '', tcm_plan: '', watch_points: [], next_steps: '', control_level: '', control_label: '', disclaimer: '', analysis: [], source: '', date: '' }) /** AI 血糖照护建议(累计 7 天有记录后) */ export function useBloodCareAi(proxy, { diagnosisId, chartUseMockData, showUserToast, formatUserMessage }) { const bloodCarePlan = ref(emptyBloodCarePlan()) const bloodCareLoading = ref(false) const bloodCareStreaming = ref(false) const bloodCareStreamField = ref('') let careStreamBuffer = '' /** 用户点击后才展开照护建议卡片 */ const bloodCarePanelOpen = ref(false) const bloodCareEligible = computed(() => { return !!diagnosisId.value && !chartUseMockData.value }) const hasBloodCareContent = computed(() => { const p = bloodCarePlan.value return !!(p.summary || p.blood_advice || p.tcm_plan) }) const bloodCareAnalysis = computed(() => { const list = bloodCarePlan.value?.analysis return Array.isArray(list) ? list : [] }) const bloodCareControlLabel = computed(() => { const label = String(bloodCarePlan.value?.control_label || '').trim() return label }) const bloodCareDisclaimer = computed(() => { return String(bloodCarePlan.value?.disclaimer || '').trim() }) function detectCareStreamField(text) { const tail = String(text || '').match(/(?:^|\n)(总评|血糖|中医|留意|复诊)[::]\s*([^\n]*)$/) if (!tail) return 'summary' const map = { '总评': 'summary', '血糖': 'blood_advice', '中医': 'tcm_plan', '留意': 'watch_points', '复诊': 'next_steps' } return map[tail[1]] || 'summary' } function mergePartialCareFromStream() { const partial = parsePartialBloodCarePlainText(careStreamBuffer) const next = { ...bloodCarePlan.value } for (const key of ['summary', 'blood_advice', 'tcm_plan', 'next_steps']) { if (partial[key] != null) next[key] = partial[key] } if (Array.isArray(partial.watch_points)) next.watch_points = partial.watch_points bloodCarePlan.value = next bloodCareStreamField.value = detectCareStreamField(careStreamBuffer) } function applyBloodCarePayload(payload) { if (!payload) return const prevAnalysis = bloodCarePlan.value.analysis bloodCarePlan.value = { ...emptyBloodCarePlan(), ...payload, watch_points: Array.isArray(payload.watch_points) ? payload.watch_points : [], analysis: Array.isArray(payload.analysis) && payload.analysis.length ? payload.analysis : (Array.isArray(prevAnalysis) ? prevAnalysis : []) } } function onBloodCareStreamEvent(event, payload) { if (event === 'analysis') { if (Array.isArray(payload?.analysis)) { bloodCarePlan.value = { ...bloodCarePlan.value, analysis: payload.analysis } } } else if (event === 'delta') { bloodCareStreaming.value = true if (payload.buffer != null) { careStreamBuffer = payload.buffer } else { careStreamBuffer += payload.text || '' } mergePartialCareFromStream() } else if (event === 'done') { applyBloodCarePayload(payload) careStreamBuffer = '' bloodCareStreamField.value = '' } else if (event === 'error') { showUserToast(formatUserMessage(payload?.message, '照护建议加载失败')) } } async function fetchBloodCareAiFallback(refresh) { const res = await proxy.apiUrl({ url: '/api/tcm/dailyBloodCareAiRecommend', method: 'GET', timeout: 35000, data: { diagnosis_id: diagnosisId.value, refresh: refresh ? 1 : 0, ...(refresh ? { _t: Date.now() } : {}) } }, false) if (res?.code === 1 && res.data) { applyBloodCarePayload(res.data) } else if (res?.msg) { showUserToast(res.msg) } } function closeBloodCarePanel() { bloodCarePanelOpen.value = false } /** 趋势图 AI 按钮:未展开则打开并生成;已展开则收起 */ async function toggleBloodCareFromChart() { if (!bloodCareEligible.value || bloodCareLoading.value) return if (bloodCarePanelOpen.value) { closeBloodCarePanel() return } bloodCarePanelOpen.value = true if (!hasBloodCareContent.value) { await fetchBloodCareAi(false) } } async function fetchBloodCareAi(refresh = false) { if (!bloodCareEligible.value || bloodCareLoading.value) return bloodCareLoading.value = true bloodCareStreaming.value = false careStreamBuffer = '' bloodCareStreamField.value = '' if (refresh) { applyBloodCarePayload(emptyBloodCarePlan()) } try { await requestAiStream({ baseUrl: proxy.$url, url: '/api/tcm/dailyBloodCareAiRecommendStream', method: 'GET', data: { diagnosis_id: diagnosisId.value, refresh: refresh ? 1 : 0, ...(refresh ? { _t: Date.now() } : {}) }, onEvent: onBloodCareStreamEvent, fallback: () => fetchBloodCareAiFallback(refresh) }) } catch (e) { try { await fetchBloodCareAiFallback(refresh) } catch (e2) { showUserToast('照护建议加载失败') } } finally { bloodCareLoading.value = false bloodCareStreaming.value = false bloodCareStreamField.value = '' } } function refreshBloodCareAi() { if (bloodCareLoading.value) return fetchBloodCareAi(true) } function resetBloodCarePlan() { applyBloodCarePayload(emptyBloodCarePlan()) careStreamBuffer = '' bloodCareStreamField.value = '' bloodCarePanelOpen.value = false } return { bloodCarePlan, bloodCareLoading, bloodCareStreaming, bloodCareStreamField, bloodCareEligible, bloodCarePanelOpen, hasBloodCareContent, bloodCareAnalysis, bloodCareControlLabel, bloodCareDisclaimer, fetchBloodCareAi, toggleBloodCareFromChart, closeBloodCarePanel, refreshBloodCareAi, resetBloodCarePlan } }