Files
crypto_okx/lib/common/static/options_settings.js
T

357 lines
12 KiB
JavaScript

(function () {
"use strict";
const hasSettings = !!document.getElementById("options-settings-root");
const hasPageFunds = !!document.getElementById("options-funds-card");
if (!hasSettings && !hasPageFunds) return;
async function apiJson(url, opts) {
const r = await fetch(url, Object.assign({ credentials: "same-origin" }, opts || {}));
return r.json();
}
function refreshFundsAfterMutation() {
if (typeof refreshAccountSnapshot !== "function") return;
refreshAccountSnapshot({ force: true });
setTimeout(function () {
refreshAccountSnapshot({ force: true, silent: true });
}, 1500);
}
function setMsg(id, text, isErr) {
const el = document.getElementById(id);
if (!el) return;
el.textContent = text || "";
el.classList.toggle("opt-error", !!isErr);
el.classList.toggle("opt-success", !!text && !isErr);
}
function fmtAmt(amount, ccy) {
return `${Number(amount).toFixed(2)} ${ccy}`;
}
function accountLabel(acct) {
return acct === "trading" ? "交易账户" : "资金账户";
}
function swapDirLabel(dir) {
return dir === "usdc_to_usdt" ? "USDC → USDT" : "USDT → USDC";
}
function confirmOk(message) {
return window.confirm(message);
}
function roundAvail(v, ccy) {
const n = Number(v);
if (!Number.isFinite(n) || n <= 0) return null;
const u = String(ccy || "").toUpperCase();
if (u === "ETH" || u === "BTC") {
return Math.round(n * 1e8) / 1e8;
}
return Math.round(n * 100) / 100;
}
async function loadBalances(force, scope) {
const parts = [];
if (force) parts.push("force=1");
if (scope && scope !== "main") parts.push("scope=" + encodeURIComponent(scope));
const q = parts.length ? "?" + parts.join("&") : "";
const d = await apiJson("/api/options/balances" + q);
if (!d.ok) throw new Error(d.msg || "余额拉取失败");
return d;
}
function pickBalance(bal, account, ccy) {
const acct = account === "trading" ? "trading" : "funding";
const c = String(ccy || "").toLowerCase();
const availKey = acct + "_" + c + "_avail";
const totalKey = acct + "_" + c;
return roundAvail(bal[availKey] != null ? bal[availKey] : bal[totalKey], ccy);
}
async function resolveSwapMaxAmount(dir) {
const bal = await loadBalances(true, "main");
const ccy = dir === "usdc_to_usdt" ? "USDC" : "USDT";
let amount = pickBalance(bal, "trading", ccy);
let source = "trading";
if (!amount) {
const fundingAmt = pickBalance(bal, "funding", ccy);
if (fundingAmt) {
amount = fundingAmt;
source = "funding";
}
}
return { amount, bal, ccy, source };
}
async function resolveMaxAmount(account, ccy, scope) {
const bal = await loadBalances(true, scope || "main");
return pickBalance(bal, account, ccy);
}
function setButtonsBusy(btnIds, amountId, busy, busyText) {
btnIds.forEach(function (id) {
const btn = document.getElementById(id);
if (!btn) return;
if (busy) {
if (!btn.dataset.origText) btn.dataset.origText = btn.textContent;
btn.disabled = true;
if (busyText) btn.textContent = busyText;
} else {
btn.disabled = false;
if (btn.dataset.origText) {
btn.textContent = btn.dataset.origText;
delete btn.dataset.origText;
}
}
});
const input = document.getElementById(amountId);
if (input) input.disabled = busy;
}
function hardenAmountAutofill(ids) {
ids.forEach(function (id) {
const el = document.getElementById(id);
if (!el) return;
function wipe() {
const v = String(el.value || "").trim();
if (/^[a-z][a-z0-9._-]{1,31}$/i.test(v)) el.value = "";
}
wipe();
el.setAttribute("readonly", "readonly");
el.addEventListener("focus", function () {
el.removeAttribute("readonly");
});
el.addEventListener("blur", function () {
if (!el.value) el.setAttribute("readonly", "readonly");
});
setTimeout(wipe, 200);
setTimeout(wipe, 800);
setTimeout(wipe, 2000);
});
}
function bindFundsUi(ids) {
const swapBtn = document.getElementById(ids.swapBtn);
const intBtn = document.getElementById(ids.intBtn);
if (!swapBtn && !intBtn) return;
const swapBtns = [ids.swapBtn, ids.swapAllBtn].filter(Boolean);
const intBtns = [ids.intBtn, ids.intAllBtn].filter(Boolean);
async function submitSwap(amount) {
setButtonsBusy(swapBtns, ids.swapAmount, true, "兑换中…");
setMsg(ids.swapMsg, "兑换中,市价成交可能有延时…", false);
try {
const d = await apiJson("/api/options/spot/swap", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
direction: document.getElementById(ids.swapDir).value,
amount: amount,
}),
});
if (d.ok) {
setMsg(ids.swapMsg, "兑换成功", false);
refreshFundsAfterMutation();
} else {
setMsg(ids.swapMsg, "兑换失败:" + (d.msg || "未知错误"), true);
}
return d;
} catch (e) {
setMsg(ids.swapMsg, "兑换失败:" + (e.message || "网络错误"), true);
return { ok: false };
} finally {
setButtonsBusy(swapBtns, ids.swapAmount, false);
}
}
async function submitInternalTransfer(amount) {
setButtonsBusy(intBtns, ids.intAmount, true, "划转中…");
setMsg(ids.intMsg, "划转中…", false);
try {
const d = await apiJson("/api/options/transfer", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
ccy: document.getElementById(ids.intCcy).value,
from: document.getElementById(ids.intFrom).value,
to: document.getElementById(ids.intTo).value,
amount: amount,
}),
});
if (d.ok) {
setMsg(ids.intMsg, "划转成功", false);
refreshFundsAfterMutation();
} else {
setMsg(ids.intMsg, "划转失败:" + (d.msg || "未知错误"), true);
}
return d;
} catch (e) {
setMsg(ids.intMsg, "划转失败:" + (e.message || "网络错误"), true);
return { ok: false };
} finally {
setButtonsBusy(intBtns, ids.intAmount, false);
}
}
if (swapBtn) {
swapBtn.addEventListener("click", async function () {
const amount = parseFloat(document.getElementById(ids.swapAmount).value);
if (!amount || amount <= 0) {
setMsg(ids.swapMsg, "请输入有效数量", true);
return;
}
await submitSwap(amount);
});
}
const swapAllBtn = document.getElementById(ids.swapAllBtn);
if (swapAllBtn) {
swapAllBtn.addEventListener("click", async function () {
try {
const dir = document.getElementById(ids.swapDir).value;
const { amount, bal, ccy, source } = await resolveSwapMaxAmount(dir);
if (!amount) {
const fu = bal.funding_usdt_avail != null ? bal.funding_usdt_avail : bal.funding_usdt;
const tu = bal.trading_usdt_avail != null ? bal.trading_usdt_avail : bal.trading_usdt;
const fc = bal.funding_usdc_avail != null ? bal.funding_usdc_avail : bal.funding_usdc;
setMsg(
ids.swapMsg,
"资金账户可用 " +
ccy +
" 不足(资金户 USDT:" +
(fu != null ? fu : "—") +
" USDC:" +
(fc != null ? fc : "—") +
"; 交易户 USDT:" +
(tu != null ? tu : "—") +
")",
true
);
return;
}
const srcLabel = source === "trading" ? "交易账户" : "资金账户";
const msg =
"确认全部兑换?\n\n" +
"方向:" +
swapDirLabel(dir) +
"\n" +
"金额:" +
fmtAmt(amount, ccy) +
"\n" +
"来源:" +
srcLabel +
"\n\n" +
"将按该账户可用余额发起市价兑换(可能有延时)。请确认。";
if (!confirmOk(msg)) return;
document.getElementById(ids.swapAmount).value = String(amount);
await submitSwap(amount);
} catch (e) {
setMsg(ids.swapMsg, "兑换失败:" + (e.message || "余额拉取失败"), true);
}
});
}
if (intBtn) {
intBtn.addEventListener("click", async function () {
const amount = parseFloat(document.getElementById(ids.intAmount).value);
if (!amount || amount <= 0) {
setMsg(ids.intMsg, "请输入有效数量", true);
return;
}
await submitInternalTransfer(amount);
});
}
const intAllBtn = document.getElementById(ids.intAllBtn);
if (intAllBtn) {
intAllBtn.addEventListener("click", async function () {
try {
const ccy = document.getElementById(ids.intCcy).value;
const from = document.getElementById(ids.intFrom).value;
const to = document.getElementById(ids.intTo).value;
const amount = await resolveMaxAmount(from, ccy, "main");
if (!amount) {
setMsg(ids.intMsg, "划出账户可用余额不足", true);
return;
}
const msg =
"确认全部划转?\n\n" +
"币种:" +
ccy +
"\n" +
"划出:" +
accountLabel(from) +
"\n" +
"划入:" +
accountLabel(to) +
"\n" +
"金额:" +
fmtAmt(amount, ccy) +
"\n\n" +
"将划转该账户全部可用余额。";
if (!confirmOk(msg)) return;
document.getElementById(ids.intAmount).value = String(amount);
await submitInternalTransfer(amount);
} catch (e) {
setMsg(ids.intMsg, "划转失败:" + (e.message || "余额拉取失败"), true);
}
});
}
[ids.swapAllBtn, ids.intAllBtn].forEach(function (btnId) {
const btn = document.getElementById(btnId);
if (!btn) return;
btn.addEventListener(
"click",
function () {
const map = {};
map[ids.swapAllBtn] = ids.swapAmount;
map[ids.intAllBtn] = ids.intAmount;
const input = document.getElementById(map[btnId]);
if (input) input.removeAttribute("readonly");
},
true
);
});
hardenAmountAutofill([ids.swapAmount, ids.intAmount]);
}
if (hasSettings) {
bindFundsUi({
swapDir: "opt-set-swap-dir",
swapAmount: "opt-set-swap-amount",
swapBtn: "opt-set-swap-btn",
swapAllBtn: "opt-set-swap-all-btn",
swapMsg: "opt-set-swap-msg",
intCcy: "opt-set-int-ccy",
intFrom: "opt-set-int-from",
intTo: "opt-set-int-to",
intAmount: "opt-set-int-amount",
intBtn: "opt-set-int-btn",
intAllBtn: "opt-set-int-all-btn",
intMsg: "opt-set-int-msg",
});
}
if (hasPageFunds) {
bindFundsUi({
swapDir: "opt-page-swap-dir",
swapAmount: "opt-page-swap-amount",
swapBtn: "opt-page-swap-btn",
swapAllBtn: "opt-page-swap-all-btn",
swapMsg: "opt-page-swap-msg",
intCcy: "opt-page-int-ccy",
intFrom: "opt-page-int-from",
intTo: "opt-page-int-to",
intAmount: "opt-page-int-amount",
intBtn: "opt-page-int-btn",
intAllBtn: "opt-page-int-all-btn",
intMsg: "opt-page-int-msg",
});
}
})();