"use strict"; /* ========================================================================== 三方向共用的数据整形 + 操作绑定。A/B/C 各自的 mount() 只管 DOM 结构和动效编排, "字段从哪来、按钮点了调什么接口" 统一走这里,禁止在布局文件里重写业务判断。 ========================================================================== */ window.HubShared = (function () { const C = window.Core; const { esc, timeShort } = C; function overviewView() { const d = C.state.data.overview; if (!d) return null; const eod = d.eod_status || {}, rev = d.revision_status || {}; return { tradeDate: d.trade_date, phase: C.PHASE_LABELS[d.session_phase] || d.session_phase, pubCount: d.publications.length, eodState: eod.state, eodLabel: C.EOD_LABELS[eod.state] || eod.state || "-", eod, revState: rev.state, revLabel: C.REV_LABELS[rev.state] || rev.state || "-", rev, anomalies: d.anomalies.length, recentCalls: d.recent_calls || [], sourceCount: d.source_count, }; } function sourcesView() { const d = C.state.data.sources; if (!d) return null; const live = d.items.filter((it) => C.FLOW_PROVIDERS.includes(it.provider)); const reserved = d.items.filter((it) => !C.FLOW_PROVIDERS.includes(it.provider)); return { live, reserved, all: d.items }; } function jobsView() { const d = C.state.data.jobs; if (!d) return null; const runs = d.runs || []; const latestByJob = new Map(); for (const r of runs) if (!latestByJob.has(r.job_id)) latestByJob.set(r.job_id, r); return { jobs: d.jobs || [], runs, latest: runs[0], latestByJob, failedRecent: runs.slice(0, 20).filter((r) => r.state === "failed").length, fresh: C.state.freshJobRuns || [], }; } function releaseView() { const d = C.state.data.batches; if (!d) return null; const pubs = d.publications || []; const batches = d.batches || []; return { tradeDate: d.trade_date, pubs, batches, rollbacks: pubs.filter((p) => p.prev_batch).length, failedBatches: batches.filter((b) => b.state === "failed").length, }; } function datasetsView() { const d = C.state.data.datasets; if (!d) return null; return { tradeDate: d.trade_date, pubs: d.publications || [], diffs: d.diff_reports || [] }; } function auditView() { const d = C.state.data.audit; if (!d) return null; return { items: d.items || [] }; } /* ---------------------------------------------------------------- 操作绑定 */ function bindProbeButtons(root, onDone) { root.querySelectorAll("[data-probe]").forEach((btn) => { btn.addEventListener("click", async () => { const provider = btn.dataset.probe; btn.disabled = true; const original = btn.textContent; btn.textContent = "探测中…"; try { const result = await C.probeSource(provider); btn.textContent = "已探测"; setTimeout(() => { btn.textContent = original; }, 1200); if (onDone) onDone(result, provider); } catch (err) { alert(err.message); btn.textContent = original; } finally { setTimeout(() => { btn.disabled = false; }, 400); } }); }); } function bindRunButtons(root, onDone) { root.querySelectorAll("[data-run]").forEach((btn) => { btn.addEventListener("click", async () => { const date = prompt("交易日 YYYYMMDD(可留空=今天)", "") || ""; btn.disabled = true; try { const result = await C.runJob(btn.dataset.run, date); if (onDone) onDone(result); } catch (err) { alert(err.message); } finally { btn.disabled = false; } }); }); } function bindRollbackButtons(root, onDone) { root.querySelectorAll("[data-rollback]").forEach((btn) => { btn.addEventListener("click", () => C.dangerous("rollback", btn.dataset.rollback, onDone)); }); } function bindBackfillButton(root, selector, onDone) { const el = root.querySelector(selector); if (el) el.addEventListener("click", () => C.dangerous("backfill", null, onDone)); } function bindReleaseDateReload(root, inputSel, btnSel, onDone) { const btn = root.querySelector(btnSel); if (!btn) return; btn.addEventListener("click", async () => { C.state.releaseDate = root.querySelector(inputSel).value.trim(); await C.pollBatches(); if (onDone) onDone(); }); } return { overviewView, sourcesView, jobsView, releaseView, datasetsView, auditView, bindProbeButtons, bindRunButtons, bindRollbackButtons, bindBackfillButton, bindReleaseDateReload, }; })();