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:
@@ -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 ""
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user