webui4frpc: 独立可用的可视化 frpc 控制器 (M0)

- 零 frp 源码依赖,单二进制 (Go + Vue3 + VueFlow + Element Plus)
- 画布多对多连线,渲染 tcp/udp/http/https frpc 配置
- worker 进程管理:自愈、日志轮转、崩溃退避重启
- frpc 一键安装 (GitHub Releases) + 手动指定路径
- 三页 UI:状态(默认)/连接配置/设置
- 状态页实时节点/转发状态,节点可增删改启停
- 画布冲突检查:端口/域名冲突标红 + 弹窗拦截保存
- backend 单测覆盖 store/render/process/httpapi/install
- plan.md + FRPC_FEATURES_AUDIT.md 文档
This commit is contained in:
2026-08-17 11:00:26 +08:00
commit c1936887a2
40 changed files with 7868 additions and 0 deletions

95
web/src/App.vue Normal file
View File

@ -0,0 +1,95 @@
<!-- 简单 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>
</nav>
</header>
<main class="content">
<CanvasView v-if="view === 'canvas'" />
<SettingsView v-else-if="view === 'settings'" />
<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'
const view = ref<'canvas' | 'settings' | 'status'>('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>