仪表盘重做:ECharts 曲线、常驻遥测、路由行直达详情

采集从仪表盘组件挪进模块级单例(web/telemetry.ts),流量曲线和每路
吞吐的历史跨页面留存——跟着组件走的话切一次页面就清零一次。每路吞吐
从 /connections 快照按连接逐条求差再按 inboundName 聚合:先求和再求差
会在连接关闭时算出负数。

图表换 ECharts(按需注册):手写 SVG 折线做不出平滑曲线和时间轴,
颜色在运行时从 CSS 变量取,跟随主题切换重取。

路由列表每行带走势、速率和可选的公网 IP 检测,点击跳路由页并选中
那一路(外壳传 focus 给 RouterPage 消费)。
This commit is contained in:
2026-08-11 22:26:36 +08:00
parent ce181e9ae5
commit eec5b791c8
15 changed files with 1385 additions and 376 deletions
+23 -1
View File
@@ -5,6 +5,7 @@ import type { ConfigSnapshot } from './api'
import { NAV } from './nav'
import { density, toggleDensity } from './prefs'
import { kernel, refreshKernel, reloadRuntime } from './runtime'
import { startTelemetry } from './telemetry'
import ConfigPage from './views/ConfigPage.vue'
import ConnectionsPage from './views/ConnectionsPage.vue'
import DashboardPage from './views/DashboardPage.vue'
@@ -23,6 +24,17 @@ const booted = ref(false)
const refreshing = ref(false)
const active = ref('dashboard')
/**
* 仪表盘的路由行点过去时要选中的那一路。放外壳是因为跳转跨页面:
* 仪表盘发起、路由页消费,消费完清掉,下次再点同一行还能再跳。
*/
const routerFocus = ref<string | null>(null)
function openInbound(id: string) {
routerFocus.value = id
active.value = 'router'
}
/**
* 重新读一遍状态。
*
@@ -59,6 +71,9 @@ function submitToken(value: string) {
onMounted(() => {
if (!needsToken.value) void load()
// 流量和每路吞吐的历史跨页面留存,所以采集挂在外壳上而不是仪表盘里。
// 单例自己跟着 kernel.online 起落,这里只负责点火
startTelemetry()
})
/**
@@ -144,12 +159,19 @@ const current = computed(() => NAV.find((n) => n.key === active.value))
</ul>
</div>
<DashboardPage v-if="active === 'dashboard'" :data="snapshot.data" />
<DashboardPage
v-if="active === 'dashboard'"
:data="snapshot.data"
@open-inbound="openInbound"
@open-router="active = 'router'"
/>
<RouterPage
v-else-if="active === 'router'"
:data="snapshot.data"
:revision="snapshot.revision"
:focus="routerFocus"
@focus-done="routerFocus = null"
@saved="load"
/>