条件单客户端勾选按 sim / live 分组,没有对应模式则不显示该组。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -635,6 +635,10 @@ def api_cond_order_candidates():
|
|||||||
has_pos = isinstance(brief, list) and len(brief) > 0
|
has_pos = isinstance(brief, list) and len(brief) > 0
|
||||||
locked = bool(snap.get("cond_order_locked"))
|
locked = bool(snap.get("cond_order_locked"))
|
||||||
selectable = bool(row.get("online")) and not has_pos and not locked
|
selectable = bool(row.get("online")) and not has_pos and not locked
|
||||||
|
inst = snap.get("instance") if isinstance(snap.get("instance"), dict) else {}
|
||||||
|
mode = str(inst.get("mode") or "").strip().lower()
|
||||||
|
if mode not in ("sim", "live"):
|
||||||
|
mode = ""
|
||||||
out.append(
|
out.append(
|
||||||
{
|
{
|
||||||
"id": row.get("id"),
|
"id": row.get("id"),
|
||||||
@@ -643,6 +647,7 @@ def api_cond_order_candidates():
|
|||||||
"has_position": has_pos,
|
"has_position": has_pos,
|
||||||
"cond_order_locked": locked,
|
"cond_order_locked": locked,
|
||||||
"selectable": selectable,
|
"selectable": selectable,
|
||||||
|
"mode": mode,
|
||||||
"error": row.get("error") or "",
|
"error": row.get("error") or "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
+80
-32
@@ -53,9 +53,76 @@
|
|||||||
return "st-done";
|
return "st-done";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function instanceMode(it) {
|
||||||
|
const m = String((it && it.mode) || "").toLowerCase();
|
||||||
|
return m === "sim" || m === "live" ? m : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCandidateCard(it, checkedIds) {
|
||||||
|
const disabled = !it.selectable;
|
||||||
|
const tip = disabled
|
||||||
|
? it.has_position
|
||||||
|
? "已有持仓"
|
||||||
|
: it.cond_order_locked
|
||||||
|
? "条件单占用"
|
||||||
|
: it.online
|
||||||
|
? "不可选"
|
||||||
|
: "离线"
|
||||||
|
: "可选";
|
||||||
|
const id = String(it.id);
|
||||||
|
const checked = !disabled && checkedIds.indexOf(id) >= 0;
|
||||||
|
return (
|
||||||
|
'<label class="cond-inst' +
|
||||||
|
(disabled ? " is-off" : "") +
|
||||||
|
'" title="' +
|
||||||
|
escapeHtml(tip) +
|
||||||
|
'">' +
|
||||||
|
'<input type="checkbox" name="cf-inst" value="' +
|
||||||
|
encodeURIComponent(id) +
|
||||||
|
'"' +
|
||||||
|
(disabled ? " disabled" : "") +
|
||||||
|
(checked ? " checked" : "") +
|
||||||
|
">" +
|
||||||
|
'<span class="cond-inst-name">' +
|
||||||
|
escapeHtml(it.name || "") +
|
||||||
|
"</span>" +
|
||||||
|
'<span class="cond-inst-tag">' +
|
||||||
|
escapeHtml(tip) +
|
||||||
|
"</span></label>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCandidateGroup(mode, items, checkedIds) {
|
||||||
|
if (!items.length) return "";
|
||||||
|
const label =
|
||||||
|
mode === "sim" || mode === "live"
|
||||||
|
? '<div class="cond-inst-group-label"><span class="overview-mode-tag overview-mode-tag--' +
|
||||||
|
mode +
|
||||||
|
'">' +
|
||||||
|
mode +
|
||||||
|
"</span></div>"
|
||||||
|
: "";
|
||||||
|
return (
|
||||||
|
'<div class="cond-inst-group">' +
|
||||||
|
label +
|
||||||
|
'<div class="cond-inst-grid">' +
|
||||||
|
items
|
||||||
|
.map(function (it) {
|
||||||
|
return renderCandidateCard(it, checkedIds);
|
||||||
|
})
|
||||||
|
.join("") +
|
||||||
|
"</div></div>"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadCandidates() {
|
async function loadCandidates() {
|
||||||
const box = document.getElementById("cf-instances");
|
const box = document.getElementById("cf-instances");
|
||||||
if (!box) return;
|
if (!box) return;
|
||||||
|
const checkedIds = Array.prototype.slice
|
||||||
|
.call(box.querySelectorAll('input[name="cf-inst"]:checked'))
|
||||||
|
.map(function (el) {
|
||||||
|
return decodeURIComponent(el.value || "");
|
||||||
|
});
|
||||||
const r = await fetch("/api/cond_orders/candidates");
|
const r = await fetch("/api/cond_orders/candidates");
|
||||||
const d = await r.json();
|
const d = await r.json();
|
||||||
const list = d.instances || [];
|
const list = d.instances || [];
|
||||||
@@ -63,38 +130,19 @@
|
|||||||
box.innerHTML = '<span class="muted">暂无启用实例</span>';
|
box.innerHTML = '<span class="muted">暂无启用实例</span>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
box.innerHTML = list
|
const sim = [];
|
||||||
.map(function (it) {
|
const live = [];
|
||||||
const disabled = !it.selectable;
|
const other = [];
|
||||||
const tip = disabled
|
list.forEach(function (it) {
|
||||||
? it.has_position
|
const m = instanceMode(it);
|
||||||
? "已有持仓"
|
if (m === "sim") sim.push(it);
|
||||||
: it.cond_order_locked
|
else if (m === "live") live.push(it);
|
||||||
? "条件单占用"
|
else other.push(it);
|
||||||
: it.online
|
});
|
||||||
? "不可选"
|
box.innerHTML =
|
||||||
: "离线"
|
renderCandidateGroup("sim", sim, checkedIds) +
|
||||||
: "可选";
|
renderCandidateGroup("live", live, checkedIds) +
|
||||||
return (
|
renderCandidateGroup("", other, checkedIds);
|
||||||
'<label class="cond-inst' +
|
|
||||||
(disabled ? " is-off" : "") +
|
|
||||||
'" title="' +
|
|
||||||
escapeHtml(tip) +
|
|
||||||
'">' +
|
|
||||||
'<input type="checkbox" name="cf-inst" value="' +
|
|
||||||
encodeURIComponent(String(it.id)) +
|
|
||||||
'"' +
|
|
||||||
(disabled ? " disabled" : "") +
|
|
||||||
">" +
|
|
||||||
'<span class="cond-inst-name">' +
|
|
||||||
escapeHtml(it.name || "") +
|
|
||||||
"</span>" +
|
|
||||||
'<span class="cond-inst-tag">' +
|
|
||||||
escapeHtml(tip) +
|
|
||||||
"</span></label>"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.join("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadOrders() {
|
async function loadOrders() {
|
||||||
|
|||||||
@@ -1350,6 +1350,8 @@ input.readonly {
|
|||||||
min-height: 62px;
|
min-height: 62px;
|
||||||
}
|
}
|
||||||
.cond-switch input { width: auto; margin: 0; }
|
.cond-switch input { width: auto; margin: 0; }
|
||||||
|
.cond-inst-group + .cond-inst-group { margin-top: 14px; }
|
||||||
|
.cond-inst-group-label { margin-bottom: 8px; }
|
||||||
.cond-inst-grid {
|
.cond-inst-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{% block title %}期权中控{% endblock %}</title>
|
<title>{% block title %}期权中控{% endblock %}</title>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=19">
|
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=20">
|
||||||
</head>
|
</head>
|
||||||
<body class="{% if request.path.startswith('/login') %}page-login{% elif request.path.startswith('/desk') %}page-desk{% elif request.path.startswith('/stats') %}page-stats{% elif request.path.startswith('/instances') %}page-instances{% elif request.path == '/' %}page-dashboard{% endif %}">
|
<body class="{% if request.path.startswith('/login') %}page-login{% elif request.path.startswith('/desk') %}page-desk{% elif request.path.startswith('/stats') %}page-stats{% elif request.path.startswith('/instances') %}page-instances{% elif request.path == '/' %}page-dashboard{% endif %}">
|
||||||
{% if session.get('logged_in') %}
|
{% if session.get('logged_in') %}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
<div class="cond-section">
|
<div class="cond-section">
|
||||||
<div class="cond-section-title">客户端</div>
|
<div class="cond-section-title">客户端</div>
|
||||||
<div id="cf-instances" class="cond-inst-grid"></div>
|
<div id="cf-instances"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="cond-actions">
|
<div class="cond-actions">
|
||||||
|
|||||||
Reference in New Issue
Block a user