fix(hub): inner-light-mind chart, exchange column and layout

Preserve exchange_key from archive cache, fix chart resize in details panel, move filters above quotes, align panel heights, and style sick trades with red/cyan.
This commit is contained in:
dekun
2026-06-11 17:53:25 +08:00
parent bb800b876b
commit 2388ecc882
4 changed files with 138 additions and 45 deletions
+17
View File
@@ -420,6 +420,23 @@ def _trade_row_to_dict(row: sqlite3.Row, overlay: dict | None = None) -> dict[st
except (json.JSONDecodeError, TypeError):
payload = {}
out = {**payload, **{k: d[k] for k in d.keys() if k not in payload}}
for key in (
"exchange_key",
"symbol",
"trade_id",
"direction",
"result",
"pnl_amount",
"opened_at",
"closed_at",
"opened_at_ms",
"closed_at_ms",
"monitor_type",
"entry_reason",
"synced_at",
):
if key in d and d[key] not in (None, ""):
out[key] = d[key]
ov = overlay or {}
out["behavior_tag"] = ov.get("behavior_tag") or ""
out["note"] = ov.get("note") or ""
+27 -10
View File
@@ -5427,12 +5427,15 @@ body.funds-fullscreen-open {
color: var(--text);
font-family: var(--font);
}
#page-archive .archive-toolbar {
margin-bottom: 12px;
}
.archive-layout {
display: grid;
grid-template-columns: minmax(240px, 300px) minmax(0, 1fr);
gap: 14px;
min-height: 520px;
align-items: start;
min-height: calc(100vh - 240px);
align-items: stretch;
}
.archive-quotes-panel,
.archive-main-panel {
@@ -5446,7 +5449,7 @@ body.funds-fullscreen-open {
flex-direction: column;
gap: 10px;
padding: 12px;
max-height: calc(100vh - 180px);
min-height: 100%;
overflow: hidden;
}
.archive-panel-head {
@@ -5546,7 +5549,7 @@ body.funds-fullscreen-open {
flex-direction: column;
gap: 10px;
padding: 12px;
min-height: 520px;
min-height: 100%;
}
.archive-stats-bar {
padding: 10px 12px;
@@ -5587,7 +5590,7 @@ body.funds-fullscreen-open {
.archive-trades-section > .archive-trades {
border: none;
border-radius: 0;
max-height: min(56vh, 560px);
max-height: 380px;
}
.archive-chart-toolbar {
flex-wrap: wrap;
@@ -5648,10 +5651,11 @@ body.funds-fullscreen-open {
}
.archive-trades {
overflow: auto;
max-height: min(56vh, 560px);
max-height: 380px;
border: 1px solid var(--border-soft);
border-radius: var(--radius);
background: var(--panel);
overscroll-behavior: contain;
}
.archive-trades-table {
width: 100%;
@@ -5697,13 +5701,24 @@ body.funds-fullscreen-open {
background: var(--inset-surface);
}
.archive-trade-row.archive-trade-sick {
background: rgba(239, 68, 68, 0.12);
background: rgba(220, 38, 38, 0.3);
}
.archive-trade-row.archive-trade-sick.is-active {
background: rgba(239, 68, 68, 0.18);
background: rgba(220, 38, 38, 0.38);
}
.archive-trade-row.archive-trade-sick td {
border-bottom-color: rgba(239, 68, 68, 0.22);
color: var(--accent);
border-bottom-color: rgba(220, 38, 38, 0.4);
}
.archive-trade-row.archive-trade-sick .archive-tag-select,
.archive-trade-row.archive-trade-sick .archive-note-input {
color: var(--accent);
background: rgba(0, 0, 0, 0.2);
border-color: color-mix(in srgb, var(--accent) 50%, var(--border-soft));
}
.archive-trade-row.archive-trade-sick td.pos,
.archive-trade-row.archive-trade-sick td.neg {
color: var(--accent);
}
.archive-actions-cell {
white-space: nowrap;
@@ -5754,9 +5769,11 @@ body.funds-fullscreen-open {
@media (max-width: 900px) {
.archive-layout {
grid-template-columns: 1fr;
min-height: 0;
}
.archive-quotes-panel {
max-height: 280px;
min-height: 280px;
max-height: 320px;
}
}
+71 -12
View File
@@ -211,12 +211,50 @@
if (elStatus) elStatus.textContent = text || "";
}
function tradeRowExchange(tr) {
if (!tr) return "—";
const exKey = tr.exchange_key || tr.account_exchange_key || "";
if (exKey) return exchangeLabel(exKey);
const name = tr.account_name || tr.exchange_name || "";
return name || "—";
}
function exchangeLabel(exKey) {
const key = String(exKey || "").toLowerCase();
if (!key) return "—";
const hit = (meta && meta.exchanges || []).find(function (ex) {
return String(ex.key || "").toLowerCase() === key;
});
return hit ? hit.name || hit.key : exKey || "—";
return hit ? hit.name || hit.key : exKey;
}
function scheduleChartResize() {
requestAnimationFrame(function () {
if (chart && elChartHost) {
const w = elChartHost.clientWidth;
const h = elChartHost.clientHeight;
if (w > 0 && h > 0) chart.applyOptions({ width: w, height: h });
}
requestAnimationFrame(function () {
if (chart && elChartHost) {
const w = elChartHost.clientWidth;
const h = elChartHost.clientHeight;
if (w > 0 && h > 0) chart.applyOptions({ width: w, height: h });
}
});
});
}
async function ensureChartSelection() {
if (selected && selected.exchange_key && selected.symbol) return;
if (!dailyTrades.length) return;
const tr = dailyTrades.find(function (t) {
return t.exchange_key && t.symbol;
});
if (!tr) return;
selected = { exchange_key: tr.exchange_key, symbol: tr.symbol };
selectedTradeId = String(tr.trade_id || tr.id);
await loadSymbolTradesForChart(tr.exchange_key, tr.symbol);
}
function isChartOpen() {
@@ -229,7 +267,11 @@
if (elBtnChartToggle) {
elBtnChartToggle.classList.toggle("is-active", !!on);
}
if (!on) destroyChart();
if (!on) {
destroyChart();
return;
}
scheduleChartResize();
}
function updateChartTitle() {
@@ -718,7 +760,11 @@
setStatus(j.detail || "K 线加载失败");
return;
}
if (chart) {
destroyChart();
}
ensureChart();
scheduleChartResize();
const candles = j.candles || [];
lastCandles = candles;
candleSeries.setData(
@@ -742,14 +788,18 @@
chart.timeScale().setVisibleLogicalRange({ from: candles.length - 120, to: candles.length + 5 });
}
updateChartTitle();
scheduleChartResize();
setStatus("K 线 " + candles.length + " 根 · " + timeframe);
}
async function openTradeChart(tr) {
if (!tr) return;
const exKey = tr.exchange_key;
const sym = tr.symbol;
if (!exKey || !sym) return;
const exKey = tr.exchange_key || tr.account_exchange_key || "";
const sym = tr.symbol || "";
if (!exKey || !sym) {
setStatus("该笔交易缺少交易所或合约,无法加载图表");
return;
}
selected = { exchange_key: exKey, symbol: sym };
selectedTradeId = String(tr.trade_id || tr.id);
setChartOpen(true);
@@ -772,7 +822,7 @@
dailyTrades
.map(function (t) {
const tid = t.trade_id || t.id;
const exKey = t.exchange_key || "";
const exKey = t.exchange_key || t.account_exchange_key || "";
const tag = t.behavior_tag || "";
const sick = tag === "sick";
const active = String(tid) === String(selectedTradeId) ? " is-active" : "";
@@ -785,9 +835,11 @@
tid +
'" data-ex="' +
esc(exKey) +
'" data-sym="' +
esc(t.symbol || "") +
'">' +
"<td>" +
esc(exchangeLabel(exKey)) +
esc(tradeRowExchange(t)) +
"</td>" +
"<td>" +
(rev ? '<span class="archive-review-mark">' + rev + "</span>" : "") +
@@ -1015,17 +1067,24 @@
});
}
if (elBtnChartToggle) {
elBtnChartToggle.addEventListener("click", function () {
elBtnChartToggle.addEventListener("click", async function () {
const next = !isChartOpen();
setChartOpen(next);
if (next && selected) void loadChart();
if (next) {
await ensureChartSelection();
void loadChart();
}
});
}
if (elChartSection) {
elChartSection.addEventListener("toggle", function () {
elChartSection.addEventListener("toggle", async function () {
if (elBtnChartToggle) elBtnChartToggle.classList.toggle("is-active", elChartSection.open);
if (elChartSection.open && selected) void loadChart();
else if (!elChartSection.open) destroyChart();
if (elChartSection.open) {
await ensureChartSelection();
void loadChart();
} else {
destroyChart();
}
});
}
if (elQuoteForm) elQuoteForm.addEventListener("submit", addQuote);
+23 -23
View File
@@ -15,7 +15,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
<link rel="stylesheet" href="/assets/app.css?v=20260612-inner-light-mind" />
<link rel="stylesheet" href="/assets/app.css?v=20260612-inner-light-fix" />
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
</head>
<body>
@@ -262,6 +262,27 @@
<h1><span class="head-tag">IN</span> 内照明心</h1>
<p class="page-desc">复盘语录 · 当日交易记录 · 按需查看 K 线</p>
</div>
<div class="archive-toolbar toolbar">
<label class="chk-label"><input type="checkbox" id="archive-filter-profit" /> 盈利单</label>
<label class="chk-label"><input type="checkbox" id="archive-filter-loss" /> 亏损单</label>
<label class="chk-label"><input type="checkbox" id="archive-filter-sick" /> 犯病</label>
<label class="archive-field">
<span>日期</span>
<input id="archive-trading-day" type="date" />
</label>
<button type="button" id="archive-btn-chart-toggle" class="ghost">图表</button>
<label class="archive-field archive-search-field">
<span>搜索</span>
<input id="archive-search" type="search" placeholder="合约 / 交易所 / 备注" autocomplete="off" />
</label>
<label class="archive-field">
<span>交易所</span>
<select id="archive-exchange"><option value="">全部</option></select>
</label>
<button type="button" id="archive-btn-refresh" class="primary">刷新</button>
<button type="button" id="archive-btn-sync" class="ghost">同步</button>
<span id="archive-status" class="toolbar-meta"></span>
</div>
<div class="archive-layout">
<aside class="archive-quotes-panel">
<div class="archive-panel-head">
@@ -276,27 +297,6 @@
<div id="archive-quotes-list" class="archive-quotes-list"></div>
</aside>
<main class="archive-main-panel">
<div class="archive-toolbar toolbar">
<label class="chk-label"><input type="checkbox" id="archive-filter-profit" /> 盈利单</label>
<label class="chk-label"><input type="checkbox" id="archive-filter-loss" /> 亏损单</label>
<label class="chk-label"><input type="checkbox" id="archive-filter-sick" /> 犯病</label>
<label class="archive-field">
<span>日期</span>
<input id="archive-trading-day" type="date" />
</label>
<button type="button" id="archive-btn-chart-toggle" class="ghost">图表</button>
<label class="archive-field archive-search-field">
<span>搜索</span>
<input id="archive-search" type="search" placeholder="合约 / 交易所 / 备注" autocomplete="off" />
</label>
<label class="archive-field">
<span>交易所</span>
<select id="archive-exchange"><option value="">全部</option></select>
</label>
<button type="button" id="archive-btn-refresh" class="primary">刷新</button>
<button type="button" id="archive-btn-sync" class="ghost">同步</button>
<span id="archive-status" class="toolbar-meta"></span>
</div>
<div id="archive-stats" class="archive-stats-bar"></div>
<details id="archive-chart-section" class="archive-acc-section archive-chart-section">
<summary class="archive-acc-summary">K 线图表 <span id="archive-chart-title" class="archive-acc-sub"></span></summary>
@@ -573,7 +573,7 @@
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
<script src="/assets/archive.js?v=20260612-inner-light-mind"></script>
<script src="/assets/archive.js?v=20260612-inner-light-fix"></script>
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
<script src="/assets/ai_review_render.js?v=2"></script>