mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-23 02:17:56 +00:00
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:
95
web/src/App.vue
Normal file
95
web/src/App.vue
Normal 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>
|
||||
Reference in New Issue
Block a user