机架管理前后端(rack/):FastAPI + SQLite + Vue3,经统一门户上线
- 后端 prrack:MQTT(phonerail-backend @ 127.0.0.1:1884)↔ 遥测入库 / 事件增量同步 / WebSocket 推送; 命令白名单 + 重传同 seq;审计记门户 X-Auth-User(只绑 127.0.0.1,main.py 拒绝非回环)。 - 自动恢复:只看电流(平线 / 过低,服务器够不到手机 adb),先长按电源再断电重启, 每小时限次后停手;默认逐台关闭。adb 探活接口 recovery.Prober 预留。 - 前端:总览 / 板子页(五路操作、电流电压曲线)/ 事件 / 操作记录 / 自动恢复;手机宽度可用。 - 部署 deploy/deploy.sh:服务器 Python 3.8 上先跑测试再替换;systemd phonerail-rack,每日备份。 - 门户应用 id 16、key rack(phonerail 已被需求文档站占用),授权 admin + staff。 测试 26 例(本机 3.14 / 服务器 3.8 均过)。线上板 b81f3fc9c8bc 在线,WS 经门户 101 + 1 条/s 遥测。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="shell">
|
||||
<header class="top">
|
||||
<router-link to="/" class="brand">PhoneRail 机架</router-link>
|
||||
<nav>
|
||||
<router-link to="/">总览</router-link>
|
||||
<router-link to="/events">事件</router-link>
|
||||
<router-link to="/audit">操作记录</router-link>
|
||||
<router-link to="/recovery">自动恢复</router-link>
|
||||
</nav>
|
||||
<div class="conn">
|
||||
<span :class="['dot', state.ws ? 'up' : 'down']" :title="state.ws ? '已连上服务器' : '与服务器断开,重连中'" />
|
||||
<span v-if="!state.ws" class="warn">断开</span>
|
||||
<span v-else-if="!state.mqtt" class="warn" title="后端连不上 MQTT broker:所有板子都会显示通信中断">broker 断开</span>
|
||||
<span class="user">{{ state.user }}</span>
|
||||
</div>
|
||||
</header>
|
||||
<main><router-view /></main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { state } from './store'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root { --bg: #f1f5f9; --card: #fff; --line: #e2e8f0; --muted: #64748b; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; background: var(--bg); font-family: -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif; color: #0f172a; }
|
||||
.shell { min-height: 100vh; }
|
||||
.top { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; gap: 16px;
|
||||
padding: 0 16px; height: 52px; background: #0f172a; color: #fff; }
|
||||
.top a { color: #cbd5e1; text-decoration: none; }
|
||||
.top .brand { color: #fff; font-weight: 600; white-space: nowrap; }
|
||||
.top nav { display: flex; gap: 14px; overflow-x: auto; flex: 1; min-width: 0; }
|
||||
.top nav a { white-space: nowrap; padding: 4px 0; }
|
||||
.top nav a.router-link-exact-active { color: #fff; border-bottom: 2px solid #38bdf8; }
|
||||
.conn { display: flex; align-items: center; gap: 8px; font-size: 13px; white-space: nowrap; }
|
||||
.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
|
||||
.dot.up { background: #22c55e; } .dot.down { background: #ef4444; }
|
||||
.conn .warn { color: #fca5a5; }
|
||||
.conn .user { color: #94a3b8; }
|
||||
main { padding: 16px; max-width: 1400px; margin: 0 auto; }
|
||||
.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 14px; }
|
||||
.muted { color: var(--muted); font-size: 13px; }
|
||||
@media (max-width: 640px) {
|
||||
.top { gap: 10px; padding: 0 10px; }
|
||||
.top .brand { display: none; }
|
||||
.conn .user { display: none; }
|
||||
main { padding: 10px; }
|
||||
}
|
||||
</style>
|
||||
在新工单中引用
屏蔽一个用户