Files
webui4frpc/web/src/App.vue
jianf bd3a62a017 fix: worker 进程并发 double-close panic + 状态页连接语义重构 + 集群页骨架
- process: spawn 不再重复起 supervise goroutine(Restart 触发旧 supervise close doneCh 后再 close → panic);新增 closeOnce/supervising 守卫;Start 首次起 supervise,重启循环在同一 goroutine 内

- httpapi: status 增加 connState(connected/connecting/failed/not_started/disabled),由 worker 日志探测真实 frps 连接(不再把本地 worker 状态误当远程 frps 状态)

- StatusView: 远程卡片主状态改为连接状态徽标;启停按钮标注为控制本地 frpc worker;本地转发区保留 worker 状态显示

- 新增 ClusterView 集群页骨架(M3 LB+健康检查载体)+ App.vue 导航接入

- render: bandwidthLimit 从 proxy 顶层移入 transport 段(frpc 0.61 正确 schema,修复真实连不上的 bug)
2026-08-17 15:13:25 +08:00

101 lines
2.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 简单 SPA 外壳顶部切换 状态 / 连接配置 / 设置 -->
<template>
<div class="app-shell">
<header class="topbar">
<span class="brand">webui4frpc</span>
<nav class="nav">
<button class="nav-btn" :class="{ active: view === 'status' }" @click="view = 'status'">
状态
</button>
<button class="nav-btn" :class="{ active: view === 'canvas' }" @click="view = 'canvas'">
连接配置
</button>
<button class="nav-btn" :class="{ active: view === 'settings' }" @click="view = 'settings'">
设置
</button>
<button class="nav-btn" :class="{ active: view === 'cluster' }" @click="view = 'cluster'">
集群
</button>
</nav>
</header>
<main class="content">
<CanvasView v-if="view === 'canvas'" />
<SettingsView v-else-if="view === 'settings'" />
<ClusterView v-else-if="view === 'cluster'" />
<StatusView v-else />
</main>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import CanvasView from './views/CanvasView.vue'
import SettingsView from './views/SettingsView.vue'
import StatusView from './views/StatusView.vue'
import ClusterView from './views/ClusterView.vue'
const view = ref<'canvas' | 'settings' | 'status' | 'cluster'>('status')
</script>
<style>
html,
body,
#app {
margin: 0;
height: 100%;
}
.app-shell {
height: 100%;
display: flex;
flex-direction: column;
font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
background: #f7f8fa;
}
.topbar {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 24px;
padding: 0 20px;
height: 52px;
background: #fff;
border-bottom: 1px solid #e4e7ed;
}
.brand {
font-weight: 600;
font-size: 16px;
}
.nav {
display: flex;
gap: 8px;
}
.nav-btn {
border: none;
background: transparent;
padding: 6px 16px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
color: #606266;
&:hover {
background: #f2f3f5;
}
&.active {
background: #303133;
color: #fff;
}
}
.content {
flex: 1;
min-height: 0;
}
</style>