import { ref } from 'vue'; import { fetchTunnelInterpretation, fetchDeviceInterpretation } from './api'; import { createTrigger, type TriggerHandle } from './useDataInterpretationTrigger'; import type { TodoItem, ToolCallRecord } from './types'; /** 数据解读 hook —— 调用后端 SSE 流式接口,实时追踪所有过程状态 */ export function useDataInterpretation() { const loading = ref(false); const streamingText = ref(''); const error = ref(null); const threadId = ref(''); const sessionId = ref(''); // 过程状态 const thinking = ref(false); const todos = ref([]); const executingTools = ref([]); const toolCalls = ref([]); /** 统一的事件处理 */ function handleEvent(event: any) { switch (event.type) { case 'thinking': thinking.value = true; break; case 'executing': thinking.value = false; executingTools.value = event.tools || []; break; case 'tool_call': executingTools.value = []; toolCalls.value = [...toolCalls.value, { tool: event.tool || '', source: event.source || 'main', status: 'call' as const }]; break; case 'tool_result': // 更新最近一条 tool_call 为 result 状态 if (toolCalls.value.length > 0) { const last = toolCalls.value[toolCalls.value.length - 1]; if (last.tool === event.tool && last.status === 'call') { last.status = 'result'; } } break; case 'updated_todo_list': thinking.value = false; todos.value = event.todos || []; break; case 'token': thinking.value = false; if (event.content) { streamingText.value += event.content; } break; } } async function interpretTunnel(params: { tun_id: string; tun_name: string }) { loading.value = true; error.value = null; streamingText.value = ''; thinking.value = false; todos.value = []; executingTools.value = []; toolCalls.value = []; try { const result = await fetchTunnelInterpretation(params, handleEvent); threadId.value = result.thread_id; sessionId.value = result.session_id; } catch (e: any) { const msg = e?.message || '数据解读请求失败'; error.value = msg; throw e; } finally { loading.value = false; thinking.value = false; executingTools.value = []; } } async function interpretDevice(params: { device_id: string; device_name: string; device_type: string }) { loading.value = true; error.value = null; streamingText.value = ''; thinking.value = false; todos.value = []; executingTools.value = []; toolCalls.value = []; try { const result = await fetchDeviceInterpretation(params, handleEvent); threadId.value = result.thread_id; sessionId.value = result.session_id; } catch (e: any) { const msg = e?.message || '数据解读请求失败'; error.value = msg; throw e; } finally { loading.value = false; thinking.value = false; executingTools.value = []; } } function reset() { streamingText.value = ''; error.value = null; threadId.value = ''; sessionId.value = ''; thinking.value = false; todos.value = []; executingTools.value = []; toolCalls.value = []; } return { loading, streamingText, error, threadId, sessionId, thinking, todos, executingTools, toolCalls, interpretTunnel, interpretDevice, reset, }; } /** * 数据解读列表级便捷 Hook。 * 整合了弹框状态 + 每行数据的悬浮触发器管理 + SSE 流式解读。 * * @param mapParams - 将列表行数据映射为接口请求参数 * * 使用:在列表/表格/卡片组等 v-for 场景的组件中调用一次即可。 */ export function useDataInterpretationList( mapParams: (item: any) => { tun_id: string; tun_name: string } | { device_id: string; device_name: string; device_type: string } ) { const { loading, streamingText, error, threadId, sessionId, thinking, todos, executingTools, toolCalls, interpretTunnel, interpretDevice, reset } = useDataInterpretation(); const modalVisible = ref(false); const triggerMap = new Map(); function getTrigger(id: string): TriggerHandle { if (!triggerMap.has(id)) { triggerMap.set(id, createTrigger()); } return triggerMap.get(id)!; } async function handleInterpret(item: any) { modalVisible.value = true; const params = mapParams(item); if ('tun_id' in params) { await interpretTunnel(params); } else { await interpretDevice(params); } } function handleClose() { modalVisible.value = false; reset(); } return { modalVisible, loading, streamingText, error, threadId, sessionId, thinking, todos, executingTools, toolCalls, getTrigger, handleInterpret, handleClose, }; }