删除中控下单区
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
(function () {
|
||||
const toast = document.getElementById("toast");
|
||||
let settingsCache = null;
|
||||
let tradeMeta = {};
|
||||
let trendPreviewId = null;
|
||||
let monitorTimer = null;
|
||||
|
||||
function showToast(msg, isErr) {
|
||||
@@ -34,7 +32,6 @@
|
||||
|
||||
function currentPage() {
|
||||
const p = window.location.pathname.replace(/\/$/, "") || "/monitor";
|
||||
if (p.includes("trade")) return "trade";
|
||||
if (p.includes("settings")) return "settings";
|
||||
return "monitor";
|
||||
}
|
||||
@@ -49,7 +46,6 @@
|
||||
});
|
||||
if (page === "monitor") startMonitorPoll();
|
||||
else stopMonitorPoll();
|
||||
if (page === "trade") initTradePage();
|
||||
if (page === "settings") loadSettingsUI();
|
||||
}
|
||||
|
||||
@@ -171,13 +167,18 @@
|
||||
const review = row.review_url
|
||||
? `<a class="btn-link" href="${esc(row.review_url)}" target="_blank" rel="noopener">复盘</a>`
|
||||
: "";
|
||||
const flaskOpen = row.flask_url_browser || row.flask_url;
|
||||
const openFlask = flaskOpen
|
||||
? `<a class="btn-link" href="${esc(flaskOpen)}" target="_blank" rel="noopener">实例</a>`
|
||||
: "";
|
||||
return `<div class="card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<div class="card-title">${esc(row.name)}</div>
|
||||
<div class="card-sub">${esc(row.flask_url_browser || row.flask_url || "")}</div>
|
||||
<div class="card-sub">${esc(flaskOpen || "")}</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
${openFlask}
|
||||
${review}
|
||||
<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>
|
||||
</div>
|
||||
@@ -215,164 +216,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function initTradePage() {
|
||||
loadSettings().then(() => {
|
||||
const sel = document.getElementById("trade-account");
|
||||
const prev = sel.value;
|
||||
sel.innerHTML = enabledAccounts()
|
||||
.map(
|
||||
(x) =>
|
||||
`<option value="${esc(x.id)}">${esc(x.name)}</option>`
|
||||
)
|
||||
.join("");
|
||||
if (prev) sel.value = prev;
|
||||
syncTradeTabs();
|
||||
loadTradeMeta();
|
||||
});
|
||||
}
|
||||
|
||||
function accountCaps() {
|
||||
const id = document.getElementById("trade-account").value;
|
||||
const ex = (settingsCache?.exchanges || []).find((x) => String(x.id) === String(id));
|
||||
return ex?.capabilities || [];
|
||||
}
|
||||
|
||||
function syncTradeTabs() {
|
||||
const caps = accountCaps();
|
||||
document.querySelectorAll(".tabs button").forEach((btn) => {
|
||||
const tab = btn.dataset.tab;
|
||||
let ok = false;
|
||||
if (tab === "order") ok = caps.includes("order");
|
||||
if (tab === "key") ok = caps.includes("key");
|
||||
if (tab === "trend") ok = caps.includes("trend");
|
||||
btn.disabled = !ok;
|
||||
btn.style.opacity = ok ? "1" : "0.4";
|
||||
});
|
||||
let active = document.querySelector(".tabs button.active");
|
||||
if (active && active.disabled) {
|
||||
const first = [...document.querySelectorAll(".tabs button")].find((b) => !b.disabled);
|
||||
if (first) switchTradeTab(first.dataset.tab);
|
||||
}
|
||||
["order", "key", "trend"].forEach((t) => {
|
||||
document.getElementById("panel-" + t).classList.toggle(
|
||||
"hidden",
|
||||
!document.querySelector(`.tabs button[data-tab="${t}"]`).classList.contains("active")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function switchTradeTab(tab) {
|
||||
document.querySelectorAll(".tabs button").forEach((b) => {
|
||||
b.classList.toggle("active", b.dataset.tab === tab);
|
||||
});
|
||||
["order", "key", "trend"].forEach((t) => {
|
||||
document.getElementById("panel-" + t).classList.toggle("hidden", t !== tab);
|
||||
});
|
||||
trendPreviewId = null;
|
||||
document.getElementById("trend-preview-box").style.display = "none";
|
||||
}
|
||||
|
||||
async function loadTradeMeta() {
|
||||
const id = document.getElementById("trade-account").value;
|
||||
if (!id) return;
|
||||
try {
|
||||
const r = await fetch("/api/trade/meta/" + encodeURIComponent(id));
|
||||
const data = await r.json();
|
||||
tradeMeta = data.meta?.meta || data.meta || {};
|
||||
const el = document.getElementById("trade-meta");
|
||||
let txt = "";
|
||||
if (tradeMeta.key_gate_rule_text) txt = tradeMeta.key_gate_rule_text;
|
||||
else if (tradeMeta.trend_pullback_preview_ttl) {
|
||||
txt = `预览 ${tradeMeta.trend_pullback_preview_ttl}s · 补仓 ${tradeMeta.trend_pullback_dca_legs} 档 · 余额偏差 ≤${tradeMeta.trend_preview_max_drift_pct}%`;
|
||||
}
|
||||
el.textContent = txt;
|
||||
el.style.display = txt ? "block" : "none";
|
||||
} catch (e) {
|
||||
const el = document.getElementById("trade-meta");
|
||||
el.textContent = "";
|
||||
el.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
async function parseJsonResponse(r) {
|
||||
const text = await r.text();
|
||||
if (!text) return {};
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
const snippet = text.slice(0, 200);
|
||||
throw new Error(
|
||||
`HTTP ${r.status} 响应不是 JSON:${snippet}${text.length > 200 ? "…" : ""}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitForm(path, formEl) {
|
||||
const id = document.getElementById("trade-account").value;
|
||||
const fd = new FormData(formEl);
|
||||
try {
|
||||
const r = await fetch(path + encodeURIComponent(id), { method: "POST", body: fd });
|
||||
const j = await parseJsonResponse(r);
|
||||
const res = j.result || {};
|
||||
const msgs =
|
||||
(res.messages || []).join("\n") ||
|
||||
res.error ||
|
||||
res.msg ||
|
||||
res.text ||
|
||||
JSON.stringify(res, null, 2);
|
||||
const failed = res.ok === false || r.status >= 400 || !!res.error || !!res.text;
|
||||
showToast(msgs || (failed ? "操作失败" : "已提交"), failed);
|
||||
if (res.ok && res.preview) {
|
||||
showTrendPreview(res);
|
||||
}
|
||||
loadTradeMeta();
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
}
|
||||
}
|
||||
|
||||
function showTrendPreview(res) {
|
||||
trendPreviewId = res.preview_id;
|
||||
const p = res.preview || {};
|
||||
const box = document.getElementById("trend-preview-box");
|
||||
const levels = (p.grid_levels || [])
|
||||
.map((r) => `<tr><td>${r.i}</td><td>${r.price}</td><td>${r.contracts}</td></tr>`)
|
||||
.join("");
|
||||
box.innerHTML = `
|
||||
<div class="section-title">预览 #${esc(p.id || trendPreviewId)} · ${p.expires_in_sec ?? "?"}s</div>
|
||||
<div class="list-line">${esc(p.symbol)} ${esc(p.direction)} · ${p.leverage}x · 快照 ${fmt(p.snapshot_available_usdt, 2)} U</div>
|
||||
<table class="data-table"><thead><tr><th>#</th><th>补仓价</th><th>张数</th></tr></thead><tbody>${levels}</tbody></table>
|
||||
<div class="form-row" style="margin-top:8px">
|
||||
<button type="button" id="btn-trend-exec">确认执行(实盘)</button>
|
||||
</div>`;
|
||||
box.style.display = "block";
|
||||
document.getElementById("btn-trend-exec").onclick = executeTrend;
|
||||
}
|
||||
|
||||
async function executeTrend() {
|
||||
if (!trendPreviewId) {
|
||||
showToast("请先生成预览", true);
|
||||
return;
|
||||
}
|
||||
if (!confirm("确认按预览参数实盘下单?")) return;
|
||||
const id = document.getElementById("trade-account").value;
|
||||
const fd = new FormData();
|
||||
fd.set("preview_id", trendPreviewId);
|
||||
try {
|
||||
const r = await fetch("/api/trade/trend/execute/" + encodeURIComponent(id), {
|
||||
method: "POST",
|
||||
body: fd,
|
||||
});
|
||||
const j = await r.json();
|
||||
const res = j.result || {};
|
||||
showToast((res.messages || []).join("\n") || JSON.stringify(res), !res.ok);
|
||||
document.getElementById("trend-preview-box").style.display = "none";
|
||||
trendPreviewId = null;
|
||||
} catch (e) {
|
||||
showToast(String(e), true);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSettingsMetaLine() {
|
||||
try {
|
||||
const r = await fetch("/api/settings/meta");
|
||||
@@ -425,9 +268,8 @@
|
||||
<div class="field field-wide"><label>复盘链接(可空)</label><input class="ex-review" value="${esc(ex.review_url || "")}" placeholder="留空则自动生成 /records" /></div>
|
||||
</div>
|
||||
<div class="cap-chips">
|
||||
<label><input type="checkbox" class="cap-order" ${caps.includes("order") ? "checked" : ""}/> 下单</label>
|
||||
<label><input type="checkbox" class="cap-key" ${caps.includes("key") ? "checked" : ""}/> 关键位</label>
|
||||
<label><input type="checkbox" class="cap-trend" ${caps.includes("trend") ? "checked" : ""}/> 趋势</label>
|
||||
<label><input type="checkbox" class="cap-key" ${caps.includes("key") ? "checked" : ""}/> 监控关键位</label>
|
||||
<label><input type="checkbox" class="cap-trend" ${caps.includes("trend") ? "checked" : ""}/> 监控趋势计划</label>
|
||||
</div>
|
||||
<div class="settings-card-foot">
|
||||
<div class="field"><label>id</label><input class="ex-id" value="${esc(ex.id || "")}" /></div>
|
||||
@@ -442,7 +284,6 @@
|
||||
version: 1,
|
||||
exchanges: rows.map((card) => {
|
||||
const caps = [];
|
||||
if (card.querySelector(".cap-order").checked) caps.push("order");
|
||||
if (card.querySelector(".cap-key").checked) caps.push("key");
|
||||
if (card.querySelector(".cap-trend").checked) caps.push("trend");
|
||||
const id = card.querySelector(".ex-id").value.trim();
|
||||
@@ -482,44 +323,6 @@
|
||||
document.getElementById("btn-monitor-refresh").onclick = loadMonitorBoard;
|
||||
document.getElementById("auto-monitor").onchange = startMonitorPoll;
|
||||
document.getElementById("btn-close-all").onclick = closeAll;
|
||||
document.getElementById("trade-account").onchange = () => {
|
||||
syncTradeTabs();
|
||||
loadTradeMeta();
|
||||
};
|
||||
document.querySelectorAll(".tabs button").forEach((btn) => {
|
||||
btn.onclick = () => {
|
||||
if (!btn.disabled) switchTradeTab(btn.dataset.tab);
|
||||
};
|
||||
});
|
||||
document.getElementById("form-order").onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
submitForm("/api/trade/order/", e.target);
|
||||
};
|
||||
document.getElementById("form-key").onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
submitForm("/api/trade/key/", e.target);
|
||||
};
|
||||
document.getElementById("form-trend").onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
submitForm("/api/trade/trend/preview/", e.target);
|
||||
};
|
||||
document.getElementById("order-sltp-mode").onchange = function () {
|
||||
const pct = this.value === "pct";
|
||||
const slField = document.getElementById("order-sl").closest(".field");
|
||||
const tpField = document.getElementById("order-tp").closest(".field");
|
||||
if (slField) slField.style.display = pct ? "none" : "";
|
||||
if (tpField) tpField.style.display = pct ? "none" : "";
|
||||
document.getElementById("wrap-sl-pct").style.display = pct ? "" : "none";
|
||||
document.getElementById("wrap-tp-pct").style.display = pct ? "" : "none";
|
||||
};
|
||||
document.getElementById("key-sl-tp-mode").onchange = function () {
|
||||
const manual = this.value === "trend_manual";
|
||||
document.getElementById("wrap-key-manual-tp").style.display = manual ? "" : "none";
|
||||
};
|
||||
document.getElementById("trend-direction").onchange = function () {
|
||||
const lbl = document.getElementById("trend-add-label");
|
||||
if (lbl) lbl.textContent = this.value === "short" ? "补仓下沿价" : "补仓上沿价";
|
||||
};
|
||||
document.getElementById("btn-settings-save").onclick = saveSettings;
|
||||
document.getElementById("btn-settings-reload").onclick = loadSettingsUI;
|
||||
document.getElementById("btn-settings-add").onclick = () => {
|
||||
@@ -533,7 +336,7 @@
|
||||
agent_url: "http://127.0.0.1:15200",
|
||||
review_url: "",
|
||||
enabled: false,
|
||||
capabilities: ["order"],
|
||||
capabilities: ["key"],
|
||||
});
|
||||
settingsCache = data;
|
||||
loadSettingsUI();
|
||||
|
||||
Reference in New Issue
Block a user