From ad2270821cd38f0f5f24f2963953c539487b25e6 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Sun, 30 Aug 2026 10:46:05 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E8=BF=9B=E5=BA=A6=E9=A1=B5?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=BA=BF=E8=B6=8A=E7=95=8C+=E5=9B=A0?= =?UTF-8?q?=E5=AD=90=E4=BB=BB=E5=8A=A1=E6=97=A5=E5=BF=97=E7=A9=BA(?= =?UTF-8?q?=E7=94=A8=E6=88=B708-30=E9=AA=8C=E6=94=B6=E4=B8=A4=E5=A4=84)=20?= =?UTF-8?q?[vps]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ①时间线:原「末节点flex 0 0 auto+bar宽100%相对本节点」=4号点贴屏幕右缘、 3号bar越界伸出容器 → 全节点等宽flex:1(dot落各等分区中心),bar改 left:calc(50%+16px)/width:calc(100%-32px)精确连接相邻dot。 ②因子任务日志空:log端点读的{db目录}/{tid}.log因子任务从不写 → 心跳 追加流水:_report_progress同时append [HH:MM:SS] stage·detail 到 {tid}.progress.log,/task/{id}/log factor_前缀优先返流水(行情加载i/N、 因子i/M、tears✓即用户要看的运行过程);189测+build绿 --- frontend/src/views/backtest/Progress.vue | 12 +++++++----- sanguo_api/routes.py | 16 +++++++++++++++- sanguo_factor/analyzer.py | 8 +++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/frontend/src/views/backtest/Progress.vue b/frontend/src/views/backtest/Progress.vue index 983f44a..dbc9caa 100644 --- a/frontend/src/views/backtest/Progress.vue +++ b/frontend/src/views/backtest/Progress.vue @@ -180,7 +180,6 @@ const pageTitle = computed(() => (isFactor.value ? '因子分析' : isOptimize.v v-for="(s, i) in (isFactor ? fxSteps : btSteps)" :key="s.key" class="tl-node" :class="[isFactor ? fxStepState(i) : btStepState(i), { failed: status === 'failed' && i === (isFactor ? fxCurrentIdx : btCurrentIdx) }]" - :style="{ flex: i === (isFactor ? fxSteps : btSteps).length - 1 ? '0 0 auto' : '1' }" >
@@ -242,19 +241,22 @@ const pageTitle = computed(() => (isFactor.value ? '因子分析' : isOptimize.v .hb-pct { font-size: 30px; font-weight: 700; color: var(--brand); font-variant-numeric: tabular-nums; flex-shrink: 0; } .hb-pct .pct-sign { font-size: 14px; color: var(--text-3); } -/* ===== 时间线 ===== */ +/* ===== 时间线 ===== + 全节点等宽(flex:1)→dot 落在各等分区中心;连接线从本区 dot 右 16px 到 + 下一区 dot 左 16px(等宽下精确对齐;原「末节点不伸缩+bar 宽 100% 越界」 + 会把 4 顶到屏幕右缘且 bar 伸出容器)。 */ .timeline { display: flex; align-items: flex-start; } -.tl-node { display: flex; flex-direction: column; align-items: center; position: relative; padding: 0 8px; } +.tl-node { flex: 1; display: flex; flex-direction: column; align-items: center; position: relative; padding: 0 8px; min-width: 0; } .tl-dot { width: 26px; height: 26px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; background: var(--bg-hover); color: var(--text-3); border: 1px solid var(--border-2); - z-index: 1; + z-index: 1; flex-shrink: 0; } .tl-label { margin-top: 8px; font-size: 12px; color: var(--text-3); white-space: nowrap; } -.tl-bar { position: absolute; top: 13px; left: 50%; width: 100%; height: 2px; background: var(--border-2); z-index: 0; } +.tl-bar { position: absolute; top: 13px; left: calc(50% + 16px); width: calc(100% - 32px); height: 2px; background: var(--border-2); z-index: 0; } .tl-node.done .tl-dot { background: var(--down); color: #fff; border-color: var(--down); } .tl-node.done .tl-label { color: var(--text); } .tl-node.done .tl-bar { background: var(--down); } diff --git a/sanguo_api/routes.py b/sanguo_api/routes.py index 54cd753..a6d9184 100644 --- a/sanguo_api/routes.py +++ b/sanguo_api/routes.py @@ -730,7 +730,21 @@ def daily_holdings(task_id: str): @router.get("/task/{task_id}/log", dependencies=[Depends(verify_token)]) def log_endpoint(task_id: str): - """Get backtest log text.""" + """Get backtest log text. + + 因子任务无传统日志文件 → 返回心跳流水(progress.log,由 analyzer + _report_progress 逐条 append),行情加载 i/N / 因子 i/M / tears ✓ + 即用户要看的"过程"。 + """ + if task_id.startswith("factor_"): + try: + lp = os.path.join(_FACTOR_PROGRESS_DIR, f"{task_id}.progress.log") + if os.path.exists(lp): + with open(lp, encoding="utf-8") as f: + return {"task_id": task_id, "log": f.read()} + except Exception: + pass + return {"task_id": task_id, "log": ""} orch = get_orchestrator() if hasattr(orch, 'db_path') and orch.db_path: file_dir = os.path.dirname(os.path.abspath(orch.db_path)) diff --git a/sanguo_factor/analyzer.py b/sanguo_factor/analyzer.py index 1d98a25..975c82d 100644 --- a/sanguo_factor/analyzer.py +++ b/sanguo_factor/analyzer.py @@ -62,7 +62,9 @@ class FactorReport: def _report_progress(task_id: str, output_dir: str, stage: str, detail: str) -> None: """跨进程进度心跳:worker 写 {output_dir}/{task_id}.progress, - GET /task/{id} 读它合并返回(前端显示"最近活动 Xs 前 · detail")。 + GET /task/{id} 读它合并返回(前端显示"最近活动 Xs 前 · detail"); + 同时 append 一行到 {task_id}.progress.log 作活动流水——因子任务 + 没有传统日志文件,心跳流水就是 /task/{id}/log 的内容。 失败静默——进度是可观测性锦上添花,不能影响分析主流程。""" if not task_id: return @@ -72,6 +74,10 @@ def _report_progress(task_id: str, output_dir: str, stage: str, detail: str) -> p = os.path.join(output_dir, f"{task_id}.progress") with open(p, "w", encoding="utf-8") as f: f.write(_j.dumps({"stage": stage, "detail": detail, "ts": _t.time()})) + lp = os.path.join(output_dir, f"{task_id}.progress.log") + stamp = _t.strftime("%H:%M:%S") + with open(lp, "a", encoding="utf-8") as f: + f.write(f"[{stamp}] {stage} · {detail}\n") except Exception: pass