mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-07 10:16:38 +08:00
修复ui 添加gost静默http服务
This commit is contained in:
@@ -41,6 +41,7 @@ html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
overflow-y: hidden;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ export const getNodeInstallCommand = (id) => Network.post("/node/install", { id
|
||||
export const createTunnel = (data) => Network.post("/tunnel/create", data)
|
||||
export const getTunnelList = () => Network.post("/tunnel/list")
|
||||
export const getTunnelById = (id) => Network.post("/tunnel/get", { id })
|
||||
export const updateTunnel = (data) => Network.post("/tunnel/update", data)
|
||||
export const deleteTunnel = (id) => Network.post("/tunnel/delete", { id })
|
||||
|
||||
// 用户隧道权限管理操作 - 全部使用POST请求
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
|
||||
<div class="form-footer">
|
||||
<p class="warning-text">注意:修改账号密码后需要重新登录</p>
|
||||
<p class="copyright">© 2024 管理系统. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -720,8 +720,6 @@ export default {
|
||||
<style scoped>
|
||||
.forward-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 移动端优化 */
|
||||
|
||||
@@ -99,12 +99,8 @@
|
||||
</el-menu>
|
||||
</el-aside>
|
||||
<el-main :class="['main-content', { 'mobile-main': isMobile }]">
|
||||
<div :style="mainContentStyle">
|
||||
<div class="scroll-container">
|
||||
<router-view/>
|
||||
|
||||
<div :class="['footer', { 'mobile-footer': isMobile }]">
|
||||
© 2023 Copyright.
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
@@ -358,8 +354,16 @@ export default {
|
||||
|
||||
.main-content {
|
||||
padding: 0;
|
||||
height: calc(100vh - 100px);
|
||||
transition: margin-left 0.3s ease;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
background-color: #f5f5f5;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.mobile-main {
|
||||
@@ -367,13 +371,13 @@ export default {
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: white;
|
||||
height: 50px;
|
||||
background-color: white;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 199px;
|
||||
right: 0;
|
||||
line-height: 50px;
|
||||
line-height: 50px;
|
||||
padding-left: 30px;
|
||||
font-size: 12px;
|
||||
color: #58666e;
|
||||
|
||||
@@ -255,6 +255,9 @@ export default {
|
||||
mounted() {
|
||||
this.loadPackageData();
|
||||
},
|
||||
created() {
|
||||
localStorage.setItem('e','/index')
|
||||
},
|
||||
methods: {
|
||||
async loadPackageData() {
|
||||
this.loading = true;
|
||||
@@ -500,8 +503,6 @@ export default {
|
||||
<style scoped>
|
||||
.package-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
|
||||
@@ -440,8 +440,6 @@ export default {
|
||||
<style scoped>
|
||||
.speed-limit-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
|
||||
@@ -63,10 +63,7 @@
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="form-footer">
|
||||
<p class="copyright">© 2024 管理系统. All rights reserved.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+386
-125
@@ -5,12 +5,13 @@
|
||||
icon="el-icon-plus"
|
||||
@click="handleAdd"
|
||||
class="add-btn"
|
||||
size="medium"
|
||||
>
|
||||
新增隧道
|
||||
</el-button>
|
||||
|
||||
<!-- 隧道卡片展示 -->
|
||||
<div class="cards-container" v-loading="loading" style="margin-top: 10px;">
|
||||
<div class="cards-container" v-loading="loading" style="margin-top: 20px;">
|
||||
<div class="cards-grid">
|
||||
<div
|
||||
v-for="tunnel in tunnelList"
|
||||
@@ -22,29 +23,41 @@
|
||||
>
|
||||
<!-- 隧道头部 -->
|
||||
<div class="tunnel-header">
|
||||
<h3 class="tunnel-name">{{ tunnel.name }}</h3>
|
||||
<div class="tunnel-badges">
|
||||
<el-tag v-if="tunnel.type === 1" type="primary" size="mini">端口转发</el-tag>
|
||||
<el-tag v-else type="warning" size="mini">隧道转发</el-tag>
|
||||
<el-tag v-if="tunnel.type === 2 && tunnel.protocol"
|
||||
:type="tunnel.protocol === 'tls' ? 'success' : tunnel.protocol === 'mtls' ? 'danger' : 'info'"
|
||||
size="mini">
|
||||
{{ tunnel.protocol.toUpperCase() }}
|
||||
</el-tag>
|
||||
<el-tag v-if="tunnel.flow === 1" type="success" size="mini">单向计算</el-tag>
|
||||
<el-tag v-else type="info" size="mini">双向计算</el-tag>
|
||||
<div class="tunnel-info">
|
||||
<h3 class="tunnel-name">{{ tunnel.name }}</h3>
|
||||
<div class="tunnel-badges">
|
||||
<el-tag v-if="tunnel.type === 1" type="primary" size="mini">端口转发</el-tag>
|
||||
<el-tag v-else type="warning" size="mini">隧道转发</el-tag>
|
||||
<el-tag v-if="tunnel.type === 2 && tunnel.protocol"
|
||||
:type="tunnel.protocol === 'tls' ? 'success' : tunnel.protocol === 'mtls' ? 'danger' : 'info'"
|
||||
size="mini">
|
||||
{{ tunnel.protocol.toUpperCase() }}
|
||||
</el-tag>
|
||||
<el-tag v-if="tunnel.flow === 1" type="success" size="mini">单向计算</el-tag>
|
||||
<el-tag v-else type="info" size="mini">双向计算</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<el-button
|
||||
style="margin-left: 10px;"
|
||||
size="small"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(tunnel)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<div class="tunnel-actions">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-edit"
|
||||
@click="handleEdit(tunnel)"
|
||||
class="action-btn edit-btn"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
@click="handleDelete(tunnel)"
|
||||
class="action-btn delete-btn"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 流程图 -->
|
||||
@@ -171,17 +184,28 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="隧道类型" prop="type">
|
||||
<el-radio-group v-model="tunnelForm.type" @change="handleTypeChange">
|
||||
<el-radio :label="1">端口转发</el-radio>
|
||||
<el-radio :label="2">隧道转发</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
v-model="tunnelForm.type"
|
||||
placeholder="请选择隧道类型"
|
||||
style="width: 100%"
|
||||
@change="handleTypeChange"
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<el-option :value="1" label="端口转发"></el-option>
|
||||
<el-option :value="2" label="隧道转发"></el-option>
|
||||
</el-select>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="流量计算" prop="flow">
|
||||
<el-radio-group v-model="tunnelForm.flow">
|
||||
<el-radio :label="1">单向计算(仅上传)</el-radio>
|
||||
<el-radio :label="2">双向计算(上传+下载)</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
v-model="tunnelForm.flow"
|
||||
placeholder="请选择流量计算方式"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option :value="1" label="单向计算(仅上传)"></el-option>
|
||||
<el-option :value="2" label="双向计算(上传+下载)"></el-option>
|
||||
</el-select>
|
||||
<div class="form-hint">
|
||||
单向:仅计算上传流量;双向:计算上传和下载的总流量
|
||||
</div>
|
||||
@@ -195,6 +219,7 @@
|
||||
placeholder="请选择入口节点"
|
||||
style="width: 100%"
|
||||
@change="handleNodeChange"
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<el-option
|
||||
v-for="node in onlineNodes"
|
||||
@@ -210,45 +235,44 @@
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="起始端口" prop="inPortSta">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.inPortSta"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="起始端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束端口" prop="inPortEnd">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.inPortEnd"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="结束端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="起始端口" prop="inPortSta">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.inPortSta"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="起始端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束端口" prop="inPortEnd">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.inPortEnd"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="结束端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 只有隧道转发(type=2)时才显示出口配置 -->
|
||||
<template v-if="tunnelForm.type === 2">
|
||||
<el-divider content-position="left">出口配置</el-divider>
|
||||
|
||||
<el-form-item label="协议类型" prop="protocol">
|
||||
<el-radio-group v-model="tunnelForm.protocol">
|
||||
<el-radio label="tls">TLS</el-radio>
|
||||
<el-radio label="tcp">TCP</el-radio>
|
||||
<el-radio label="mtls">mTLS</el-radio>
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
v-model="tunnelForm.protocol"
|
||||
placeholder="请选择协议类型"
|
||||
style="width: 100%"
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<el-option value="tls" label="TLS"></el-option>
|
||||
<el-option value="tcp" label="TCP"></el-option>
|
||||
<el-option value="mtls" label="mTLS"></el-option>
|
||||
</el-select>
|
||||
<div class="form-hint">
|
||||
TLS:传输层安全协议;TCP:传输控制协议;mTLS:双向TLS认证
|
||||
数据过墙推荐选择TLS协议
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
@@ -258,6 +282,7 @@
|
||||
placeholder="请选择出口节点"
|
||||
style="width: 100%"
|
||||
@change="handleNodeChange"
|
||||
:disabled="isEdit"
|
||||
>
|
||||
<el-option
|
||||
v-for="node in onlineNodes"
|
||||
@@ -274,32 +299,26 @@
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="起始端口" prop="outIpSta">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.outIpSta"
|
||||
:min="10000"
|
||||
:max="65535"
|
||||
placeholder="起始端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束端口" prop="outIpEnd">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.outIpEnd"
|
||||
:min="10000"
|
||||
:max="65535"
|
||||
placeholder="结束端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-form-item label="起始端口" prop="outIpSta">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.outIpSta"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="起始端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束端口" prop="outIpEnd">
|
||||
<el-input-number
|
||||
v-model="tunnelForm.outIpEnd"
|
||||
:min="1"
|
||||
:max="65535"
|
||||
placeholder="结束端口"
|
||||
style="width: 100%"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-alert
|
||||
@@ -313,13 +332,19 @@
|
||||
</el-form>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button
|
||||
@click="dialogVisible = false"
|
||||
class="dialog-btn cancel-btn"
|
||||
>
|
||||
取 消
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="submitLoading"
|
||||
class="dialog-btn submit-btn"
|
||||
>
|
||||
{{ submitLoading ? '创建中...' : '创 建' }}
|
||||
{{ submitLoading ? (isEdit ? '更新中...' : '创建中...') : (isEdit ? '更 新' : '创 建') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
@@ -327,7 +352,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { createTunnel, deleteTunnel, getTunnelList, getNodeList } from "@/api";
|
||||
import { createTunnel, updateTunnel, deleteTunnel, getTunnelList, getNodeList } from "@/api";
|
||||
|
||||
export default {
|
||||
name: "Tunnel",
|
||||
@@ -339,14 +364,15 @@ export default {
|
||||
dialogVisible: false,
|
||||
dialogTitle: '新增隧道',
|
||||
submitLoading: false,
|
||||
isEdit: false,
|
||||
tunnelForm: {
|
||||
id: null,
|
||||
name: '',
|
||||
inNodeId: null,
|
||||
inPortSta: 10000,
|
||||
inPortSta: 1,
|
||||
inPortEnd: 65535,
|
||||
outNodeId: null,
|
||||
outIpSta: 10000,
|
||||
outIpSta: 1,
|
||||
outIpEnd: 65535,
|
||||
type: 1,
|
||||
flow: 1, // 默认单向计算
|
||||
@@ -450,11 +476,37 @@ export default {
|
||||
// 新增隧道
|
||||
handleAdd() {
|
||||
this.dialogVisible = true;
|
||||
this.isEdit = false;
|
||||
this.dialogTitle = '新增隧道';
|
||||
this.resetForm();
|
||||
this.loadNodes(); // 打开对话框时加载节点列表
|
||||
},
|
||||
|
||||
// 编辑功能已移除 - 隧道创建后不能修改
|
||||
// 编辑隧道 - 只能修改名称、流量计费、端口范围
|
||||
handleEdit(tunnel) {
|
||||
this.isEdit = true;
|
||||
this.dialogTitle = '编辑隧道';
|
||||
this.dialogVisible = true;
|
||||
|
||||
// 填充表单数据(只允许修改特定字段)
|
||||
this.tunnelForm = {
|
||||
id: tunnel.id,
|
||||
name: tunnel.name,
|
||||
flow: tunnel.flow,
|
||||
inPortSta: tunnel.inPortSta,
|
||||
inPortEnd: tunnel.inPortEnd,
|
||||
outIpSta: tunnel.outIpSta,
|
||||
outIpEnd: tunnel.outIpEnd,
|
||||
// 以下字段不允许修改,但需要保留原值用于显示和提交
|
||||
type: tunnel.type,
|
||||
inNodeId: tunnel.inNodeId,
|
||||
outNodeId: tunnel.outNodeId,
|
||||
protocol: tunnel.protocol,
|
||||
status: tunnel.status
|
||||
};
|
||||
|
||||
this.loadNodes(); // 打开对话框时加载节点列表
|
||||
},
|
||||
|
||||
// 删除隧道
|
||||
handleDelete(tunnel) {
|
||||
@@ -527,36 +579,65 @@ export default {
|
||||
|
||||
this.submitLoading = true;
|
||||
|
||||
// 只支持创建隧道,不支持编辑
|
||||
const data = { ...this.tunnelForm };
|
||||
|
||||
createTunnel(data).then(res => {
|
||||
this.submitLoading = false;
|
||||
if (res.code === 0) {
|
||||
this.$message.success('创建成功');
|
||||
this.dialogVisible = false;
|
||||
this.loadTunnels();
|
||||
} else {
|
||||
this.$message.error(res.msg || '创建失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false;
|
||||
this.$message.error('网络错误,请重试');
|
||||
});
|
||||
if (this.isEdit) {
|
||||
// 编辑模式 - 只更新允许修改的字段
|
||||
const updateData = {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
flow: data.flow,
|
||||
inPortSta: data.inPortSta,
|
||||
inPortEnd: data.inPortEnd,
|
||||
outIpSta: data.outIpSta,
|
||||
outIpEnd: data.outIpEnd
|
||||
};
|
||||
|
||||
updateTunnel(updateData).then(res => {
|
||||
this.submitLoading = false;
|
||||
if (res.code === 0) {
|
||||
this.$message.success('更新成功');
|
||||
this.dialogVisible = false;
|
||||
this.loadTunnels();
|
||||
} else {
|
||||
this.$message.error(res.msg || '更新失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false;
|
||||
this.$message.error('网络错误,请重试');
|
||||
});
|
||||
} else {
|
||||
// 创建模式
|
||||
createTunnel(data).then(res => {
|
||||
this.submitLoading = false;
|
||||
if (res.code === 0) {
|
||||
this.$message.success('创建成功');
|
||||
this.dialogVisible = false;
|
||||
this.loadTunnels();
|
||||
} else {
|
||||
this.$message.error(res.msg || '创建失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false;
|
||||
this.$message.error('网络错误,请重试');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 重置表单
|
||||
resetForm() {
|
||||
this.isEdit = false;
|
||||
this.dialogTitle = '新增隧道';
|
||||
this.tunnelForm = {
|
||||
id: null,
|
||||
name: '',
|
||||
inNodeId: null,
|
||||
inPortSta: 10000,
|
||||
inPortSta: 1,
|
||||
inPortEnd: 65535,
|
||||
outNodeId: null,
|
||||
outIpSta: 10000,
|
||||
outIpSta: 1,
|
||||
outIpEnd: 65535,
|
||||
type: 1,
|
||||
flow: 1, // 默认单向计算
|
||||
@@ -574,8 +655,6 @@ export default {
|
||||
<style scoped>
|
||||
.tunnel-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: calc(100vh - 60px);
|
||||
}
|
||||
|
||||
/* 页面头部 */
|
||||
@@ -800,13 +879,102 @@ export default {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* 操作按钮 */
|
||||
/* 隧道信息容器 */
|
||||
.tunnel-info {
|
||||
flex: 1;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
/* 操作按钮容器 */
|
||||
.tunnel-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: flex-start;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-btn {
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
background: #409eff;
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.edit-btn:hover {
|
||||
background: #66b1ff;
|
||||
border-color: #66b1ff;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.delete-btn {
|
||||
background: #f56c6c;
|
||||
border-color: #f56c6c;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #f78989;
|
||||
border-color: #f78989;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(245, 108, 108, 0.3);
|
||||
}
|
||||
|
||||
/* 新增按钮样式 */
|
||||
.add-btn {
|
||||
border-radius: 8px;
|
||||
padding: 12px 24px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 12px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 16px rgba(64, 158, 255, 0.4);
|
||||
}
|
||||
|
||||
/* 对话框按钮样式 */
|
||||
.dialog-footer {
|
||||
padding-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding-top: 15px;
|
||||
margin-top: auto;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
min-width: 80px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #f5f7fa;
|
||||
border-color: #dcdfe6;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: #ecf5ff;
|
||||
border-color: #b3d8ff;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
|
||||
}
|
||||
|
||||
/* 表单提示 */
|
||||
@@ -817,6 +985,12 @@ export default {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* 只读字段提示样式 */
|
||||
.form-readonly-hint {
|
||||
color: #f56c6c;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
@@ -879,23 +1053,38 @@ export default {
|
||||
.tunnel-header {
|
||||
margin-bottom: 15px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tunnel-info {
|
||||
margin-right: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tunnel-name {
|
||||
font-size: 17px;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.tunnel-badges {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tunnel-actions {
|
||||
justify-content: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
max-width: 80px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 平板端流程图优化 */
|
||||
.tunnel-flow {
|
||||
padding: 18px 15px;
|
||||
@@ -976,6 +1165,59 @@ export default {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 对话框按钮移动端优化 */
|
||||
.dialog-footer {
|
||||
padding-top: 15px;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
flex: 1;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 表单输入框移动端优化 */
|
||||
.el-input-number {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.el-input-number .el-input__inner {
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 对话框移动端优化 */
|
||||
.el-dialog {
|
||||
width: 95% !important;
|
||||
margin: 5vh auto !important;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.el-input, .el-select {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tunnel-card {
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
@@ -1086,14 +1328,22 @@ export default {
|
||||
display: none; /* 垂直布局时隐藏箭头 */
|
||||
}
|
||||
|
||||
/* 删除按钮优化 */
|
||||
.tunnel-header .el-button {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 0;
|
||||
/* 手机端按钮优化 */
|
||||
.tunnel-actions {
|
||||
margin-top: 8px;
|
||||
align-self: flex-start;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
max-width: 70px;
|
||||
padding: 6px 10px;
|
||||
font-size: 11px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.action-btn i {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.tunnel-meta {
|
||||
@@ -1136,6 +1386,17 @@ export default {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
/* 超小屏幕对话框优化 */
|
||||
.dialog-footer {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.dialog-btn {
|
||||
width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.tunnel-card {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<!-- 搜索框 -->
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索用户名或姓名"
|
||||
placeholder="搜索用户名"
|
||||
prefix-icon="el-icon-search"
|
||||
@keyup.enter.native="handleSearch"
|
||||
clearable
|
||||
@@ -39,9 +39,8 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="name" label="姓名" min-width="100"></el-table-column>
|
||||
<el-table-column prop="user" label="用户名" min-width="120"></el-table-column>
|
||||
<el-table-column label="流量统计" width="200">
|
||||
<el-table-column prop="user" label="用户名" ></el-table-column>
|
||||
<el-table-column label="流量统计" >
|
||||
<template slot-scope="scope">
|
||||
<div class="flow-stats">
|
||||
<div class="flow-item">
|
||||
@@ -246,13 +245,6 @@
|
||||
ref="userForm"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input
|
||||
v-model="userForm.name"
|
||||
placeholder="请输入姓名"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="用户名" prop="user">
|
||||
<el-input
|
||||
@@ -744,7 +736,6 @@ export default {
|
||||
},
|
||||
userForm: {
|
||||
id: null,
|
||||
name: '',
|
||||
user: '',
|
||||
pwd: '',
|
||||
status: 1,
|
||||
@@ -754,9 +745,6 @@ export default {
|
||||
flowResetTime: 1
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{ required: true, message: '请输入姓名', trigger: 'blur' }
|
||||
],
|
||||
user: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
{ min: 3, message: '用户名长度至少3位', trigger: 'blur' }
|
||||
@@ -1326,8 +1314,6 @@ export default {
|
||||
<style scoped>
|
||||
.user-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</el-button>
|
||||
|
||||
<!-- 节点卡片展示 -->
|
||||
<div class="cards-container" v-loading="loading" style="margin-top: 10px;">
|
||||
<div class="cards-container" v-loading="loading" style="margin-top: 20px;">
|
||||
<div class="cards-grid">
|
||||
<div
|
||||
v-for="node in nodeList"
|
||||
@@ -168,6 +168,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 节点与隧道转发IP重要说明 -->
|
||||
<el-alert
|
||||
title="重要提示"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-top: 30px; margin-bottom: 20px;">
|
||||
<template slot="title">
|
||||
<span style="font-weight: 600;">节点与隧道转发IP说明</span>
|
||||
</template>
|
||||
<div style="font-size: 13px; line-height: 1.5; color: #e6a23c;">
|
||||
<p style="margin: 0 0 8px 0;">• 节点IP若与实际入口IP不同,可点击编辑手动更改</p>
|
||||
<p style="margin: 0 0 8px 0;">• 更改节点IP后,<strong>已创建的隧道和转发IP不会自动更改</strong></p>
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
:visible.sync="dialogVisible"
|
||||
@@ -883,9 +899,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.node-container {
|
||||
padding: 5px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: calc(100vh - 60px);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 页面头部 */
|
||||
@@ -1184,25 +1198,25 @@ export default {
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.node-container {
|
||||
padding: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
.cards-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
|
||||
.charts-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
||||
.traffic-stats {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
@@ -1210,58 +1224,58 @@ export default {
|
||||
padding: 15px 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
|
||||
.header-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.node-card {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
|
||||
.node-header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.node-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
.status-indicator {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.chart-wrapper {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
|
||||
.chart-title {
|
||||
font-size: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
.stat-item {
|
||||
padding: 6px;
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
|
||||
.stat-header {
|
||||
font-size: 11px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
.traffic-value {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
.speed-value {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
|
||||
.ip-value {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
||||
.node-actions {
|
||||
padding-top: 10px;
|
||||
gap: 6px;
|
||||
@@ -1280,58 +1294,58 @@ export default {
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.node-container {
|
||||
padding: 3px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.cards-grid {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
.node-card {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
|
||||
.charts-container {
|
||||
gap: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.traffic-stats {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
|
||||
.stat-item {
|
||||
padding: 5px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
|
||||
.chart-wrapper {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
|
||||
.node-actions {
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
|
||||
.node-actions .el-button {
|
||||
font-size: 11px;
|
||||
padding: 5px 10px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
.add-btn {
|
||||
font-size: 12px;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
|
||||
.page-header {
|
||||
padding: 10px 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user