From df69817eb07ce042cba279c81e1d0f79f2dd8587 Mon Sep 17 00:00:00 2001 From: luobinjie Date: Mon, 12 Jan 2026 16:28:11 +0800 Subject: [PATCH] =?UTF-8?q?ai=E8=AF=8A=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/api.js | 57 ++++++++- src/components/doctorCheck/ButtonList.vue | 66 +++++++--- src/components/sumDoctorCheck/ButtonList.vue | 128 +++++++++---------- 3 files changed, 158 insertions(+), 93 deletions(-) diff --git a/src/api/api.js b/src/api/api.js index 20ed9cf..bd113e6 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -1,4 +1,4 @@ -import request from "@/api/request"; +import request, { showFullScreenLoading, tryHideFullScreenLoading } from "@/api/request"; import store from "../store/index"; const sysConfig = getSysConfig() @@ -74,4 +74,59 @@ export async function putapi(url, params = {}, config) { }) .finally(() => {}); }); +} + +// 浏览器端 fetch 流式读取(用于增量处理服务器 chunked/text-stream 响应) +export async function fetchStream(url, params = {}, onChunk = (chunk) => {}) { + const fullUrl = `${sysConfig.apiurl}${url}`; + showFullScreenLoading(); + try { + const token = window.sessionStorage.getItem('token'); + const headers = { + 'Content-Type': 'application/json' + }; + if (token) headers['Authorization'] = `Bearer ${token}`; + + const resp = await fetch(fullUrl, { + method: 'POST', + headers, + body: JSON.stringify(params), + }); + + if (!resp.ok) { + const text = await resp.text().catch(() => ''); + throw new Error(`fetchStream HTTP ${resp.status} ${text}`); + } + + if (!resp.body || !resp.body.getReader) { + // 非流式返回,直接读取全部文本并回调一次 + const text = await resp.text(); + try { onChunk(text); } catch (e) { /* ignore */ } + // 已经拿到首个数据,先隐藏 loading + tryHideFullScreenLoading(); + return text; + } + + const reader = resp.body.getReader(); + const decoder = new TextDecoder('utf-8'); + let result = ''; + let firstChunkSeen = false; + while (true) { + const { value, done } = await reader.read(); + if (done) break; + const chunk = decoder.decode(value, { stream: true }); + result += chunk; + // 在收到第一个 chunk 时立即隐藏 loading + if (!firstChunkSeen) { + firstChunkSeen = true; + tryHideFullScreenLoading(); + } + try { onChunk(chunk); } catch (e) { /* 忽略回调内部错误 */ } + } + return result; + } catch (err) { + throw err; + } finally { + tryHideFullScreenLoading(); + } } \ No newline at end of file diff --git a/src/components/doctorCheck/ButtonList.vue b/src/components/doctorCheck/ButtonList.vue index c5d659d..4c6edcb 100644 --- a/src/components/doctorCheck/ButtonList.vue +++ b/src/components/doctorCheck/ButtonList.vue @@ -198,7 +198,7 @@