接入六个新页面,外壳加内核状态心跳
导航新增「规则」项;设置、配置、规则页接 saved 事件重读配置,仪表盘只 读传 data。心跳每 10 秒探一次内核在不在:kernel.online 原本只在 load() 里更新,用户停在任何页面期间它都是静态快照,内核重启一次,请求页和 仪表盘会锁死在「内核未连接」,各页等它回来的 watch 永远不可达。恢复 在线的那一次顺带拉新运行时数据。
This commit is contained in:
+49
-3
@@ -1,11 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { ApiError, api, clearToken, getToken, setToken } from './api'
|
import { ApiError, api, clearToken, getToken, setToken } from './api'
|
||||||
import type { ConfigSnapshot } from './api'
|
import type { ConfigSnapshot } from './api'
|
||||||
import { NAV } from './nav'
|
import { NAV } from './nav'
|
||||||
import { density, toggleDensity } from './prefs'
|
import { density, toggleDensity } from './prefs'
|
||||||
import { kernel, reloadRuntime } from './runtime'
|
import { kernel, refreshKernel, reloadRuntime } from './runtime'
|
||||||
|
import ConfigPage from './views/ConfigPage.vue'
|
||||||
|
import ConnectionsPage from './views/ConnectionsPage.vue'
|
||||||
|
import DashboardPage from './views/DashboardPage.vue'
|
||||||
|
import RequestsPage from './views/RequestsPage.vue'
|
||||||
import RouterPage from './views/RouterPage.vue'
|
import RouterPage from './views/RouterPage.vue'
|
||||||
|
import RulesPage from './views/RulesPage.vue'
|
||||||
|
import SettingsPage from './views/SettingsPage.vue'
|
||||||
import TokenGate from './views/TokenGate.vue'
|
import TokenGate from './views/TokenGate.vue'
|
||||||
import IconFold from '~icons/ri/sidebar-fold-line'
|
import IconFold from '~icons/ri/sidebar-fold-line'
|
||||||
import IconUnfold from '~icons/ri/sidebar-unfold-line'
|
import IconUnfold from '~icons/ri/sidebar-unfold-line'
|
||||||
@@ -55,6 +61,24 @@ onMounted(() => {
|
|||||||
if (!needsToken.value) void load()
|
if (!needsToken.value) void load()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内核状态的心跳。kernel.online 原本只在 load() 里更新,用户停在任何页面期间它都是
|
||||||
|
* 静态快照:内核重启一次,请求页和仪表盘会锁死在「内核未连接」,各页等它回来的 watch
|
||||||
|
* 永远不会触发。10 秒探一次(/api/kernel 是个 5 秒超时的轻量探测),恢复在线的那一次
|
||||||
|
* 顺带把运行时数据拉新,路由页的组和线路也跟着回来。
|
||||||
|
*/
|
||||||
|
let kernelTimer = 0
|
||||||
|
onMounted(() => {
|
||||||
|
kernelTimer = window.setInterval(() => {
|
||||||
|
if (needsToken.value || !snapshot.value) return
|
||||||
|
const wasOnline = kernel.value.online
|
||||||
|
void refreshKernel().then(() => {
|
||||||
|
if (!wasOnline && kernel.value.online) void reloadRuntime()
|
||||||
|
})
|
||||||
|
}, 10_000)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => window.clearInterval(kernelTimer))
|
||||||
|
|
||||||
const problems = computed(
|
const problems = computed(
|
||||||
() => snapshot.value?.diagnostics.filter((d) => d.severity === 'error') ?? [],
|
() => snapshot.value?.diagnostics.filter((d) => d.severity === 'error') ?? [],
|
||||||
)
|
)
|
||||||
@@ -120,13 +144,35 @@ const current = computed(() => NAV.find((n) => n.key === active.value))
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DashboardPage v-if="active === 'dashboard'" :data="snapshot.data" />
|
||||||
|
|
||||||
<RouterPage
|
<RouterPage
|
||||||
v-if="active === 'router'"
|
v-else-if="active === 'router'"
|
||||||
:data="snapshot.data"
|
:data="snapshot.data"
|
||||||
:revision="snapshot.revision"
|
:revision="snapshot.revision"
|
||||||
@saved="load"
|
@saved="load"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ConfigPage
|
||||||
|
v-else-if="active === 'config'"
|
||||||
|
:data="snapshot.data"
|
||||||
|
:revision="snapshot.revision"
|
||||||
|
@saved="load"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RulesPage
|
||||||
|
v-else-if="active === 'rules'"
|
||||||
|
:data="snapshot.data"
|
||||||
|
:revision="snapshot.revision"
|
||||||
|
@saved="load"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RequestsPage v-else-if="active === 'requests'" />
|
||||||
|
|
||||||
|
<ConnectionsPage v-else-if="active === 'connections'" />
|
||||||
|
|
||||||
|
<SettingsPage v-else-if="active === 'settings'" @saved="load" />
|
||||||
|
|
||||||
<div v-else class="note">
|
<div v-else class="note">
|
||||||
<component :is="current?.icon" class="note-icon" />
|
<component :is="current?.icon" class="note-icon" />
|
||||||
<p>{{ current?.label }} 尚未实现</p>
|
<p>{{ current?.label }} 尚未实现</p>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import IconRoute from '~icons/ri/route-line'
|
import IconRoute from '~icons/ri/route-line'
|
||||||
import IconDashboard from '~icons/ri/dashboard-line'
|
import IconDashboard from '~icons/ri/dashboard-line'
|
||||||
import IconConfig from '~icons/ri/file-list-3-line'
|
import IconConfig from '~icons/ri/file-list-3-line'
|
||||||
|
import IconRules from '~icons/ri/filter-3-line'
|
||||||
import IconRequests from '~icons/ri/pulse-line'
|
import IconRequests from '~icons/ri/pulse-line'
|
||||||
import IconConnections from '~icons/ri/links-line'
|
import IconConnections from '~icons/ri/links-line'
|
||||||
import IconSettings from '~icons/ri/settings-3-line'
|
import IconSettings from '~icons/ri/settings-3-line'
|
||||||
@@ -21,6 +22,7 @@ export const NAV: NavItem[] = [
|
|||||||
{ key: 'dashboard', label: '仪表盘', icon: IconDashboard },
|
{ key: 'dashboard', label: '仪表盘', icon: IconDashboard },
|
||||||
{ key: 'router', label: '路由', icon: IconRoute },
|
{ key: 'router', label: '路由', icon: IconRoute },
|
||||||
{ key: 'config', label: '配置', icon: IconConfig },
|
{ key: 'config', label: '配置', icon: IconConfig },
|
||||||
|
{ key: 'rules', label: '规则', icon: IconRules },
|
||||||
{ key: 'requests', label: '请求', icon: IconRequests },
|
{ key: 'requests', label: '请求', icon: IconRequests },
|
||||||
{ key: 'connections', label: '连接', icon: IconConnections },
|
{ key: 'connections', label: '连接', icon: IconConnections },
|
||||||
{ key: 'settings', label: '设置', icon: IconSettings },
|
{ key: 'settings', label: '设置', icon: IconSettings },
|
||||||
|
|||||||
+6
-1
@@ -32,6 +32,11 @@ export const lineProvider = ref<Record<string, string>>({})
|
|||||||
|
|
||||||
export const kernel = ref<{ online: boolean; version?: string }>({ online: false })
|
export const kernel = ref<{ online: boolean; version?: string }>({ online: false })
|
||||||
|
|
||||||
|
/** 只探测内核在不在线,不拉运行时数据。给外壳的定时心跳用,各页等内核回来的 watch 靠它可达。 */
|
||||||
|
export async function refreshKernel(): Promise<void> {
|
||||||
|
kernel.value = await api.kernel().catch(() => ({ online: false }))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新读一遍运行时状态。
|
* 重新读一遍运行时状态。
|
||||||
*
|
*
|
||||||
@@ -42,7 +47,7 @@ export const kernel = ref<{ online: boolean; version?: string }>({ online: false
|
|||||||
* 最需要这个界面的时候。
|
* 最需要这个界面的时候。
|
||||||
*/
|
*/
|
||||||
export async function reloadRuntime(): Promise<void> {
|
export async function reloadRuntime(): Promise<void> {
|
||||||
kernel.value = await api.kernel().catch(() => ({ online: false }))
|
await refreshKernel()
|
||||||
if (!kernel.value.online) {
|
if (!kernel.value.online) {
|
||||||
runtime.value = {}
|
runtime.value = {}
|
||||||
providers.value = {}
|
providers.value = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user