import { nextTick, ref } from 'vue' import { useSystemState } from './useSystemState.js' import { runOrchestrator } from '../services/orchestrator.js' const initialMessages = [ { id: 1, role: 'agent', text: '我已读取当前报销、发票、行程和制度命中情况。当前建议:优先处理即将超时与高风险单据。' }, { id: 2, role: 'user', text: '请列出今天最需要关注的风险。' }, { id: 3, role: 'agent', text: '主要风险包括:3 笔单据将在 30 分钟内超时,市场部存在 2 笔高风险差旅报销,另有 1 笔报销缺少酒店入住清单。' } ] export const prompts = ['生成审批意见', '列出补件清单', '解释风险原因', '生成运营简报'] export function useChat(activeView) { const { currentUser } = useSystemState() const messages = ref([...initialMessages]) const draft = ref('') const uploadedFiles = ref([]) const messageList = ref(null) const activeCase = ref(null) const sending = ref(false) function agentReply(text) { const c = activeCase.value if (text.includes('超时') || text.includes('SLA')) { return '当前最紧急的是 3 笔即将超时单据,建议先按剩余处理时长排序,并把缺附件单据转给申请人补齐。' } if (text.includes('高风险') || text.includes('风险')) { return '高风险主要集中在市场部差旅报销,风险点包括住宿超标、重复发票疑似命中、行程说明缺失。建议人工复核后再通过。' } if (text.includes('部门')) { return '从待处理金额看,销售部与研发中心占比最高;从异常占比看,市场部更需要优先关注。' } if (text.includes('简报')) { return '运营简报:今日待审批 12 单,高风险 4 单,平均审批 5.6h,SLA 达成率 96%。建议优先处理差旅报销和即将超时单据。' } if (text.includes('补件') || text.includes('附件')) { return '建议补件清单:酒店入住水单、完整行程单、发票原件或验真结果、直属经理确认记录。' } if (text.includes('审批意见')) { return c ? `${c.id} 建议审批意见:费用归属与预算中心基本匹配,请补充必要说明后通过。` : '建议审批意见:当前单据存在待确认项,请先完成风险核查和附件补齐后再审批。' } return '收到。我可以继续帮你拆解异常原因、比较部门趋势、生成审批意见,或整理一份今日报销运营简报。' } function scrollToBottom() { nextTick(() => messageList.value?.scrollTo({ top: messageList.value.scrollHeight, behavior: 'smooth' })) } function replaceMessage(id, nextMessage) { const index = messages.value.findIndex((item) => item.id === id) if (index === -1) { messages.value.push(nextMessage) return } messages.value.splice(index, 1, nextMessage) } function buildOrchestratorMeta(payload) { const items = [] if (payload?.selected_agent) { items.push(`Agent: ${payload.selected_agent}`) } if (payload?.permission_level) { items.push(`权限: ${payload.permission_level}`) } if (payload?.trace_summary?.tool_count) { items.push(`工具: ${payload.trace_summary.tool_count}`) } if (payload?.trace_summary?.degraded) { items.push('已降级') } if (payload?.requires_confirmation) { items.push('待确认') } if (payload?.run_id) { items.push(`Run: ${payload.run_id}`) } return items } function buildAssistantMessage(payload, fallbackText) { const result = payload?.result || {} return { text: result.answer || result.message || fallbackText, meta: buildOrchestratorMeta(payload), citations: Array.isArray(result.citations) ? result.citations : [], suggestedActions: Array.isArray(result.suggested_actions) ? result.suggested_actions : [], draftPayload: result.draft_payload || null, riskFlags: Array.isArray(result.risk_flags) ? result.risk_flags : [] } } async function sendMessage() { const text = draft.value.trim() if (!text || sending.value) return false const userMessageId = Date.now() const pendingMessageId = userMessageId + 1 messages.value.push({ id: userMessageId, role: 'user', text }) draft.value = '' sending.value = true messages.value.push({ id: pendingMessageId, role: 'agent', text: 'Orchestrator 正在处理中...', meta: ['运行中'] }) scrollToBottom() const user = currentUser.value || {} try { const payload = await runOrchestrator({ source: 'user_message', user_id: user.username || user.name || 'anonymous', message: text, context_json: { role_codes: Array.isArray(user.roleCodes) ? user.roleCodes : [], is_admin: Boolean(user.isAdmin), name: user.name || '', role: user.role || '', active_case_id: activeCase.value?.id || '' } }) const assistantMessage = buildAssistantMessage(payload, 'Orchestrator 已完成处理。') replaceMessage(pendingMessageId, { id: pendingMessageId, role: 'agent', ...assistantMessage }) } catch (error) { replaceMessage(pendingMessageId, { id: pendingMessageId, role: 'agent', text: `后端暂时不可用,已切换为本地占位回复:${agentReply(text)}`, meta: [error?.message || '后端调用失败'], citations: [], suggestedActions: [], draftPayload: null, riskFlags: [] }) } finally { sending.value = false scrollToBottom() } return true } function handleUpload(event) { uploadedFiles.value = Array.from(event.target.files ?? []).map((file) => ({ name: file.name, size: file.size })) if (uploadedFiles.value.length) { const names = uploadedFiles.value.map((file) => file.name).join('、') messages.value.push({ id: Date.now(), role: 'agent', text: `已接收 ${uploadedFiles.value.length} 个附件:${names}。我会优先核对发票验真、费用标准、预算归属和必要审批材料。` }) scrollToBottom() } } function openChat(request) { activeCase.value = request activeView.value = 'chat' nextTick(() => messageList.value?.scrollTo({ top: messageList.value.scrollHeight })) } function openNewChat() { activeCase.value = null activeView.value = 'chat' } return { messages, draft, uploadedFiles, messageList, activeCase, prompts, sending, sendMessage, handleUpload, openChat, openNewChat, scrollToBottom } }