diff --git a/app.py b/app.py
index ce9e9b9..75452b9 100644
--- a/app.py
+++ b/app.py
@@ -635,6 +635,10 @@ def api_cond_order_candidates():
has_pos = isinstance(brief, list) and len(brief) > 0
locked = bool(snap.get("cond_order_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(
{
"id": row.get("id"),
@@ -643,6 +647,7 @@ def api_cond_order_candidates():
"has_position": has_pos,
"cond_order_locked": locked,
"selectable": selectable,
+ "mode": mode,
"error": row.get("error") or "",
}
)
diff --git a/static/cond_orders.js b/static/cond_orders.js
index 46b6ac4..702c068 100644
--- a/static/cond_orders.js
+++ b/static/cond_orders.js
@@ -53,9 +53,76 @@
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 (
+ '"
+ );
+ }
+
+ function renderCandidateGroup(mode, items, checkedIds) {
+ if (!items.length) return "";
+ const label =
+ mode === "sim" || mode === "live"
+ ? '
' +
+ mode +
+ "
"
+ : "";
+ return (
+ '' +
+ label +
+ '
' +
+ items
+ .map(function (it) {
+ return renderCandidateCard(it, checkedIds);
+ })
+ .join("") +
+ "
"
+ );
+ }
+
async function loadCandidates() {
const box = document.getElementById("cf-instances");
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 d = await r.json();
const list = d.instances || [];
@@ -63,38 +130,19 @@
box.innerHTML = '暂无启用实例';
return;
}
- box.innerHTML = list
- .map(function (it) {
- const disabled = !it.selectable;
- const tip = disabled
- ? it.has_position
- ? "已有持仓"
- : it.cond_order_locked
- ? "条件单占用"
- : it.online
- ? "不可选"
- : "离线"
- : "可选";
- return (
- '"
- );
- })
- .join("");
+ const sim = [];
+ const live = [];
+ const other = [];
+ list.forEach(function (it) {
+ const m = instanceMode(it);
+ if (m === "sim") sim.push(it);
+ else if (m === "live") live.push(it);
+ else other.push(it);
+ });
+ box.innerHTML =
+ renderCandidateGroup("sim", sim, checkedIds) +
+ renderCandidateGroup("live", live, checkedIds) +
+ renderCandidateGroup("", other, checkedIds);
}
async function loadOrders() {
diff --git a/static/hub.css b/static/hub.css
index a119b8e..82c6460 100644
--- a/static/hub.css
+++ b/static/hub.css
@@ -1350,6 +1350,8 @@ input.readonly {
min-height: 62px;
}
.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 {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
diff --git a/templates/base.html b/templates/base.html
index 00bfced..4c97256 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -4,7 +4,7 @@
{% block title %}期权中控{% endblock %}
-
+
{% if session.get('logged_in') %}
diff --git a/templates/cond_orders.html b/templates/cond_orders.html
index 7f392ff..f472f59 100644
--- a/templates/cond_orders.html
+++ b/templates/cond_orders.html
@@ -62,7 +62,7 @@