eec57610dc
Co-authored-by: Cursor <cursoragent@cursor.com>
170 lines
5.1 KiB
JavaScript
170 lines
5.1 KiB
JavaScript
/**
|
|
* 表单币种输入:防抖 + 定时刷新,展示交易所最新价(/api/order_defaults)。
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
const DEFAULT_DEBOUNCE_MS = 350;
|
|
const DEFAULT_POLL_MS = 5000;
|
|
const bound = new WeakSet();
|
|
|
|
function $(id) {
|
|
return id ? document.getElementById(id) : null;
|
|
}
|
|
|
|
function symbolValue(el) {
|
|
if (!el) return "";
|
|
return (el.value || "").trim();
|
|
}
|
|
|
|
function directionValue(dirId) {
|
|
const el = dirId ? $(dirId) : null;
|
|
const v = (el && el.value ? el.value : "long").trim().toLowerCase();
|
|
return v === "short" ? "short" : "long";
|
|
}
|
|
|
|
function formatPrice(px, sym) {
|
|
const n = Number(px);
|
|
if (!Number.isFinite(n)) return "—";
|
|
const u = (sym || "").trim().toUpperCase();
|
|
let digits = 4;
|
|
if (u.startsWith("BTC") || u.startsWith("ETH") || n >= 1000) digits = 2;
|
|
else if (n >= 10) digits = 3;
|
|
else if (n >= 1) digits = 4;
|
|
else if (n >= 0.01) digits = 5;
|
|
else digits = 6;
|
|
return n.toFixed(digits);
|
|
}
|
|
|
|
function pollMs() {
|
|
const raw =
|
|
(document.body && document.body.getAttribute("data-price-refresh-ms")) || "";
|
|
const n = Number(raw);
|
|
return Number.isFinite(n) && n >= 2000 ? n : DEFAULT_POLL_MS;
|
|
}
|
|
|
|
function paint(el, sym, px, err) {
|
|
if (!el) return;
|
|
if (err) {
|
|
el.textContent = "现价:—";
|
|
el.classList.add("symbol-live-price--err");
|
|
el.classList.remove("symbol-live-price--ok");
|
|
el.title = err;
|
|
return;
|
|
}
|
|
if (px === null || typeof px === "undefined") {
|
|
el.textContent = "现价:—";
|
|
el.classList.remove("symbol-live-price--ok", "symbol-live-price--err");
|
|
el.title = sym ? "无法读取交易所价格" : "";
|
|
return;
|
|
}
|
|
const label = sym ? sym.toUpperCase().replace(/\/USDT.*/, "") : "";
|
|
el.textContent = label ? label + " 现价 " + formatPrice(px, sym) : "现价 " + formatPrice(px, sym);
|
|
el.classList.add("symbol-live-price--ok");
|
|
el.classList.remove("symbol-live-price--err");
|
|
el.title = "交易所最新价(约 " + pollMs() / 1000 + "s 刷新)";
|
|
}
|
|
|
|
function bindOne(el) {
|
|
if (!el || bound.has(el)) return;
|
|
bound.add(el);
|
|
|
|
const symId = el.getAttribute("data-symbol-input");
|
|
const dirId = el.getAttribute("data-direction-input") || "";
|
|
let debounceTimer = null;
|
|
let pollTimer = null;
|
|
let fetchSeq = 0;
|
|
|
|
function clearPoll() {
|
|
if (pollTimer) {
|
|
clearInterval(pollTimer);
|
|
pollTimer = null;
|
|
}
|
|
}
|
|
|
|
function startPoll() {
|
|
clearPoll();
|
|
pollTimer = setInterval(refresh, pollMs());
|
|
}
|
|
|
|
function refresh() {
|
|
const symEl = $(symId);
|
|
const sym = symbolValue(symEl);
|
|
if (!sym) {
|
|
paint(el, "", null, "");
|
|
clearPoll();
|
|
return;
|
|
}
|
|
const dir = directionValue(dirId);
|
|
const seq = ++fetchSeq;
|
|
el.classList.add("symbol-live-price--loading");
|
|
fetch(
|
|
"/api/order_defaults?symbol=" +
|
|
encodeURIComponent(sym) +
|
|
"&direction=" +
|
|
encodeURIComponent(dir)
|
|
)
|
|
.then(function (r) {
|
|
return r.json().then(function (d) {
|
|
return { status: r.status, data: d };
|
|
}).catch(function () {
|
|
return { status: r.status, data: null };
|
|
});
|
|
})
|
|
.then(function (res) {
|
|
if (seq !== fetchSeq) return;
|
|
el.classList.remove("symbol-live-price--loading");
|
|
const data = res.data || {};
|
|
if (res.status >= 400 || !data || !data.ok) {
|
|
paint(el, sym, null, (data && data.msg) || "读取失败");
|
|
return;
|
|
}
|
|
const px = data.last_price != null ? data.last_price : data.price;
|
|
if (px === null || typeof px === "undefined") {
|
|
paint(el, data.symbol || sym, null, "无法读取交易所价格");
|
|
return;
|
|
}
|
|
paint(el, data.symbol || sym, px, "");
|
|
if (!pollTimer) startPoll();
|
|
})
|
|
.catch(function () {
|
|
if (seq !== fetchSeq) return;
|
|
el.classList.remove("symbol-live-price--loading");
|
|
paint(el, sym, null, "网络错误");
|
|
});
|
|
}
|
|
|
|
function schedule() {
|
|
clearTimeout(debounceTimer);
|
|
debounceTimer = setTimeout(refresh, DEFAULT_DEBOUNCE_MS);
|
|
}
|
|
|
|
const symEl = $(symId);
|
|
if (symEl) {
|
|
symEl.addEventListener("input", schedule);
|
|
symEl.addEventListener("change", schedule);
|
|
}
|
|
const dirEl = dirId ? $(dirId) : null;
|
|
if (dirEl) {
|
|
dirEl.addEventListener("change", schedule);
|
|
}
|
|
|
|
schedule();
|
|
}
|
|
|
|
function init(root) {
|
|
const scope = root || document;
|
|
scope.querySelectorAll(".symbol-live-price").forEach(bindOne);
|
|
}
|
|
|
|
global.SymbolLivePrice = { init: init, bind: bindOne };
|
|
|
|
if (document.readyState === "loading") {
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
init(document);
|
|
});
|
|
} else {
|
|
init(document);
|
|
}
|
|
})(typeof window !== "undefined" ? window : globalThis);
|