From 5a040366da83d6ce07ce46aa1ff0326f3912efae Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Thu, 16 Jul 2026 11:03:15 +0800 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20usePolling=20=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E4=B8=B2=E8=A1=8C=E8=BD=AE=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一轮完成后才安排下一轮避免慢请求重叠,页面不可见时自动暂停、恢复后立即刷新,支持动态间隔与错误回调。 --- frontend/src/composables/usePolling.ts | 78 +++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/frontend/src/composables/usePolling.ts b/frontend/src/composables/usePolling.ts index e922131..bee684c 100644 --- a/frontend/src/composables/usePolling.ts +++ b/frontend/src/composables/usePolling.ts @@ -1,20 +1,82 @@ -import { useIntervalFn } from '@vueuse/core' +import { onBeforeUnmount, onMounted, ref } from 'vue' + +interface PollingOptions { + /** 启动后是否立即执行一次,默认立即执行。 */ + immediate?: boolean + /** 页面不可见时是否暂停,默认暂停。 */ + pauseWhenHidden?: boolean + /** 轮询函数抛错时的可选回调。 */ + onError?: (error: unknown) => void +} /** * 轮询 composable - * 封装 useIntervalFn,自动在组件卸载时清理 + * 串行轮询:上一轮完成后才安排下一轮,避免慢请求重叠和旧响应覆盖。 + * 页面进入后台时自动暂停,恢复可见后立即刷新;组件卸载时自动清理。 */ -export function usePolling(fn: () => void | Promise, interval = 5000, immediate = true) { - const { pause, resume } = useIntervalFn(fn, interval, { immediate }) +export function usePolling( + fn: () => void | Promise, + interval: number | (() => number) = 5000, + options: PollingOptions = {}, +) { + const { immediate = true, pauseWhenHidden = true, onError } = options + const isActive = ref(false) + const isRunning = ref(false) + let timer: ReturnType | null = null + + function clearTimer() { + if (!timer) return + clearTimeout(timer) + timer = null + } + + function schedule() { + clearTimer() + if (!isActive.value) return + if (pauseWhenHidden && document.hidden) return + const delay = typeof interval === 'function' ? interval() : interval + timer = setTimeout(() => void run(), Math.max(0, delay)) + } + + async function run() { + clearTimer() + if (!isActive.value || isRunning.value) return + if (pauseWhenHidden && document.hidden) return + + isRunning.value = true + try { + await fn() + } catch (error) { + onError?.(error) + } finally { + isRunning.value = false + schedule() + } + } function stop() { - pause() + isActive.value = false + clearTimer() } function start() { - if (immediate) fn() - resume() + if (isActive.value) return + isActive.value = true + if (immediate) void run() + else schedule() } - return { start, stop, pause, resume } + function handleVisibilityChange() { + if (!pauseWhenHidden || !isActive.value) return + if (document.hidden) clearTimer() + else void run() + } + + onMounted(() => document.addEventListener('visibilitychange', handleVisibilityChange)) + onBeforeUnmount(() => { + stop() + document.removeEventListener('visibilitychange', handleVisibilityChange) + }) + + return { start, stop, run, isActive, isRunning } } From ab9e87f948e82b62fb10d9aefaf345ab914c3c2e Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Thu, 16 Jul 2026 11:03:25 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=E6=8E=A8=E7=90=86=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E8=B6=85=E6=97=B6=E4=B8=8E=E6=89=93=E5=AD=97=E6=9C=BA?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E6=94=B9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 抽取 withTimeout 替代 Promise.race 超时控制,对比结果新增打字机逐字渲染与清理,推理聊天参数与列表类型同步收敛。 --- .../src/views/compare/CompareChatView.vue | 14 ++--- .../src/views/compare/CompareResultView.vue | 58 ++++++++++++++----- .../src/views/inference/InferenceChatView.vue | 37 +++++++----- .../src/views/inference/InferenceListView.vue | 16 +++-- 4 files changed, 83 insertions(+), 42 deletions(-) diff --git a/frontend/src/views/compare/CompareChatView.vue b/frontend/src/views/compare/CompareChatView.vue index d94176e..e701f6a 100644 --- a/frontend/src/views/compare/CompareChatView.vue +++ b/frontend/src/views/compare/CompareChatView.vue @@ -1,8 +1,9 @@ diff --git a/frontend/src/views/compare/CompareResultView.vue b/frontend/src/views/compare/CompareResultView.vue index e3cfd3f..bc9d620 100644 --- a/frontend/src/views/compare/CompareResultView.vue +++ b/frontend/src/views/compare/CompareResultView.vue @@ -1,14 +1,9 @@
{{ r.content }}
+
{{ r.displayContent }}
正在生成回答... @@ -212,6 +235,13 @@ onMounted(init) color: #f56c6c; } + .streaming-text { + min-height: 80px; + line-height: 1.7; + white-space: pre-wrap; + word-break: break-word; + } + .result-stats { display: flex; gap: 16px; diff --git a/frontend/src/views/inference/InferenceChatView.vue b/frontend/src/views/inference/InferenceChatView.vue index 679a0ce..7d6b056 100644 --- a/frontend/src/views/inference/InferenceChatView.vue +++ b/frontend/src/views/inference/InferenceChatView.vue @@ -1,5 +1,5 @@ From 4173b53b1b086ad56b74f409532dfb93d707938a Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Thu, 16 Jul 2026 11:03:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20=E5=89=8D=E7=AB=AF=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=8C=89=E9=9C=80=E5=8C=96=E4=B8=8E=20Mock=20?= =?UTF-8?q?=E6=87=92=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除全量 Element Plus 与全局 VChart 注册,改为按需引入样式与组件内局部图表;Mock 适配器改为按 VITE_ENABLE_MOCK 环境变量懒加载,生产默认不拦截请求;ECharts 精简为看板所需图表,各视图与 stores 同步适配。 --- frontend/README.md | 9 ++++ .../regression-training-log-layout.mjs | 3 +- frontend/src/App.vue | 6 ++- frontend/src/api/request.ts | 4 -- frontend/src/env.d.ts | 9 ++++ frontend/src/layouts/MainLayout.vue | 1 + frontend/src/main.ts | 32 ++++++----- frontend/src/plugins/echarts.ts | 10 +--- frontend/src/stores/models.ts | 21 +++++--- frontend/src/stores/system.ts | 13 ++++- .../src/views/dashboard/DashboardView.vue | 6 ++- .../views/eval/create/DimensionFormFields.vue | 11 ++-- .../src/views/fine-tune/FineTuneListView.vue | 20 +++---- frontend/src/views/login/LoginView.vue | 2 +- frontend/src/views/system/HardwareView.vue | 2 + frontend/src/views/system/LogsView.vue | 54 +++++++++---------- frontend/src/views/system/TrainingLogView.vue | 22 ++++---- frontend/vite.config.ts | 4 +- 18 files changed, 137 insertions(+), 92 deletions(-) diff --git a/frontend/README.md b/frontend/README.md index 85f9468..883fe95 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -26,6 +26,15 @@ npm run dev 后端 API 默认通过 Vite 代理转发到 `http://localhost:7861`(见 `vite.config.ts`)。 +开发环境默认启用前端 Mock。如需联调真实后端,使用: + +```bash +VITE_ENABLE_MOCK=false npm run dev +``` + +生产构建默认不包含 Mock;仅在演示构建中可显式设置 +`VITE_ENABLE_MOCK=true`。 + ## 构建 ```bash diff --git a/frontend/scripts/regression-training-log-layout.mjs b/frontend/scripts/regression-training-log-layout.mjs index 05245ea..215b245 100644 --- a/frontend/scripts/regression-training-log-layout.mjs +++ b/frontend/scripts/regression-training-log-layout.mjs @@ -117,7 +117,8 @@ assert.match( assert.match(source, /import \{ getSystemInfo \} from '@\/api\/modules\/system'/, '训练概览必须复用系统 GPU 监控数据源') assert.match(source, /Promise\.all\(\[datasetPromise, loadLog\(currentTask\), loadGpuStatus\(\)\]\)/, 'GPU 状态必须和训练日志一起刷新') assert.match(source, /if \(refreshInFlight\) return/, '轮询刷新必须阻止并发重叠') -assert.match(source, /onUnmounted\([\s\S]*?clearInterval\(timer\)/, '组件卸载时必须清理轮询定时器') +assert.match(source, /usePolling/, '训练日志必须使用统一轮询机制') +assert.match(source, /stopPolling\(\)/, '训练结束后必须停止轮询') const overview = findElements( templateAst, diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 856a59f..c9dc7e9 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,9 @@ diff --git a/frontend/src/api/request.ts b/frontend/src/api/request.ts index 3ee62a7..4c44b53 100644 --- a/frontend/src/api/request.ts +++ b/frontend/src/api/request.ts @@ -17,10 +17,6 @@ const service: AxiosInstance = axios.create({ timeout: 30000, }) -// 安装 mock adapter(拦截所有 axios 请求返回 mock 数据,方便前端独立开发调试) -import { installMockAdapter } from '@/mock/adapter' -installMockAdapter(service) - // 请求拦截器 service.interceptors.request.use( (config) => config, diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts index 323c78a..443a7fa 100644 --- a/frontend/src/env.d.ts +++ b/frontend/src/env.d.ts @@ -1,5 +1,14 @@ /// +interface ImportMetaEnv { + /** 是否启用前端 Mock;开发环境默认开启,生产环境默认关闭。 */ + readonly VITE_ENABLE_MOCK?: 'true' | 'false' +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} + declare module '*.vue' { import type { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, any> diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue index 0a67cda..6cb75d7 100644 --- a/frontend/src/layouts/MainLayout.vue +++ b/frontend/src/layouts/MainLayout.vue @@ -1,6 +1,7 @@ diff --git a/frontend/src/views/login/LoginView.vue b/frontend/src/views/login/LoginView.vue index aa89d4b..3b5e5e8 100644 --- a/frontend/src/views/login/LoginView.vue +++ b/frontend/src/views/login/LoginView.vue @@ -136,7 +136,7 @@ async function handleLogin() { position: relative; color: #fff; background-color: #121127; - background-image: url('@/assets/login-hero-flow.png'); + background-image: url('@/assets/login-hero-flow.jpg'); background-size: cover; background-position: center; overflow: hidden; diff --git a/frontend/src/views/system/HardwareView.vue b/frontend/src/views/system/HardwareView.vue index 765cfcd..0ea331c 100644 --- a/frontend/src/views/system/HardwareView.vue +++ b/frontend/src/views/system/HardwareView.vue @@ -1,6 +1,8 @@