diff --git a/manual_trading_hub/static/app.css b/manual_trading_hub/static/app.css index f9c7adb..61ab133 100644 --- a/manual_trading_hub/static/app.css +++ b/manual_trading_hub/static/app.css @@ -1961,8 +1961,14 @@ body.login-page { color: #ffb84d; } +.market-countdown { + color: var(--accent); + font-variant-numeric: tabular-nums; +} + .market-chart-wrap { - position: relative; + display: flex; + flex-direction: column; height: min(72vh, 640px); min-height: 360px; border: 1px solid var(--border-soft); @@ -1971,23 +1977,46 @@ body.login-page { overflow: hidden; } +.market-ohlcv-bar { + flex: 0 0 auto; + padding: 8px 12px; + border-bottom: 1px solid var(--border-soft); + background: rgba(8, 14, 24, 0.96); + font-size: 0.78rem; +} + +.market-chart-body { + flex: 1; + display: flex; + min-height: 0; + position: relative; +} + .market-chart-host { - width: 100%; + flex: 1; + min-width: 0; height: 100%; } +.market-price-gutter { + flex: 0 0 72px; + position: relative; + border-left: 1px solid var(--border-soft); + background: #0a1018; +} + .market-exchange-badge { position: absolute; + left: 50%; top: 50%; - right: 52px; - z-index: 3; - transform: translateY(-50%) rotate(-90deg); + z-index: 1; + transform: translate(-50%, -50%) rotate(-90deg); transform-origin: center center; font-family: var(--font-display, var(--font)); font-size: 0.95rem; font-weight: 600; letter-spacing: 0.12em; - color: rgba(184, 212, 232, 0.22); + color: rgba(184, 212, 232, 0.18); pointer-events: none; white-space: nowrap; user-select: none; @@ -1997,20 +2026,6 @@ body.login-page { display: none; } -.market-ohlcv-overlay { - position: absolute; - top: 10px; - left: 10px; - z-index: 4; - pointer-events: none; - padding: 8px 12px; - border-radius: 8px; - background: rgba(8, 14, 24, 0.88); - border: 1px solid var(--border-soft); - font-size: 0.78rem; - max-width: calc(100% - 80px); -} - .market-ohlcv-title { font-weight: 600; color: var(--accent); @@ -2053,18 +2068,18 @@ body.login-page { .market-price-tag { position: absolute; - right: 52px; - z-index: 6; + left: 4px; + right: 4px; + z-index: 2; pointer-events: none; - padding: 4px 8px; + padding: 4px 6px; border-radius: 4px; font-family: var(--font); - font-size: 0.75rem; + font-size: 0.72rem; font-weight: 600; line-height: 1.2; text-align: center; transform: translateY(-50%); - min-width: 76px; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.35); } @@ -2096,9 +2111,10 @@ body.login-page { .market-price-auto { position: absolute; - right: 8px; + left: 6px; + right: 6px; bottom: 10px; - z-index: 5; + z-index: 2; padding: 4px 8px; font-size: 0.68rem; font-family: var(--font); diff --git a/manual_trading_hub/static/chart.js b/manual_trading_hub/static/chart.js index 36a8125..053c077 100644 --- a/manual_trading_hub/static/chart.js +++ b/manual_trading_hub/static/chart.js @@ -23,6 +23,8 @@ const elRefresh = document.getElementById("market-refresh"); const elStatus = document.getElementById("market-status"); const elUpdated = document.getElementById("market-updated"); + const elBarCountdown = document.getElementById("market-bar-countdown"); + const elPriceGutter = document.querySelector(".market-price-gutter"); const elO = document.getElementById("mkt-o"); const elH = document.getElementById("mkt-h"); const elL = document.getElementById("mkt-l"); @@ -160,8 +162,17 @@ updatePriceTag(); } + function tickLiveClock() { + const cd = fmtBarCountdown(barRemainMs(currentTf)); + if (elPriceTagTime && elPriceTag && !elPriceTag.classList.contains("hidden")) { + elPriceTagTime.textContent = cd; + } + if (elBarCountdown) elBarCountdown.textContent = "距收盘 " + cd; + } + function updatePriceTag() { if (!elPriceTag || !candleSeries || !chart) return; + tickLiveClock(); const bar = latestCandle(); if (!bar || bar.close == null) { elPriceTag.classList.add("hidden"); @@ -174,7 +185,7 @@ } catch (e) { y = null; } - const hostH = chartHost.clientHeight || 0; + const hostH = (elPriceGutter && elPriceGutter.clientHeight) || chartHost.clientHeight || 0; if (y == null || y < 8 || y > hostH - 8) { elPriceTag.classList.add("hidden"); elPriceTag.setAttribute("aria-hidden", "true"); @@ -186,12 +197,12 @@ elPriceTag.setAttribute("aria-hidden", "false"); elPriceTag.style.top = y + "px"; if (elPriceTagValue) elPriceTagValue.textContent = fmtPrice(bar.close); - if (elPriceTagTime) elPriceTagTime.textContent = fmtBarCountdown(barRemainMs(currentTf)); } function startPriceTagTimer() { stopPriceTagTimer(); - priceTagTimer = setInterval(updatePriceTag, 1000); + tickLiveClock(); + priceTagTimer = setInterval(tickLiveClock, 1000); } function stopPriceTagTimer() { @@ -519,7 +530,8 @@ elStatus.className = data.stale ? "market-status warn" : "market-status"; elStatus.textContent = hint; } - if (elUpdated) elUpdated.textContent = data.updated_at || "--"; + if (elUpdated) elUpdated.textContent = "数据 " + (data.updated_at || "--"); + tickLiveClock(); } catch (e) { if (myToken !== loadToken) return; if (elStatus) { @@ -537,6 +549,8 @@ } if (elTf) { elTf.addEventListener("change", function () { + currentTf = (elTf && elTf.value) || "1d"; + tickLiveClock(); loadChart(false); }); } @@ -573,8 +587,8 @@ bind(); } startAutoRefresh(); - startPriceTagTimer(); await loadChart(false); + startPriceTagTimer(); }, reload: function (force) { loadChart(!!force); diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index 82847aa..617e001 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -8,7 +8,7 @@ - +
@@ -93,12 +93,12 @@ +