修改前端漏斗
This commit is contained in:
@@ -77,6 +77,53 @@ function formatIsoToBeijing(iso) {
|
||||
return s.replace("T", " ");
|
||||
}
|
||||
|
||||
const FUNNEL_WINDOW_LS_KEY = "funnel_display_hours";
|
||||
const FUNNEL_WINDOW_DEFAULT = 24;
|
||||
const FUNNEL_WINDOW_MIN = 1;
|
||||
const FUNNEL_WINDOW_MAX = 168;
|
||||
|
||||
function getFunnelWindowHours() {
|
||||
try {
|
||||
const n = Number(localStorage.getItem(FUNNEL_WINDOW_LS_KEY));
|
||||
if (Number.isFinite(n) && n >= FUNNEL_WINDOW_MIN && n <= FUNNEL_WINDOW_MAX) {
|
||||
return Math.round(n);
|
||||
}
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
return FUNNEL_WINDOW_DEFAULT;
|
||||
}
|
||||
|
||||
function setFunnelWindowHours(raw) {
|
||||
const n = Math.round(Number(raw));
|
||||
const h =
|
||||
Number.isFinite(n) && n >= FUNNEL_WINDOW_MIN && n <= FUNNEL_WINDOW_MAX
|
||||
? n
|
||||
: FUNNEL_WINDOW_DEFAULT;
|
||||
try {
|
||||
localStorage.setItem(FUNNEL_WINDOW_LS_KEY, String(h));
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
const inp = document.getElementById("funnelWindowHoursInput");
|
||||
if (inp) inp.value = String(h);
|
||||
return h;
|
||||
}
|
||||
|
||||
function initFunnelWindowControls() {
|
||||
const inp = document.getElementById("funnelWindowHoursInput");
|
||||
if (inp) inp.value = String(getFunnelWindowHours());
|
||||
const btn = document.getElementById("applyFunnelWindowBtn");
|
||||
if (btn) {
|
||||
btn.addEventListener("click", () => {
|
||||
const h = setFunnelWindowHours(getInputNumber("funnelWindowHoursInput"));
|
||||
const msg = document.getElementById("funnelWindowMsg");
|
||||
if (msg) msg.textContent = `// 展示窗口已设为最近 ${h} 小时(本浏览器记忆)`;
|
||||
refresh().catch(console.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function tickClock() {
|
||||
const el = document.getElementById("liveClock");
|
||||
if (!el) return;
|
||||
@@ -132,8 +179,9 @@ function renderFunnel(items, funnelCtx) {
|
||||
if (!items.length) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "matrix-hint matrix-hint-empty";
|
||||
const winH = Number(ctx.windowHours) || FUNNEL_WINDOW_DEFAULT;
|
||||
let why =
|
||||
"// 暂无漏斗记录:本面板只展示 <code>source=gemma_funnel</code> 的排序结果(需配置开启且 Ollama 跑完一轮后写入告警表)。";
|
||||
`// 暂无漏斗记录:本面板只展示最近 <code>${winH}h</code> 内 <code>source=gemma_funnel</code> 的排序结果(需配置开启且 Ollama 跑完一轮后写入告警表)。`;
|
||||
if (!gemmaOn) {
|
||||
why += " 当前 <code>gemma.enabled=false</code>,漏斗未运行。";
|
||||
} else if (cycleMsg === "funnel_pending") {
|
||||
@@ -162,8 +210,10 @@ function renderFunnel(items, funnelCtx) {
|
||||
const card = document.createElement("article");
|
||||
card.className = "matrix-card" + (pushed ? " hot" : "");
|
||||
const vol = (d.programmatic && d.programmatic.est_quote_vol_24h_usdt) || "—";
|
||||
const updatedCn = formatIsoToBeijing(a.created_at);
|
||||
card.innerHTML = `
|
||||
<div class="matrix-card-title">${a.symbol}</div>
|
||||
<div class="matrix-card-title">${escapeHtml(a.symbol || "—")}</div>
|
||||
<div class="matrix-card-meta time">更新 ${escapeHtml(updatedCn)}(北京时间)</div>
|
||||
<div class="matrix-card-meta">
|
||||
COMPOSITE <strong>${comp.toFixed(1)}</strong> · P${g.priority || "?"} ·
|
||||
结构 ${g.daily_structure || "?"} · 量 ${g.volume_view || "?"} ·
|
||||
@@ -324,12 +374,13 @@ async function saveDailyReportSettings() {
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
const funnelWindowH = getFunnelWindowHours();
|
||||
const [status, alerts, logs, config, funnel, dailyReport] = await Promise.all([
|
||||
fetchJson("/api/status"),
|
||||
fetchJson("/api/alerts"),
|
||||
fetchJson("/api/logs"),
|
||||
fetchJson("/api/config"),
|
||||
fetchJson("/api/funnel"),
|
||||
fetchJson(`/api/funnel?window_hours=${encodeURIComponent(funnelWindowH)}`),
|
||||
fetchJson("/api/daily-report"),
|
||||
]);
|
||||
updateHud(status);
|
||||
@@ -340,10 +391,13 @@ async function refresh() {
|
||||
if (cf) cf.textContent = pretty(config);
|
||||
|
||||
const runState = (status && status.state) || {};
|
||||
const funnelWindowApplied =
|
||||
funnel && funnel.window_hours != null ? Number(funnel.window_hours) : funnelWindowH;
|
||||
renderFunnel(funnel.items || [], {
|
||||
gemmaEnabled: !!(config.gemma && config.gemma.enabled),
|
||||
cycleMsg: runState.gemma_cycle_msg || "",
|
||||
lastFunnelAt: runState.last_funnel_at || "",
|
||||
windowHours: funnelWindowApplied,
|
||||
});
|
||||
renderDailyReport(dailyReport);
|
||||
try {
|
||||
@@ -370,12 +424,12 @@ async function refresh() {
|
||||
if (fm) {
|
||||
let line =
|
||||
`// 浏览器刚拉完 API:${pullCn} | HUD 的 LAST:上一轮 Gate 扫描整轮结束(可与本行差约 ${poll}s)| ` +
|
||||
`矩阵卡片 ${fc} 条:来自告警库「每币最新一条」| 记忆体 last_funnel 更新:${lfAt} | 后轮 gemma:${gmsg}`;
|
||||
`矩阵卡片 ${fc} 条:最近 ${funnelWindowApplied}h 内 gemma_funnel(每币最新一条)| 记忆体 last_funnel 更新:${lfAt} | 后轮 gemma:${gmsg}`;
|
||||
if (String(gmsg).includes("funnel_ranked=0") && fc > 0) {
|
||||
line +=
|
||||
" | 说明:本轮后台漏斗未写入新排名(常见:4h 内同一币已跑过 FUNNEL-GEMMA 被跳过、或候选在取日线/Ollama 前被滤掉),卡片仍是历史结果,不是前端卡死。";
|
||||
` | 说明:本轮后台漏斗未写入新排名(常见:4h 内同一币已跑过 FUNNEL-GEMMA 被跳过),仍显示 ${funnelWindowApplied}h 内已有卡片。`;
|
||||
} else {
|
||||
line += " | 若文案长期不变=近期没有新的 gemma_funnel 入库。";
|
||||
line += ` | 超过 ${funnelWindowApplied}h 的漏斗记录不再显示(库内仍在)。`;
|
||||
}
|
||||
fm.textContent = line;
|
||||
}
|
||||
@@ -613,6 +667,7 @@ loadOrderExecutors().catch(console.error);
|
||||
tickClock();
|
||||
setInterval(tickClock, 1000);
|
||||
initMatrixRain();
|
||||
initFunnelWindowControls();
|
||||
refresh();
|
||||
setInterval(refresh, 4000);
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
|
||||
Reference in New Issue
Block a user