Add spot sell retry UI; sell all trading-account coin on sell-back.
Fix coin-margin sell-back to use full available balance instead of bridge record; show retry banner and button on the options positions panel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5233,6 +5233,24 @@ html[data-theme="light"] .opt-source-badge--oo {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 600;
|
||||
}
|
||||
.opt-spot-sell-banner {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px 12px;
|
||||
margin: 0 0 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(255, 170, 80, 0.45);
|
||||
border-radius: 10px;
|
||||
background: rgba(48, 32, 12, 0.55);
|
||||
}
|
||||
.opt-spot-sell-banner-text {
|
||||
flex: 1;
|
||||
min-width: 180px;
|
||||
font-size: 0.82rem;
|
||||
color: #f5d9b8;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.opt-target-monitors {
|
||||
margin: 0 0 10px;
|
||||
padding: 10px 12px;
|
||||
@@ -5836,6 +5854,13 @@ html[data-theme="light"] .options-page-wrap .pos-empty {
|
||||
color: #334155 !important;
|
||||
}
|
||||
|
||||
html[data-theme="light"] .options-page-wrap .opt-spot-sell-banner {
|
||||
border-color: rgba(180, 110, 40, 0.45);
|
||||
background: rgba(255, 244, 228, 0.95);
|
||||
}
|
||||
html[data-theme="light"] .options-page-wrap .opt-spot-sell-banner-text {
|
||||
color: #6a4a18;
|
||||
}
|
||||
html[data-theme="light"] .options-page-wrap .opt-target-monitors {
|
||||
background: #eef4fa !important;
|
||||
border: 1px solid #94a3b8 !important;
|
||||
|
||||
@@ -2166,7 +2166,7 @@
|
||||
if (ss && ss.ok && !ss.skipped) {
|
||||
okMsg += "\n已自动卖回 USDT";
|
||||
} else if (ss && ss.bridge_status === "pending_sell_spot") {
|
||||
okMsg += "\n卖回 USDT 失败,请点「重试卖回」";
|
||||
okMsg += "\n卖回 USDT 失败,请点持仓区「重试卖回」";
|
||||
}
|
||||
alert(okMsg);
|
||||
} else {
|
||||
@@ -2210,6 +2210,82 @@
|
||||
setOptionsPosTab(state.posTab);
|
||||
}
|
||||
|
||||
function shouldShowSpotSellBanner(d, positions) {
|
||||
if (!isCoinMarginMode()) return false;
|
||||
const ss = d && d.spot_sell;
|
||||
if (!ss) return false;
|
||||
const hasCoinPos = (positions || []).some(function (p) { return isCoinPos(p); });
|
||||
if (hasCoinPos) return false;
|
||||
if (ss.bridge_status === "pending_sell_spot") return true;
|
||||
const eth = Number(ss.trading_eth || 0);
|
||||
const btc = Number(ss.trading_btc || 0);
|
||||
return eth > 0.00001 || btc > 0.000001;
|
||||
}
|
||||
|
||||
function spotSellUnderlying(ss) {
|
||||
if (!ss) return (state.underlying || "ETH").toUpperCase();
|
||||
if (ss.bridge_underlying) return String(ss.bridge_underlying).toUpperCase();
|
||||
const eth = Number(ss.trading_eth || 0);
|
||||
const btc = Number(ss.trading_btc || 0);
|
||||
if (btc > eth && btc > 0.000001) return "BTC";
|
||||
if (eth > 0.00001) return "ETH";
|
||||
return (ss.default_underly || state.underlying || "ETH").toUpperCase();
|
||||
}
|
||||
|
||||
function paintSpotSellBanner(d, positions) {
|
||||
const banner = document.getElementById("opt-spot-sell-banner");
|
||||
const textEl = document.getElementById("opt-spot-sell-banner-text");
|
||||
if (!banner || !textEl) return;
|
||||
const show = shouldShowSpotSellBanner(d, positions);
|
||||
if (!show) {
|
||||
banner.hidden = true;
|
||||
state.spotSellUnderlying = null;
|
||||
return;
|
||||
}
|
||||
const ss = d.spot_sell || {};
|
||||
const uly = spotSellUnderlying(ss);
|
||||
state.spotSellUnderlying = uly;
|
||||
const eth = Number(ss.trading_eth || 0);
|
||||
const btc = Number(ss.trading_btc || 0);
|
||||
const amt = uly === "BTC" ? btc : eth;
|
||||
let msg = "交易账户残留 " + uly;
|
||||
if (amt > 0) msg += " " + fmt(amt, 6);
|
||||
if (ss.bridge_status === "pending_sell_spot") {
|
||||
msg += ",自动卖回 USDT 失败";
|
||||
} else {
|
||||
msg += ",可卖回 USDT";
|
||||
}
|
||||
textEl.textContent = msg;
|
||||
banner.hidden = false;
|
||||
}
|
||||
|
||||
async function retrySpotSell() {
|
||||
const btn = document.getElementById("opt-spot-sell-retry-btn");
|
||||
const uly = (state.spotSellUnderlying || state.underlying || "ETH").toUpperCase();
|
||||
if (!confirm("将交易账户全部 " + uly + " 市价卖回 USDT,确认?")) return;
|
||||
if (btn) btn.disabled = true;
|
||||
try {
|
||||
const r = await apiJson("/api/options/spot-bridge/retry-sell", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ underlying: uly }),
|
||||
});
|
||||
if (r.ok) {
|
||||
if (r.skipped) {
|
||||
alert(r.msg || "无残留币需卖回");
|
||||
} else {
|
||||
alert("已卖回 USDT" + (r.sell && r.sell.coin_sold != null ? (" (" + r.sell.coin_sold + " " + uly + ")") : ""));
|
||||
}
|
||||
refreshAllPositions();
|
||||
if (typeof refreshAccountSnapshot === "function") refreshAccountSnapshot();
|
||||
} else {
|
||||
alert(r.msg || "卖回失败");
|
||||
}
|
||||
} finally {
|
||||
if (btn) btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePositionsList(d) {
|
||||
const now = Date.now();
|
||||
const list = (d && d.ok && d.positions) ? d.positions : [];
|
||||
@@ -2307,6 +2383,7 @@
|
||||
if (seq !== positionsRefreshSeq) return;
|
||||
const list = resolvePositionsList(d);
|
||||
paintPositions(list);
|
||||
paintSpotSellBanner(d, list);
|
||||
const fromPos = list.reduce(function (targets, p) {
|
||||
if (!p) return targets;
|
||||
if (p.target_index != null) {
|
||||
@@ -2706,6 +2783,8 @@
|
||||
document.getElementById("opt-load-chain").addEventListener("click", loadChain);
|
||||
document.getElementById("opt-refresh-positions").addEventListener("click", refreshAllPositions);
|
||||
document.getElementById("opt-open-btn").addEventListener("click", openPosition);
|
||||
const spotSellRetryBtn = document.getElementById("opt-spot-sell-retry-btn");
|
||||
if (spotSellRetryBtn) spotSellRetryBtn.addEventListener("click", retrySpotSell);
|
||||
const pendingRefreshBtn = document.getElementById("opt-pending-refresh");
|
||||
if (pendingRefreshBtn) {
|
||||
pendingRefreshBtn.addEventListener("click", function () {
|
||||
|
||||
Reference in New Issue
Block a user