fix(web): 进度页时间线越界+因子任务日志空(用户08-30验收两处) [vps]
①时间线:原「末节点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绿
This commit is contained in:
@@ -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' }"
|
||||
>
|
||||
<div class="tl-dot">
|
||||
<span v-if="(isFactor ? fxStepState(i) : btStepState(i)) === 'done'">✓</span>
|
||||
@@ -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); }
|
||||
|
||||
+15
-1
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user