/** * SSE 流式接收工具(支持多事件类型) * 适用于 uni-app 微信小程序 * * 事件类型: * intent — 意图分类 * status — 思考状态 * content — 回答内容(默认) * meta — 元数据(来源、conversation_id) */ export function createSSE(url, data, options = {}) { const { headers = {}, onMessage = () => {}, onDone = () => {}, onError = () => {}, onIntent = () => {}, onStatus = () => {}, onMeta = () => {} } = options let buffer = '' let aborted = false let currentEvent = '' const defaultHeaders = { 'Content-Type': 'application/json', 'Accept': 'text/event-stream', } const token = uni.getStorageSync('access_token') if (token) { defaultHeaders['Authorization'] = 'Bearer ' + token } const task = uni.request({ url, method: 'POST', data, header: { ...defaultHeaders, ...headers }, enableChunked: true, responseType: 'text', success: () => { if (!aborted) onDone() }, fail: (err) => { if (!aborted) onError(err) } }) task.onChunkReceived((res) => { if (aborted) return try { const chunk = typeof res.data === 'string' ? res.data : new TextDecoder('utf-8').decode(res.data) buffer += chunk const lines = buffer.split('\n') buffer = lines.pop() || '' for (const line of lines) { if (line.startsWith('event: ')) { currentEvent = line.slice(7).trim() continue } if (!line.startsWith('data: ')) continue const d = line.slice(6) if (d === '[DONE]') { onDone(); return } if (currentEvent === 'intent') { onIntent(d) } else if (currentEvent === 'status') { onStatus(d) } else if (currentEvent === 'meta') { try { onMeta(JSON.parse(d)) } catch(e) {} } else { // content 或默认 onMessage(d) } currentEvent = '' } } catch (e) { console.error('[SSE] Parse error:', e) } }) return { abort: () => { aborted = true task.abort() } } } export default { createSSE }