diff --git a/app.py b/app.py index 575047b..444249b 100644 --- a/app.py +++ b/app.py @@ -307,13 +307,19 @@ def _handle_wechat_settings(*, send_test: bool = False) -> None: def settings_page(): if request.method == "POST": action = (request.form.get("form_action") or "account").strip() + return_tab = (request.form.get("return_tab") or "").strip() if action == "wechat": _handle_wechat_settings(send_test=False) + return_tab = return_tab or "wechat" elif action == "wechat_test": _handle_wechat_settings(send_test=True) + return_tab = return_tab or "wechat" else: _handle_account_settings() - return redirect(url_for("settings_page")) + return_tab = return_tab or "account" + if return_tab not in ("account", "wechat", "backup"): + return_tab = "account" + return redirect(url_for("settings_page", tab=return_tab)) return render_template( "settings.html", diff --git a/static/hub.css b/static/hub.css index a014bf3..fba33ae 100644 --- a/static/hub.css +++ b/static/hub.css @@ -586,6 +586,68 @@ button, .btn { font-weight: 600; } .settings-lead { margin: 6px 0 0; max-width: 520px; line-height: 1.45; } +.settings-shell { + max-width: 920px; + margin: 0; + padding: 0; + overflow: hidden; + background: + linear-gradient(165deg, rgba(20, 36, 58, 0.92), rgba(10, 16, 28, 0.96)); + border: 1px solid var(--line-strong); + box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.04); +} +.settings-tab-radio { + position: absolute; + opacity: 0; + pointer-events: none; + width: 0; + height: 0; +} +.settings-tabs { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 14px 16px 0; + border-bottom: 1px solid rgba(56, 189, 248, 0.14); + background: rgba(5, 12, 22, 0.35); +} +.settings-tab-btn { + display: inline-flex; + align-items: center; + padding: 9px 14px; + border-radius: 10px 10px 0 0; + border: 1px solid transparent; + border-bottom: 0; + color: var(--muted); + font-size: 0.88rem; + font-weight: 550; + cursor: pointer; + user-select: none; + transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease; +} +.settings-tab-btn:hover { + color: var(--text); + background: rgba(56, 189, 248, 0.08); +} +#hub-set-account:checked ~ .settings-tabs label[for="hub-set-account"], +#hub-set-wechat:checked ~ .settings-tabs label[for="hub-set-wechat"], +#hub-set-backup:checked ~ .settings-tabs label[for="hub-set-backup"] { + color: var(--text); + background: rgba(34, 211, 238, 0.12); + border-color: rgba(56, 189, 248, 0.35); + box-shadow: inset 0 -2px 0 var(--accent); +} +.settings-panels { + padding: 0; +} +.settings-panel { + display: none; +} +#hub-set-account:checked ~ .settings-panels .settings-panel--account, +#hub-set-wechat:checked ~ .settings-panels .settings-panel--wechat, +#hub-set-backup:checked ~ .settings-panels .settings-panel--backup { + display: block; +} .settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); @@ -604,10 +666,47 @@ button, .btn { box-shadow: var(--shadow), inset 0 1px 0 rgba(255, 255, 255, 0.04); } .settings-card-head { - padding: 18px 20px 14px; + padding: 20px 22px 16px; border-bottom: 1px solid rgba(56, 189, 248, 0.12); background: linear-gradient(180deg, rgba(34, 211, 238, 0.06), transparent); } +.settings-backup-body { + padding: 18px 22px 22px; + display: flex; + flex-direction: column; + gap: 20px; +} +.backup-block { + padding: 16px 16px 12px; + border: 1px solid rgba(56, 189, 248, 0.14); + border-radius: 12px; + background: rgba(5, 12, 22, 0.4); +} +.backup-block-head h3 { + margin: 0 0 6px; + font-size: 1rem; + font-weight: 600; +} +.settings-panel--account .settings-form, +.settings-panel--wechat .settings-form { + max-width: 560px; +} +.backup-block-head .field-hint { + margin: 0 0 12px; +} +.settings-field--inline { + margin: 0; + min-width: 12rem; +} +.settings-field--inline select { + margin-top: 6px; + min-width: 12rem; + padding: 9px 10px; + border-radius: 10px; + border: 1px solid var(--line); + background: rgba(5, 10, 18, 0.85); + color: var(--text); +} .settings-card-head h2 { margin: 8px 0 6px; font-family: var(--font-display); @@ -777,10 +876,6 @@ button, .btn { gap: 10px; margin-top: 2px; } -.backup-block-head h3 { - margin: 0 0 4px; - font-size: 1rem; -} .backup-table { width: 100%; border-collapse: collapse; diff --git a/templates/base.html b/templates/base.html index 32bec2f..343b1ff 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/settings.html b/templates/settings.html index e6463f2..b42fcb8 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -1,129 +1,147 @@ {% extends "base.html" %} {% block title %}系统设置 · 期权中控{% endblock %} {% block body %} +{% set _tab = (request.args.get('tab') or 'account').strip() %} +{% if _tab not in ('account', 'wechat', 'backup') %}{% set _tab = 'account' %}{% endif %}

Hub Settings

系统设置

-

账号与中控告警在此维护,保存后立即生效。

+

账号、告警与备份在此维护;保存后立即生效。

-
-
-
- Account -

登录账号

-

修改后立即写入 .env,无需重启。

-
-
- -
- 当前用户名 -
{{ current_username }}
-
- -
- -
- - -
- -
-
+
+ + + -
-
- Notify -

企业微信告警

-

中控专用:客户端离线 / 故障 / 恢复时推送,与交易机通知分开。

-
-
- -
- - -
-

连续拉取失败达到阈值后才告警,默认 2 次;同一机器在冷却期内不重复推送。

- -
- - -
-
-
+
+ + + +
-
-
- Backup -

备份恢复

-

每天北京时间 03:00 自动备份,保留最近 10 天。中控可下载本机与各客户端备份;客户端仅能下载自己的。

-
- -
-
-

中控备份

-

目录 /root/crypto_okx_hub_backups · 含 .env 与 hub.db

-
- - - +
+
-
- - - -
文件时间大小操作
加载中…
-
-
- -
-
-

客户端备份

-

经 Bridge 拉取各机备份列表,可下载到本地

-
-
+ +
+
+ Notify +

企业微信告警

+

中控专用:客户端离线 / 故障 / 恢复时推送,与交易机通知分开。

+
+
+ + +
+ + +
+

连续拉取失败达到阈值后才告警,默认 2 次;同一机器在冷却期内不重复推送。

+ +
+ + +
+
+
+ +
+
+ Backup +

备份恢复

+

每天北京时间 03:00 自动备份,保留最近 10 天。中控可下载本机与各客户端备份;客户端仅能下载自己的。

+
+ +
+
+
+

中控备份

+

目录 /root/crypto_okx_hub_backups · 含 .env 与 hub.db

+
+ + + +
+
+
+ + + +
文件时间大小操作
加载中…
+
+
+ +
+
+

客户端备份

+

经 Bridge 拉取各机备份列表,可下载到本地

+
+ + + +
+
+
+ + + +
文件时间大小操作
请选择客户端
+
-
- - - -
文件时间大小操作
请选择客户端
-
-
- + +
{% endblock %} {% block scripts %} - + {% endblock %}