diff --git a/lib/hub/hub_strategy_lib.py b/lib/hub/hub_strategy_lib.py
index eaf995a..1f37664 100644
--- a/lib/hub/hub_strategy_lib.py
+++ b/lib/hub/hub_strategy_lib.py
@@ -218,16 +218,22 @@ def strategy_meta_payload() -> dict[str, Any]:
return {"ok": True, "exchanges": tabs}
-def _checklist_html(checklist: dict[str, Any]) -> str:
+def _checklist_html(checklist: dict[str, Any], *, include_title: bool = True) -> str:
groups = checklist.get("groups") or []
- parts = [f"
{escape_html(str(checklist.get('title') or '开仓检查清单'))}
"]
+ parts: list[str] = []
+ if include_title:
+ parts.append(f"{escape_html(str(checklist.get('title') or '开仓检查清单'))}
")
for grp in groups:
if not isinstance(grp, dict):
continue
gtitle = escape_html(str(grp.get("title") or ""))
parts.append(f"{gtitle}
")
for item in grp.get("items") or []:
- parts.append(f"- ☐ {escape_html(str(item))}
")
+ # 用 CSS 方框代替 ☐,避免部分打印机/预览吞掉 Unicode 勾选符
+ parts.append(
+ f"- "
+ f"{escape_html(str(item))}
"
+ )
parts.append("
")
footnotes = checklist.get("footnotes") or []
if footnotes:
@@ -330,15 +336,28 @@ body {
padding: 0;
}
.checklist li {
- margin: 7px 0;
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+ margin: 8px 0;
padding: 0;
}
.box {
+ flex: 0 0 auto;
display: inline-block;
- width: 1.05em;
- margin-right: 6px;
- font-size: 1.05em;
- line-height: 1.2;
+ width: 0.95em;
+ height: 0.95em;
+ margin-top: 0.2em;
+ border: 1.5px solid #333;
+ border-radius: 2px;
+ background: #fff;
+ box-sizing: border-box;
+ -webkit-print-color-adjust: exact;
+ print-color-adjust: exact;
+}
+.item-text {
+ flex: 1 1 auto;
+ min-width: 0;
}
.footnotes {
margin: 18px 0 0;
@@ -364,16 +383,18 @@ body {
def _print_auto_script() -> str:
+ # 不在 afterprint 里 window.close(): Firefox 会在打开打印框时就触发 afterprint,
+ # 短页(检查清单)更容易被立刻关掉,表现为「打不开/打不出来」;策略长文相对不易复现.
return """"""
@@ -398,7 +419,7 @@ def build_print_html(exchange_key: str, part: str = "doc") -> str:
{escape_html(cl_title)}
{escape_html(label)} · {escape_html(version)} · 打印 {escape_html(now)}
-{_checklist_html(checklist)}
+{_checklist_html(checklist, include_title=False)}
"""
elif part == "doc":
page_title = f"{label} · 策略说明"
@@ -453,8 +474,9 @@ h1{{font-size:1.35rem;margin:0 0 8px}}
.strategy-doc table{{border-collapse:collapse;width:100%;font-size:.88rem}}
.strategy-doc th,.strategy-doc td{{border:1px solid #ccc;padding:6px 8px;text-align:left}}
.checklist ul{{list-style:none;padding:0;margin:0}}
-.checklist li{{margin:8px 0;padding-left:0}}
-.box{{display:inline-block;width:1em;margin-right:6px}}
+.checklist li{{display:flex;align-items:flex-start;gap:8px;margin:8px 0}}
+.box{{flex:0 0 auto;display:inline-block;width:.95em;height:.95em;margin-top:.2em;border:1.5px solid #333;border-radius:2px;background:#fff;box-sizing:border-box}}
+.item-text{{flex:1 1 auto}}
@media print{{body{{margin:12mm}}}}
diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html
index 4da8fd3..c7aeac5 100644
--- a/manual_trading_hub/static/index.html
+++ b/manual_trading_hub/static/index.html
@@ -1374,7 +1374,7 @@
-
+
diff --git a/manual_trading_hub/static/strategy.js b/manual_trading_hub/static/strategy.js
index b2e7bac..d81ce4b 100644
--- a/manual_trading_hub/static/strategy.js
+++ b/manual_trading_hub/static/strategy.js
@@ -142,12 +142,41 @@
renderTabs();
}
- function printSection(mode) {
+ async function printSection(mode) {
const part = mode === "checklist" ? "checklist" : "doc";
const url = `/api/strategy/${encodeURIComponent(activeKey)}/print?part=${encodeURIComponent(part)}`;
- const w = window.open(url, "_blank", "noopener,noreferrer");
+ // 同步打开空白页保留用户手势;再 fetch 写入,避免 noopener 丢句柄 / 短页误关窗
+ const w = window.open("about:blank", "_blank");
if (!w) {
if (statusEl) statusEl.textContent = "请允许弹出窗口以打开打印预览";
+ return;
+ }
+ try {
+ w.document.write(
+ "打印准备中…正在加载打印内容…"
+ );
+ w.document.close();
+ } catch (_) {}
+ try {
+ const r = await fetch(url, { credentials: "same-origin" });
+ const html = await r.text();
+ if (!r.ok) {
+ throw new Error((html && html.slice(0, 120)) || r.statusText || "加载打印页失败");
+ }
+ w.document.open();
+ w.document.write(html);
+ w.document.close();
+ if (statusEl) statusEl.textContent = "";
+ } catch (e) {
+ const msg = String(e && e.message ? e.message : e);
+ try {
+ w.document.open();
+ w.document.write(
+ `打印页加载失败: ${esc(msg)}`
+ );
+ w.document.close();
+ } catch (_) {}
+ if (statusEl) statusEl.textContent = msg;
}
}
diff --git a/tests/test_hub_strategy_lib.py b/tests/test_hub_strategy_lib.py
index 3f3ab79..426127b 100644
--- a/tests/test_hub_strategy_lib.py
+++ b/tests/test_hub_strategy_lib.py
@@ -33,16 +33,19 @@ class TestHubStrategyLib(unittest.TestCase):
def test_export_html_contains_checklist(self):
html = build_export_html("gate")
self.assertIn("Gate", html)
- self.assertIn("☐", html)
+ self.assertIn('class="box"', html)
+ self.assertNotIn("☐", html)
def test_print_html_doc_and_checklist(self):
doc = build_print_html("binance", "doc")
cl = build_print_html("binance", "checklist")
self.assertIn("window.print", doc)
self.assertIn("window.print", cl)
+ self.assertNotIn("window.close", cl)
self.assertIn("币安", doc)
self.assertIn("开仓检查清单", cl)
- self.assertIn("☐", cl)
+ self.assertIn('class="box"', cl)
+ self.assertIn("本账户仅做多", cl)
if __name__ == "__main__":