修复中控自更新触发与错误提示;实例页改为表格,带鱼屏宽度锁 1800。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-04 07:44:12 +08:00
parent 2e460efb73
commit 5b4e35c582
8 changed files with 303 additions and 190 deletions
+2 -2
View File
@@ -4,9 +4,9 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}期权中控{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=12">
<link rel="stylesheet" href="{{ url_for('static', filename='hub.css') }}?v=13">
</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 == '/' %}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') %}
<header class="topbar">
<nav>
+85 -70
View File
@@ -65,87 +65,102 @@
<div class="settings-card-head inst-list-head">
<span class="settings-badge">Registry</span>
<h2>已登记 · {{ instances|length }}</h2>
<p class="muted">点击卡片展开编辑;Token 默认隐藏。</p>
<p class="muted">表格查看;点「编辑」展开修改。Token 默认隐藏。</p>
</div>
<div class="inst-card-list">
<div class="inst-table-wrap">
{% if not instances %}
<p class="muted inst-empty">暂无实例。先在客户端配置 <code>HUB_BRIDGE_TOKEN</code> 并保证公网可访问,再点右上角「添加实例」。</p>
{% endif %}
{% for inst in instances %}
<details class="inst-card" data-id="{{ inst.id }}" data-name="{{ inst.name }}">
<summary class="inst-card-summary">
<div class="inst-card-main">
<div class="inst-card-title-row">
<strong class="inst-card-name">{{ inst.name }}</strong>
{% else %}
<table class="inst-table">
<thead>
<tr>
<th>名称</th>
<th>URL</th>
<th>状态</th>
<th>版本</th>
<th>标签</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for inst in instances %}
<tr class="inst-row" data-id="{{ inst.id }}" data-name="{{ inst.name }}">
<td class="inst-td-name"><strong>{{ inst.name }}</strong></td>
<td class="inst-td-url muted" title="{{ inst.base_url }}">{{ inst.base_url }}</td>
<td>
{% if inst.enabled %}
<span class="inst-pill inst-pill--on">启用</span>
{% else %}
<span class="inst-pill inst-pill--off">停用</span>
{% endif %}
{% if inst.tags %}
<span class="inst-pill inst-pill--tag">{{ inst.tags }}</span>
{% endif %}
</div>
<div class="inst-card-url muted">{{ inst.base_url }}</div>
</div>
<div class="inst-card-aside">
<span class="update-version muted" data-inst-version title="点击卡片展开后可刷新">—</span>
<span class="inst-card-chevron" aria-hidden="true"></span>
</div>
</summary>
<div class="inst-card-body">
<form method="post" class="settings-form inst-form" autocomplete="off">
<input type="hidden" name="action" value="update">
<input type="hidden" name="id" value="{{ inst.id }}">
<div class="settings-row-2">
<label class="settings-field">名称
<input name="name" value="{{ inst.name }}" required autocomplete="off">
</label>
<label class="settings-field">标签
<input name="tags" value="{{ inst.tags or '' }}" autocomplete="off">
</label>
</div>
<label class="settings-field">公网 URL
<input name="base_url" value="{{ inst.base_url }}" required autocomplete="off">
</label>
<div class="settings-row-2">
<label class="settings-field">Bridge Token
<span class="inst-token-wrap">
<input type="password" name="bridge_token" value="{{ inst.bridge_token }}" required autocomplete="new-password" class="inst-token-input">
<button type="button" class="btn btn-sm btn-secondary inst-token-toggle" aria-label="显示 Token">显示</button>
</span>
</label>
<label class="settings-field">Embed 路径
<input name="embed_path" value="{{ inst.embed_path }}" autocomplete="off">
</label>
</div>
<label class="settings-switch">
<input type="checkbox" name="enabled" value="1" {% if inst.enabled %}checked{% endif %}>
<span class="settings-switch-ui" aria-hidden="true"></span>
<span class="settings-switch-text">
<strong>启用拉取</strong>
<small>关闭后总览不再巡检此机</small>
</span>
</label>
<div class="inst-card-actions">
<button type="submit" class="settings-primary">保存</button>
<a class="btn" href="/desk?id={{ inst.id }}">内嵌打开</a>
<a class="btn btn-secondary" href="/settings?tab=update">系统更新</a>
<button type="button" class="btn btn-secondary btn-sm" data-inst-refresh-ver>刷新版本</button>
</div>
</form>
<form method="post" class="inst-delete-form" onsubmit="return confirm('确认删除「{{ inst.name }}」?')">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="{{ inst.id }}">
<button type="submit" class="btn-danger btn-sm">删除实例</button>
</form>
</div>
</details>
{% endfor %}
</td>
<td><span class="update-version muted" data-inst-version>—</span></td>
<td class="inst-td-tags muted">{{ inst.tags or '—' }}</td>
<td class="inst-td-ops">
<label class="btn btn-sm" for="inst-edit-{{ inst.id }}">编辑</label>
<a class="btn btn-sm" href="/desk?id={{ inst.id }}">内嵌</a>
<button type="button" class="btn btn-sm btn-secondary" data-inst-refresh-ver>版本</button>
<form method="post" class="inst-inline-del" onsubmit="return confirm('确认删除「{{ inst.name }}」?')">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="{{ inst.id }}">
<button type="submit" class="btn-danger btn-sm">删除</button>
</form>
</td>
</tr>
<tr class="inst-edit-row">
<td colspan="6">
<input type="checkbox" id="inst-edit-{{ inst.id }}" class="inst-edit-check">
<div class="inst-edit-panel">
<form method="post" class="settings-form inst-form" autocomplete="off">
<input type="hidden" name="action" value="update">
<input type="hidden" name="id" value="{{ inst.id }}">
<div class="settings-row-2">
<label class="settings-field">名称
<input name="name" value="{{ inst.name }}" required autocomplete="off">
</label>
<label class="settings-field">标签
<input name="tags" value="{{ inst.tags or '' }}" autocomplete="off">
</label>
</div>
<label class="settings-field">公网 URL
<input name="base_url" value="{{ inst.base_url }}" required autocomplete="off">
</label>
<div class="settings-row-2">
<label class="settings-field">Bridge Token
<span class="inst-token-wrap">
<input type="password" name="bridge_token" value="{{ inst.bridge_token }}" required autocomplete="new-password" class="inst-token-input">
<button type="button" class="btn btn-sm btn-secondary inst-token-toggle" aria-label="显示 Token">显示</button>
</span>
</label>
<label class="settings-field">Embed 路径
<input name="embed_path" value="{{ inst.embed_path }}" autocomplete="off">
</label>
</div>
<label class="settings-switch">
<input type="checkbox" name="enabled" value="1" {% if inst.enabled %}checked{% endif %}>
<span class="settings-switch-ui" aria-hidden="true"></span>
<span class="settings-switch-text">
<strong>启用拉取</strong>
<small>关闭后总览不再巡检此机</small>
</span>
</label>
<div class="inst-card-actions">
<button type="submit" class="settings-primary">保存</button>
<label for="inst-edit-{{ inst.id }}" class="btn btn-secondary">收起</label>
<a class="btn btn-secondary" href="/settings?tab=update">系统更新</a>
</div>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</section>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='instances_page.js') }}?v=1"></script>
<script src="{{ url_for('static', filename='instances_page.js') }}?v=2"></script>
{% endblock %}
+2 -2
View File
@@ -145,7 +145,7 @@
<div class="settings-card-head">
<span class="settings-badge">Update</span>
<h2>系统更新</h2>
<p class="muted">快速更新 = git pull + pm2 restart。中控本机更新;客户端经 Bridge 远程下发。有持仓时慎用。</p>
<p class="muted">快速更新 = git pull + pm2 restart。中控本机更新;客户端经 Bridge 远程下发。有持仓时慎用。中控更新日志:<code>logs/hub_update.log</code></p>
</div>
<div class="settings-backup-body">
@@ -183,7 +183,7 @@
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='settings_backup.js') }}?v=2"></script>
<script src="{{ url_for('static', filename='settings_update.js') }}?v=1"></script>
<script src="{{ url_for('static', filename='settings_update.js') }}?v=2"></script>
<script>
HubSettingsBackup.boot();
HubSettingsUpdate.boot();