更新
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user