增加大模型
This commit is contained in:
+120
-32
@@ -1,10 +1,13 @@
|
||||
/** 日 K + 成交量(Canvas 高清) */
|
||||
/** 日 K + 成交量(Canvas 高清)· 浏览器 localStorage 缓存 · 点击全屏 */
|
||||
|
||||
const chartDataCache = new Map();
|
||||
const chartQueue = [];
|
||||
let chartQueueRunning = false;
|
||||
const CHART_FETCH_GAP_MS = 120;
|
||||
|
||||
const LS_KLINE_PREFIX = "ba_kline_";
|
||||
const KLINE_TTL_MS = 60 * 60 * 1000;
|
||||
|
||||
const COLORS = {
|
||||
bg: "#0d1118",
|
||||
grid: "#2a3548",
|
||||
@@ -16,7 +19,40 @@ const COLORS = {
|
||||
};
|
||||
|
||||
const MINI_SIZE = { w: 380, h: 100 };
|
||||
const MODAL_SIZE = { w: 1280, h: 720 };
|
||||
|
||||
function modalSize() {
|
||||
const fs = document.fullscreenElement;
|
||||
if (fs) {
|
||||
return {
|
||||
w: Math.max(800, window.innerWidth - 48),
|
||||
h: Math.max(480, window.innerHeight - 100),
|
||||
};
|
||||
}
|
||||
return { w: 1280, h: 720 };
|
||||
}
|
||||
|
||||
function loadKlineFromLS(symbol) {
|
||||
try {
|
||||
const raw = localStorage.getItem(LS_KLINE_PREFIX + symbol);
|
||||
if (!raw) return null;
|
||||
const obj = JSON.parse(raw);
|
||||
if (!obj?.candles?.length || Date.now() - (obj.ts || 0) > KLINE_TTL_MS) return null;
|
||||
return obj;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function saveKlineToLS(symbol, candles, source) {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
LS_KLINE_PREFIX + symbol,
|
||||
JSON.stringify({ ts: Date.now(), candles, source })
|
||||
);
|
||||
} catch {
|
||||
/* quota */
|
||||
}
|
||||
}
|
||||
|
||||
function enqueueCharts(root) {
|
||||
root.querySelectorAll(".mini-chart[data-symbol]").forEach((box) => {
|
||||
@@ -66,7 +102,7 @@ function drawCandlestickChart(canvas, candles, options = {}) {
|
||||
if (!canvas || !candles.length) return;
|
||||
|
||||
const large = options.large === true;
|
||||
const size = large ? MODAL_SIZE : MINI_SIZE;
|
||||
const size = large ? modalSize() : MINI_SIZE;
|
||||
const volRatio = large ? 0.22 : 0.32;
|
||||
const pad = large
|
||||
? { t: 16, r: 16, b: 28, l: 56 }
|
||||
@@ -164,7 +200,7 @@ function drawCandlestickChart(canvas, candles, options = {}) {
|
||||
|
||||
function drawEmptyChart(canvas, large = false) {
|
||||
if (!canvas) return;
|
||||
const size = large ? MODAL_SIZE : MINI_SIZE;
|
||||
const size = large ? modalSize() : MINI_SIZE;
|
||||
const { ctx, w, h } = setupCanvas(canvas, size.w, size.h);
|
||||
ctx.fillStyle = "#1a2332";
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
@@ -173,6 +209,32 @@ function drawEmptyChart(canvas, large = false) {
|
||||
ctx.fillText("暂无数据", w / 2 - 28, h / 2);
|
||||
}
|
||||
|
||||
async function fetchKlines(symbol) {
|
||||
let candles = chartDataCache.get(symbol);
|
||||
let source = "memory";
|
||||
if (candles) return { candles, source };
|
||||
|
||||
const ls = loadKlineFromLS(symbol);
|
||||
if (ls) {
|
||||
candles = ls.candles;
|
||||
source = "browser";
|
||||
chartDataCache.set(symbol, candles);
|
||||
return { candles, source: ls.source || source };
|
||||
}
|
||||
|
||||
const res = await fetch(`/api/chart/${symbol}/daily?limit=300`);
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.detail || res.statusText);
|
||||
}
|
||||
const data = await res.json();
|
||||
candles = data.candles || [];
|
||||
source = data.source || "db";
|
||||
chartDataCache.set(symbol, candles);
|
||||
saveKlineToLS(symbol, candles, source);
|
||||
return { candles, source };
|
||||
}
|
||||
|
||||
async function loadMiniChart(box) {
|
||||
const symbol = box.dataset.symbol;
|
||||
if (!symbol) return;
|
||||
@@ -182,26 +244,22 @@ async function loadMiniChart(box) {
|
||||
if (status) status.textContent = "加载…";
|
||||
|
||||
try {
|
||||
let candles = chartDataCache.get(symbol);
|
||||
let source = "cache";
|
||||
if (!candles) {
|
||||
const res = await fetch(`/api/chart/${symbol}/daily?limit=300`);
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.detail || res.statusText);
|
||||
}
|
||||
const data = await res.json();
|
||||
candles = data.candles || [];
|
||||
source = data.source || "db";
|
||||
chartDataCache.set(symbol, candles);
|
||||
}
|
||||
const { candles, source } = await fetchKlines(symbol);
|
||||
if (!candles.length) throw new Error("无K线数据");
|
||||
drawCandlestickChart(canvas, candles, { large: false });
|
||||
box.dataset.loaded = "1";
|
||||
const srcLabel =
|
||||
source === "db" ? "本地" : source === "db_stale" ? "本地(旧)" : source === "cache" ? "缓存" : "同步";
|
||||
source === "browser"
|
||||
? "浏览器"
|
||||
: source === "db"
|
||||
? "本地"
|
||||
: source === "db_stale"
|
||||
? "本地(旧)"
|
||||
: source === "cache"
|
||||
? "缓存"
|
||||
: "同步";
|
||||
if (status) status.textContent = `${candles.length}日·${srcLabel}`;
|
||||
box.title = `${symbol} 日K+量 ${candles.length}根 (${srcLabel}),点击放大`;
|
||||
box.title = `${symbol} 日K+量 ${candles.length}根 (${srcLabel}),点击全屏`;
|
||||
} catch (e) {
|
||||
if (status) status.textContent = "—";
|
||||
box.title = `${symbol}: ${e.message}`;
|
||||
@@ -211,6 +269,36 @@ async function loadMiniChart(box) {
|
||||
}
|
||||
}
|
||||
|
||||
let chartModalSymbol = "";
|
||||
|
||||
function closeChartModal() {
|
||||
const modal = document.getElementById("chart-modal");
|
||||
if (!modal) return;
|
||||
modal.classList.add("hidden");
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen?.().catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
function openChartModal(symbol) {
|
||||
const candles = chartDataCache.get(symbol);
|
||||
if (!candles?.length) return;
|
||||
|
||||
chartModalSymbol = symbol;
|
||||
const modal = document.getElementById("chart-modal");
|
||||
modal.classList.remove("hidden");
|
||||
document.getElementById("chart-modal-title").textContent =
|
||||
`${symbol} · 日K + 成交量(${candles.length} 根)`;
|
||||
const canvas = document.getElementById("chart-modal-canvas");
|
||||
drawCandlestickChart(canvas, candles, { large: true });
|
||||
|
||||
const inner = modal.querySelector(".chart-modal-inner");
|
||||
const req = inner.requestFullscreen || inner.webkitRequestFullscreen;
|
||||
if (req) {
|
||||
req.call(inner).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
function setupChartModal() {
|
||||
let modal = document.getElementById("chart-modal");
|
||||
if (!modal) {
|
||||
@@ -221,33 +309,33 @@ function setupChartModal() {
|
||||
<div class="chart-modal-inner">
|
||||
<button type="button" class="chart-modal-close" aria-label="关闭">×</button>
|
||||
<h3 id="chart-modal-title"></h3>
|
||||
<p class="chart-modal-hint">日K + 成交量 · 300根 · 滚轮可缩放页面</p>
|
||||
<p class="chart-modal-hint">日K + 成交量 · 300根 · 点击全屏 · Esc 退出</p>
|
||||
<div class="chart-modal-canvas-wrap">
|
||||
<canvas id="chart-modal-canvas"></canvas>
|
||||
</div>
|
||||
</div>`;
|
||||
document.body.appendChild(modal);
|
||||
modal.querySelector(".chart-modal-close").onclick = () =>
|
||||
modal.classList.add("hidden");
|
||||
modal.querySelector(".chart-modal-close").onclick = closeChartModal;
|
||||
modal.addEventListener("click", (e) => {
|
||||
if (e.target === modal) modal.classList.add("hidden");
|
||||
if (e.target === modal) closeChartModal();
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") modal.classList.add("hidden");
|
||||
if (e.key === "Escape") closeChartModal();
|
||||
});
|
||||
document.addEventListener("fullscreenchange", () => {
|
||||
if (!chartModalSymbol) return;
|
||||
const canvas = document.getElementById("chart-modal-canvas");
|
||||
const candles = chartDataCache.get(chartModalSymbol);
|
||||
if (canvas && candles?.length) {
|
||||
drawCandlestickChart(canvas, candles, { large: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.body.addEventListener("click", (e) => {
|
||||
const box = e.target.closest(".mini-chart[data-symbol]");
|
||||
if (!box || box.dataset.loaded !== "1") return;
|
||||
const symbol = box.dataset.symbol;
|
||||
const candles = chartDataCache.get(symbol);
|
||||
if (!candles) return;
|
||||
modal.classList.remove("hidden");
|
||||
document.getElementById("chart-modal-title").textContent =
|
||||
`${symbol} · 日K + 成交量(${candles.length} 根)`;
|
||||
const canvas = document.getElementById("chart-modal-canvas");
|
||||
drawCandlestickChart(canvas, candles, { large: true });
|
||||
openChartModal(box.dataset.symbol);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user