更改目录结构

This commit is contained in:
bjdgyc
2021-03-01 15:46:08 +08:00
parent 3464d1d10e
commit 0f91c779e3
105 changed files with 29099 additions and 96 deletions

160
web/src/pages/Home.vue Normal file
View File

@@ -0,0 +1,160 @@
<template>
<div class="home">
<el-row :gutter="40" class="panel-group">
<el-col :span="6" class="card-panel-col">
<div class="card-panel">
<i class="el-icon-user-solid" style="font-size:50px;color: #f4516c;"></i>
<div class="card-panel-description">
<div class="card-panel-text">在线数</div>
<countTo :startVal='0' :endVal='counts.online' :duration='2000' class="panel-num"></countTo>
</div>
</div>
</el-col>
<el-col :span="6" class="card-panel-col">
<div class="card-panel">
<i class="el-icon-user-solid" style="font-size:50px;color: #36a3f7"></i>
<div class="card-panel-description">
<div class="card-panel-text">用户数</div>
<countTo :startVal='0' :endVal='counts.user' :duration='2000' class="panel-num"></countTo>
</div>
</div>
</el-col>
<el-col :span="6" class="card-panel-col">
<div class="card-panel">
<i class="el-icon-wallet" style="font-size:50px;color:#34bfa3"></i>
<div class="card-panel-description">
<div class="card-panel-text">用户组数</div>
<countTo :startVal='0' :endVal='counts.group' :duration='2000' class="panel-num"></countTo>
</div>
</div>
</el-col>
<el-col :span="6" class="card-panel-col">
<div class="card-panel">
<i class="el-icon-s-order" style="font-size:50px;color:#40c9c6"></i>
<div class="card-panel-description">
<div class="card-panel-text">IP映射数</div>
<countTo :startVal='0' :endVal='counts.ip_map' :duration='2000' class="panel-num"></countTo>
</div>
</div>
</el-col>
</el-row>
<el-row class="line-chart">
<LineChart :chart-data="lineChartUser"/>
</el-row>
<el-row class="line-chart">
<LineChart :chart-data="lineChartOrder"/>
</el-row>
</div>
</template>
<script>
import countTo from 'vue-count-to';
import LineChart from "@/components/LineChart";
import axios from "axios";
const lineChartUser = {
title: '每日在线统计',
xname: ['2019-12-13', '2019-12-14', '2019-12-15', '2019-12-16', '2019-12-17', '2019-12-18', '2019-12-19'],
xdata: {
'test1': [10, 120, 11, 134, 105, 10, 15],
'test2': [10, 82, 91, 14, 162, 10, 15]
}
}
const lineChartOrder = {
title: '每日流量统计',
xname: ['2019-12-13', '2019-12-14', '2019-12-15', '2019-12-16', '2019-12-17', '2019-12-18', '2019-12-19'],
xdata: {
'test1': [100, 120, 161, 134, 105, 160, 165],
'test2': [120, 82, 91, 154, 162, 140, 145]
}
}
export default {
name: "Home",
components: {
LineChart,
countTo,
},
data() {
return {
counts: {
online: 0,
user: 0,
group: 0,
ip_map: 0,
},
lineChartUser: lineChartUser,
lineChartOrder: lineChartOrder,
}
},
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['首页'])
},
mounted() {
this.getData()
},
methods: {
getData() {
axios.get('/set/home').then(resp => {
var data = resp.data.data
console.log(data);
this.counts = data.counts
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
},
}
</script>
<style scoped>
.card-panel {
display: flex;
justify-content: space-around;
border: 1px solid red;
padding: 30px 0;
color: #666;
background: #fff;
/*box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);*/
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
border-color: rgba(0, 0, 0, .05);
}
.card-panel-description {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.card-panel-text {
line-height: 18px;
color: rgba(0, 0, 0, .45);
font-size: 16px;
}
.panel-num {
font-size: 20px;
font-weight: 700;
}
.line-chart {
background: #fff;
padding: 0 16px;
margin-top: 40px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
border-color: rgba(0, 0, 0, .05);
}
</style>

118
web/src/pages/Login.vue Normal file
View File

@@ -0,0 +1,118 @@
<template>
<div class="login">
<el-card style="width: 550px;">
<div class="issuer">AnyLink SSL VPN管理后台</div>
<el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="ruleForm">
<el-form-item label="管理用户名" prop="admin_user">
<el-input v-model="ruleForm.admin_user"></el-input>
</el-form-item>
<el-form-item label="管理密码" prop="admin_pass">
<el-input type="password" v-model="ruleForm.admin_pass" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="isLoading" @click="submitForm('ruleForm')">登录</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script>
import axios from "axios";
import qs from "qs";
import {setToken, setUser} from "@/plugins/token";
export default {
name: "Login",
mounted() {
// 进入login删除登录信息
console.log("login created")
//绑定事件
window.addEventListener('keydown', this.keyDown);
},
destroyed(){
window.removeEventListener('keydown',this.keyDown,false);
},
data() {
return {
ruleForm: {},
rules: {
admin_user: [
{required: true, message: '请输入用户名', trigger: 'blur'},
{max: 50, message: '长度小于 50 个字符', trigger: 'blur'}
],
admin_pass: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 6, message: '长度大于 6 个字符', trigger: 'blur'}
],
},
}
},
methods: {
keyDown(e) {
//如果是回车则执行登录方法
if (e.keyCode === 13) {
this.submitForm('ruleForm');
}
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('error submit!!');
return false;
}
this.isLoading = true
// alert('submit!');
axios.post('/base/login', qs.stringify(this.ruleForm)).then(resp => {
var rdata = resp.data
if (rdata.code === 0) {
this.$message.success(rdata.msg);
setToken(rdata.data.token)
setUser(rdata.data.admin_user)
this.$router.push("/home");
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
}).finally(() => {
this.isLoading = false
}
);
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
}
</script>
<style scoped>
.login {
/*border: 1px solid red;*/
height: 100%;
/*margin: 0 auto;*/
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.issuer {
font-size: 26px;
font-weight: bold;
margin-bottom: 50px;
}
</style>

View File

@@ -0,0 +1,447 @@
<template>
<div>
<el-card>
<el-form :inline="true">
<el-form-item>
<el-button
size="small"
type="primary"
icon="el-icon-plus"
@click="handleEdit('')">添加
</el-button>
</el-form-item>
</el-form>
<el-table
ref="multipleTable"
:data="tableData"
border>
<el-table-column
sortable="true"
prop="id"
label="ID"
width="60">
</el-table-column>
<el-table-column
prop="name"
label="组名">
</el-table-column>
<el-table-column
prop="note"
label="备注">
</el-table-column>
<el-table-column
prop="allow_lan"
label="本地网络">
<template slot-scope="scope">
<el-switch
v-model="scope.row.allow_lan"
disabled>
</el-switch>
</template>
</el-table-column>
<el-table-column
prop="bandwidth"
label="带宽限制">
</el-table-column>
<el-table-column
prop="client_dns"
label="客户端DNS"
width="160">
<template slot-scope="scope">
<el-row v-for="(item,inx) in scope.row.client_dns" :key="inx">{{ item.val }}</el-row>
</template>
</el-table-column>
<el-table-column
prop="route_include"
label="路由包含"
width="200">
<template slot-scope="scope">
<el-row v-for="(item,inx) in scope.row.route_include" :key="inx">{{ item.val }}</el-row>
</template>
</el-table-column>
<el-table-column
prop="route_exclude"
label="路由排除"
width="200">
<template slot-scope="scope">
<el-row v-for="(item,inx) in scope.row.route_exclude" :key="inx">{{ item.val }}</el-row>
</template>
</el-table-column>
<el-table-column
prop="link_acl"
label="LINK-ACL"
min-width="200">
<template slot-scope="scope">
<el-row v-for="(item,inx) in scope.row.link_acl" :key="inx">
{{ item.action }} => {{ item.val }} : {{ item.port }}
</el-row>
</template>
</el-table-column>
<el-table-column
prop="status"
label="状态"
width="70">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 1" type="success">可用</el-tag>
<el-tag v-else type="danger">停用</el-tag>
</template>
</el-table-column>
<el-table-column
prop="updated_at"
label="更新时间"
:formatter="tableDateFormat">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleEdit(scope.row)">编辑
</el-button>
<el-popconfirm
style="margin-left: 10px"
@onConfirm="handleDel(scope.row)"
title="确定要删除用户吗?">
<el-button
slot="reference"
size="mini"
type="danger">删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="sh-20"></div>
<el-pagination
background
layout="prev, pager, next"
:pager-count="11"
@current-change="pageChange"
:current-page="page"
:total="count">
</el-pagination>
</el-card>
<!--新增修改弹出框-->
<el-dialog
:close-on-click-modal="false"
title="用户组"
:visible.sync="user_edit_dialog"
width="750px"
center>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="ruleForm">
<el-form-item label="用户组ID" prop="id">
<el-input v-model="ruleForm.id" disabled></el-input>
</el-form-item>
<el-form-item label="组名" prop="name">
<el-input v-model="ruleForm.name" :disabled="ruleForm.id > 0"></el-input>
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="ruleForm.note"></el-input>
</el-form-item>
<el-form-item label="带宽限制" prop="bandwidth">
<el-input v-model.number="ruleForm.bandwidth">
<template slot="append">BYTE</template>
</el-input>
</el-form-item>
<el-form-item label="本地网络" prop="allow_lan">
<el-switch
v-model="ruleForm.allow_lan">
</el-switch>
</el-form-item>
<el-form-item label="客户端DNS" prop="client_dns">
<el-row class="msg-info">
<el-col :span="20">输入IP格式如: 192.168.0.10</el-col>
<el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.client_dns)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.client_dns)"></el-button>
</el-col>
</el-row>
<el-row v-for="(item,index) in ruleForm.client_dns"
:key="index" style="margin-bottom: 5px" gutter="10">
<el-col :span="10">
<el-input v-model="item.val"></el-input>
</el-col>
<el-col :span="14">
<el-input v-model="item.note" placeholder="备注"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="包含路由" prop="route_include">
<el-row class="msg-info">
<el-col :span="20">输入CIDR格式如: 192.168.1.0/24</el-col>
<el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.route_include)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_include)"></el-button>
</el-col>
</el-row>
<el-row v-for="(item,index) in ruleForm.route_include"
:key="index" style="margin-bottom: 5px" gutter="10">
<el-col :span="10">
<el-input v-model="item.val"></el-input>
</el-col>
<el-col :span="14">
<el-input v-model="item.note" placeholder="备注"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="排除路由" prop="route_exclude">
<el-row class="msg-info">
<el-col :span="20">输入CIDR格式如: 192.168.2.0/24</el-col>
<el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.route_exclude)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.route_exclude)"></el-button>
</el-col>
</el-row>
<el-row v-for="(item,index) in ruleForm.route_exclude"
:key="index" style="margin-bottom: 5px" gutter="10">
<el-col :span="10">
<el-input v-model="item.val"></el-input>
</el-col>
<el-col :span="14">
<el-input v-model="item.note" placeholder="备注"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="权限控制" prop="link_acl">
<el-row class="msg-info">
<el-col :span="20">输入CIDR格式如: 192.168.3.0/24 端口0表示所有端口</el-col>
<el-col :span="4">
<el-button size="mini" type="success" icon="el-icon-plus" circle
@click.prevent="addDomain(ruleForm.link_acl)"></el-button>
<el-button size="mini" type="danger" icon="el-icon-minus" circle
@click.prevent="removeDomain(ruleForm.link_acl)"></el-button>
</el-col>
</el-row>
<el-row v-for="(item,index) in ruleForm.link_acl"
:key="index" style="margin-bottom: 5px" gutter="5">
<el-col :span="11">
<el-input placeholder="请输入CIDR地址" v-model="item.val">
<el-select v-model="item.action" slot="prepend">
<el-option label="允许" value="allow"></el-option>
<el-option label="禁止" value="deny"></el-option>
</el-select>
</el-input>
</el-col>
<el-col :span="3">
<el-input v-model.number="item.port" placeholder="端口"></el-input>
</el-col>
<el-col :span="10">
<el-input v-model="item.note" placeholder="备注"></el-input>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="ruleForm.status">
<el-radio :label="1" border>启用</el-radio>
<el-radio :label="0" border>停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">保存</el-button>
<el-button @click="disVisible">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "List",
components: {},
mixins: [],
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['用户组信息', '用户组列表'])
},
mounted() {
this.getData(1)
},
data() {
return {
page: 1,
tableData: [],
count: 10,
ruleForm: {
bandwidth: 0,
status: 1,
allow_lan: true,
client_dns: [{val: '114.114.114.114'}],
route_include: [],
route_exclude: [],
link_acl: [],
},
rules: {
name: [
{required: true, message: '请输入用户名', trigger: 'blur'},
{max: 30, message: '长度小于 30 个字符', trigger: 'blur'}
],
bandwidth: [
{required: true, message: '请输入用户姓名', trigger: 'blur'},
{type: 'number', message: '年龄必须为数字值'}
],
email: [
{required: true, message: '请输入用户邮箱', trigger: 'blur'},
{type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}
],
status: [
{required: true}
],
},
}
},
methods: {
handleDel(row) {
axios.post('/group/del?id=' + row.id).then(resp => {
const rdata = resp.data;
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleEdit(row) {
!this.$refs['ruleForm'] || this.$refs['ruleForm'].resetFields();
console.log(row)
this.user_edit_dialog = true
if (!row) {
return;
}
axios.get('/group/detail', {
params: {
id: row.id,
}
}).then(resp => {
this.ruleForm = resp.data.data
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
pageChange(p) {
this.getData(p)
},
getData(page) {
this.page = page
axios.get('/group/list', {
params: {
page: page,
}
}).then(resp => {
const rdata = resp.data.data;
console.log(rdata);
this.tableData = rdata.datas;
this.count = rdata.count
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
removeDomain(arr, item) {
console.log(item)
// let index = arr.indexOf(item);
// if (index !== -1 && arr.length > 1) {
// arr.splice(index, 1)
// }
arr.pop()
},
addDomain(arr) {
arr.push({val: "", action: "allow", port: 0});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('error submit!!');
return false;
}
axios.post('/group/set', this.ruleForm).then(resp => {
const rdata = resp.data;
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
}
</script>
<style scoped>
.msg-info {
background-color: #f4f4f5;
color: #909399;
padding: 0 5px;
margin: 0;
box-sizing: border-box;
border-radius: 4px;
font-size: 12px;
}
.el-select {
width: 80px;
}
</style>

183
web/src/pages/set/Other.vue Normal file
View File

@@ -0,0 +1,183 @@
<template>
<el-card>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="邮件配置" name="dataSmtp">
<el-form :model="dataSmtp" ref="dataSmtp" :rules="rules" label-width="100px" class="tab-one">
<el-form-item label="服务器地址" prop="host">
<el-input v-model="dataSmtp.host"></el-input>
</el-form-item>
<el-form-item label="服务器端口" prop="port">
<el-input v-model.number="dataSmtp.port"></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="dataSmtp.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="dataSmtp.password"></el-input>
</el-form-item>
<el-form-item label="启用SSL" prop="use_ssl">
<el-switch v-model="dataSmtp.use_ssl"></el-switch>
</el-form-item>
<el-form-item label="邮件from" prop="from">
<el-input v-model="dataSmtp.from"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('dataSmtp')">保存</el-button>
<el-button @click="resetForm('dataSmtp')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="其他设置" name="dataOther">
<el-form :model="dataOther" ref="dataOther" :rules="rules" label-width="100px" class="tab-one">
<el-form-item label="Banner信息" prop="banner">
<el-input
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="dataOther.banner">
</el-input>
</el-form-item>
<el-form-item label="账户开通邮件" prop="account_mail">
<el-input
type="textarea"
:rows="10"
placeholder="请输入内容"
v-model="dataOther.account_mail">
</el-input>
</el-form-item>
<el-form-item label="邮件展示">
<iframe
width="500px"
height="300px"
:srcdoc="dataOther.account_mail">
</iframe>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('dataOther')">保存</el-button>
<el-button @click="resetForm('dataOther')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-card>
</template>
<script>
import axios from "axios";
export default {
name: "Other",
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['基础信息', '其他设置'])
},
mounted() {
this.getSmtp()
},
data() {
return {
activeName: 'dataSmtp',
dataSmtp: {},
dataOther: {},
rules: {
host: {required: true, message: '请输入服务器地址', trigger: 'blur'},
port: [
{required: true, message: '请输入服务器端口', trigger: 'blur'},
{type: 'number', message: '请输入正确的服务器端口', trigger: ['blur', 'change']}
],
issuer: {required: true, message: '请输入系统名称', trigger: 'blur'},
},
};
},
methods: {
handleClick(tab, event) {
window.console.log(tab.name, event);
switch (tab.name) {
case "dataSmtp":
this.getSmtp()
break
case "dataOther":
this.getOther()
break
}
},
getSmtp() {
axios.get('/set/other/smtp').then(resp => {
let rdata = resp.data
console.log(rdata)
if (rdata.code !== 0) {
this.$message.error(rdata.msg);
return;
}
this.dataSmtp = rdata.data
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
getOther() {
axios.get('/set/other').then(resp => {
let rdata = resp.data
console.log(rdata)
if (rdata.code !== 0) {
this.$message.error(rdata.msg);
return;
}
this.dataOther = rdata.data
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
alert('error submit!');
}
switch (formName) {
case "dataSmtp":
axios.post('/set/other/smtp/edit', this.dataSmtp).then(resp => {
var rdata = resp.data
console.log(rdata);
if (rdata.code === 0) {
this.$message.success(rdata.msg);
} else {
this.$message.error(rdata.msg);
}
})
break;
case "dataOther":
axios.post('/set/other/edit', this.dataOther).then(resp => {
var rdata = resp.data
console.log(rdata);
if (rdata.code === 0) {
this.$message.success(rdata.msg);
} else {
this.$message.error(rdata.msg);
}
})
break;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
}
</script>
<style scoped>
.tab-one {
width: 600px;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<el-card>
<el-table
:data="soft_data"
border>
<el-table-column
prop="info"
label="信息"
width="260">
</el-table-column>
<el-table-column
prop="name"
label="配置"
width="200">
</el-table-column>
<el-table-column
prop="data"
label="数据">
<template slot-scope="scope">
{{ scope.row.data }}
</template>
</el-table-column>
</el-table>
</el-card>
</template>
<script>
import axios from "axios";
export default {
name: "Soft",
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['基础信息', '软件配置'])
},
mounted() {
this.getSoftInfo()
},
data() {
return {soft_data: []}
},
methods: {
getSoftInfo() {
axios.get('/set/soft', {}).then(resp => {
var data = resp.data
console.log(data);
this.soft_data = data.data;
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,133 @@
<template>
<div>
<el-row :gutter="10" class="mb10">
<el-col :span="8">
<el-card v-if="system.cpu" body-style="text-align: center;">
<div slot="header">
<span>CPU使用率</span>
</div>
<el-progress type="circle" :percentage="system.cpu.percent" style="margin-bottom: 20px"/>
<Cell left="CPU主频" :right="system.cpu.ghz" divider/>
<Cell left="系统负载" :right="system.sys.load"/>
</el-card>
</el-col>
<el-col :span="8">
<el-card v-if="system.mem" body-style="text-align: center;">
<div slot="header">
<span>内存使用率</span>
</div>
<el-progress type="circle" :percentage="system.mem.percent" style="margin-bottom: 20px"/>
<Cell left="总内存" :right="system.mem.total" divider/>
<Cell left="剩余内存" :right="system.mem.free"/>
</el-card>
</el-col>
<el-col :span="8">
<el-card v-if="system.disk" body-style="text-align: center;">
<div slot="header">
<span>磁盘信息</span>
</div>
<el-progress type="circle" :percentage="system.disk.percent" style="margin-bottom: 20px"/>
<Cell left="总存储" :right="system.disk.total" divider/>
<Cell left="剩余存储" :right="system.disk.free"/>
</el-card>
</el-col>
</el-row>
<el-card v-if="system.sys" style="margin-top: 10px">
<div slot="header">
<span>go运行环境</span>
</div>
<Cell left="GO版本" :right="system.sys.goOs" divider/>
<Cell left="GoArch" :right="system.sys.goArch" divider/>
<Cell left="GoVersion" :right="system.sys.goVersion" divider/>
<Cell left="Goroutine" :right="system.sys.goroutine"/>
</el-card>
<el-card v-if="system.sys" style="margin-top: 10px">
<div slot="header">
<span>服务器信息</span>
</div>
<Cell left="机器名称" :right="system.sys.hostname" divider/>
<Cell left="操作系统" :right="system.sys.platform" divider/>
<Cell left="内核版本" :right="system.sys.kernel" divider/>
<Cell left="CPU核心" :right="system.cpu.core" divider/>
<Cell left="CPU" :right="system.cpu.modelName"/>
</el-card>
</div>
</template>
<script>
import Cell from "@/components/Cell";
import axios from "axios";
export default {
name: 'Monitor',
components: {Cell},
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['基础信息', '系统信息'])
},
mounted() {
this.getData();
// const chatTimer = setInterval(() => {
// this.getData();
// }, 2000);
//
// this.$once('hook:beforeDestroy', () => {
// clearInterval(chatTimer);
// });
},
data() {
return {system: {}}
},
methods: {
getData() {
axios.get('/set/system', {}).then(resp => {
var data = resp.data.data
console.log(data);
this.system = data;
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
}
}
}
</script>
<style scoped>
.monitor {
display: flex;
justify-content: space-between;
align-items: center;
}
.monitor-left {
font-size: 14px;
}
.monitor-right {
font-size: 12px;
color: #909399;
}
</style>

View File

@@ -0,0 +1,273 @@
<template>
<div>
<el-card>
<el-form :inline="true">
<el-form-item>
<el-button
size="small"
type="primary"
icon="el-icon-plus"
@click="handleEdit('')">添加
</el-button>
</el-form-item>
</el-form>
<el-table
ref="multipleTable"
:data="tableData"
border>
<el-table-column
sortable="true"
prop="id"
label="ID"
width="60">
</el-table-column>
<el-table-column
prop="ip_addr"
label="IP地址">
</el-table-column>
<el-table-column
prop="mac_addr"
label="MAC地址">
</el-table-column>
<el-table-column
prop="username"
label="用户名">
</el-table-column>
<el-table-column
prop="keep"
label="IP保留">
<template slot-scope="scope">
<!-- <el-tag v-if="scope.row.keep" type="success">保留</el-tag>-->
<el-switch
disabled
v-model="scope.row.keep"
active-color="#13ce66">
</el-switch>
</template>
</el-table-column>
<el-table-column
prop="note"
label="备注">
</el-table-column>
<el-table-column
prop="last_login"
label="最后登陆时间"
:formatter="tableDateFormat">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleEdit(scope.row)">编辑
</el-button>
<el-popconfirm
class="m-left-10"
@onConfirm="handleDel(scope.row)"
title="确定要删除IP映射吗">
<el-button
slot="reference"
size="mini"
type="danger"
@click="handleDelete(scope.row)">删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="sh-20"></div>
<el-pagination
background
layout="prev, pager, next"
:pager-count="11"
@current-change="pageChange"
:total="count">
</el-pagination>
</el-card>
<!--新增修改弹出框-->
<el-dialog
title="提示"
:close-on-click-modal="false"
:visible="user_edit_dialog"
@close="disVisible"
width="600px"
center>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="ruleForm">
<el-form-item label="ID" prop="id">
<el-input v-model="ruleForm.id" disabled></el-input>
</el-form-item>
<el-form-item label="IP地址" prop="ip_addr">
<el-input v-model="ruleForm.ip_addr"></el-input>
</el-form-item>
<el-form-item label="MAC地址" prop="mac_addr">
<el-input v-model="ruleForm.mac_addr"></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="备注" prop="note">
<el-input v-model="ruleForm.note"></el-input>
</el-form-item>
<el-form-item label="IP保留" prop="keep">
<el-switch
v-model="ruleForm.keep"
active-color="#13ce66">
</el-switch>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">保存</el-button>
<el-button @click="disVisible">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "IpMap",
components: {},
mixins:[],
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['用户信息', 'IP映射'])
},
mounted() {
this.getData(1)
},
data() {
return {
tableData: [],
count: 10,
nowIndex: 0,
ruleForm: {
status: 1,
groups: [],
},
rules: {
username: [
{required: false, message: '请输入用户名', trigger: 'blur'},
{max: 50, message: '长度小于 50 个字符', trigger: 'blur'}
],
mac_addr: [
{required: true, message: '请输入mac地址', trigger: 'blur'}
],
ip_addr: [
{required: true, message: '请输入ip地址', trigger: 'blur'}
],
status: [
{required: true}
],
},
}
},
methods: {
getData(p) {
axios.get('/user/ip_map/list', {
params: {
page: p,
}
}).then(resp => {
var data = resp.data.data
console.log(data);
this.tableData = data.datas;
this.count = data.count
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
pageChange(p) {
this.getData(p)
},
handleEdit(row) {
!this.$refs['ruleForm'] || this.$refs['ruleForm'].resetFields();
console.log(row)
this.user_edit_dialog = true
if (!row) {
return;
}
axios.get('/user/ip_map/detail', {
params: {
id: row.id,
}
}).then(resp => {
this.ruleForm = resp.data.data
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleDel(row) {
axios.post('/user/ip_map/del?id=' + row.id).then(resp => {
var rdata = resp.data
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('error submit!!');
return false;
}
// alert('submit!');
axios.post('/user/ip_map/set', this.ruleForm).then(resp => {
var rdata = resp.data
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
});
},
},
}
</script>
<style scoped>
</style>

410
web/src/pages/user/List.vue Normal file
View File

@@ -0,0 +1,410 @@
<template>
<div>
<el-card>
<el-form :inline="true">
<el-form-item>
<el-button
size="small"
type="primary"
icon="el-icon-plus"
@click="handleEdit('')">添加
</el-button>
</el-form-item>
<el-form-item label="用户名:">
<el-input size="small" v-model="searchData" placeholder="请输入内容"></el-input>
</el-form-item>
<el-form-item>
<el-button
size="small"
type="primary"
icon="el-icon-search"
@click="handleSearch()">搜索
</el-button>
<el-button
size="small"
icon="el-icon-refresh"
@click="searchData=''">重置搜索
</el-button>
</el-form-item>
</el-form>
<el-table
ref="multipleTable"
:data="tableData"
border>
<el-table-column
sortable="true"
prop="id"
label="ID"
width="60">
</el-table-column>
<el-table-column
prop="username"
label="用户名"
width="150">
</el-table-column>
<el-table-column
prop="nickname"
label="姓名"
width="100">
</el-table-column>
<el-table-column
prop="email"
label="邮箱">
</el-table-column>
<el-table-column
prop="otp_secret"
label="OTP密钥"
width="110">
<template slot-scope="scope">
<el-button
v-if="!scope.row.disable_otp"
type="text"
icon="el-icon-view"
@click="getOtpImg(scope.row)">
{{ scope.row.otp_secret.substring(0, 6) }}
</el-button>
</template>
</el-table-column>
<el-table-column
prop="groups"
label="用户组">
<template slot-scope="scope">
<el-row v-for="item in scope.row.groups" :key="item">{{ item }}</el-row>
</template>
</el-table-column>
<el-table-column
prop="status"
label="状态"
width="70">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === 1" type="success">可用</el-tag>
<el-tag v-else type="danger">停用</el-tag>
</template>
</el-table-column>
<el-table-column
prop="updated_at"
label="更新时间"
:formatter="tableDateFormat">
</el-table-column>
<el-table-column
label="操作"
width="210">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleEdit(scope.row)">编辑
</el-button>
<!-- <el-popconfirm
class="m-left-10"
@onConfirm="handleClick('reset',scope.row)"
title="确定要重置用户密码和密钥吗?">
<el-button
slot="reference"
size="mini"
type="warning">重置
</el-button>
</el-popconfirm>-->
<el-popconfirm
class="m-left-10"
@onConfirm="handleDel(scope.row)"
title="确定要删除用户吗?">
<el-button
slot="reference"
size="mini"
type="danger">删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="sh-20"></div>
<el-pagination
background
layout="prev, pager, next"
:pager-count="11"
@current-change="pageChange"
:current-page="page"
:total="count">
</el-pagination>
</el-card>
<el-dialog
title="OTP密钥"
:visible.sync="otpImgData.visible"
width="350px"
center>
<div style="text-align: center">{{ otpImgData.username }} : {{ otpImgData.nickname }}</div>
<img :src="otpImgData.base64Img" alt="otp-img"/>
</el-dialog>
<!--新增修改弹出框-->
<el-dialog
:close-on-click-modal="false"
title="用户"
:visible="user_edit_dialog"
@close="disVisible"
width="650px"
center>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="ruleForm">
<el-form-item label="用户ID" prop="id">
<el-input v-model="ruleForm.id" disabled></el-input>
</el-form-item>
<el-form-item label="用户名" prop="username">
<el-input v-model="ruleForm.username" :disabled="ruleForm.id > 0"></el-input>
</el-form-item>
<el-form-item label="姓名" prop="nickname">
<el-input v-model="ruleForm.nickname"></el-input>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="ruleForm.email"></el-input>
</el-form-item>
<el-form-item label="PIN码" prop="pin_code">
<el-input v-model="ruleForm.pin_code" placeholder="不填由系统自动生成"></el-input>
</el-form-item>
<el-form-item label="禁用OTP" prop="disable_otp">
<el-switch
v-model="ruleForm.disable_otp">
</el-switch>
</el-form-item>
<el-form-item label="OTP密钥" prop="otp_secret" v-if="!ruleForm.disable_otp">
<el-input v-model="ruleForm.otp_secret" placeholder="不填由系统自动生成"></el-input>
</el-form-item>
<el-form-item label="用户组" prop="groups">
<el-checkbox-group v-model="ruleForm.groups">
<el-checkbox v-for="(item) in grouNames" :key="item" :label="item" :name="item"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="发送邮件" prop="send_email">
<el-switch
v-model="ruleForm.send_email">
</el-switch>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="ruleForm.status">
<el-radio :label="1" border>启用</el-radio>
<el-radio :label="0" border>停用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">保存</el-button>
<!-- <el-button @click="resetForm('ruleForm')">重置</el-button>-->
<el-button @click="disVisible">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "List",
components: {},
mixins: [],
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['用户信息', '用户列表'])
},
mounted() {
this.getGroups();
this.getData(1)
},
data() {
return {
page: 1,
grouNames: [],
tableData: [],
count: 10,
searchData: '',
otpImgData: {visible: false, username: '', nickname: '', base64Img: ''},
ruleForm: {
send_email: true,
status: 1,
groups: [],
},
rules: {
username: [
{required: true, message: '请输入用户名', trigger: 'blur'},
{max: 50, message: '长度小于 50 个字符', trigger: 'blur'}
],
nickname: [
{required: true, message: '请输入用户姓名', trigger: 'blur'}
],
email: [
{required: true, message: '请输入用户邮箱', trigger: 'blur'},
{type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change']}
],
password: [
{min: 6, message: '长度大于 6 个字符', trigger: 'blur'}
],
pin_code: [
{min: 6, message: 'PIN码大于 6 个字符', trigger: 'blur'}
],
date1: [
{type: 'date', required: true, message: '请选择日期', trigger: 'change'}
],
groups: [
{type: 'array', required: true, message: '请至少选择一个组', trigger: 'change'}
],
status: [
{required: true}
],
},
}
},
methods: {
getOtpImg(row) {
// this.base64Img = Buffer.from(data).toString('base64');
this.otpImgData.visible = true
axios.get('/user/otp_qr', {
params: {
id: row.id,
b64: '1',
}
}).then(resp => {
var rdata = resp.data;
// console.log(resp);
this.otpImgData.username = row.username;
this.otpImgData.nickname = row.nickname;
this.otpImgData.base64Img = 'data:image/png;base64,' + rdata
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleDel(row) {
axios.post('/user/del?id=' + row.id).then(resp => {
var rdata = resp.data
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleEdit(row) {
!this.$refs['ruleForm'] || this.$refs['ruleForm'].resetFields();
console.log(row)
this.user_edit_dialog = true
if (!row) {
return;
}
axios.get('/user/detail', {
params: {
id: row.id,
}
}).then(resp => {
var data = resp.data.data
// 修改默认不发送邮件
data.send_email = false
this.ruleForm = data
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleSearch() {
console.log(this.searchData)
this.getData(1, this.searchData)
},
pageChange(p) {
this.getData(p)
},
getData(page, prefix) {
this.page = page
axios.get('/user/list', {
params: {
page: page,
prefix: prefix || '',
}
}).then(resp => {
var data = resp.data.data
console.log(data);
this.tableData = data.datas;
this.count = data.count
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
getGroups() {
axios.get('/group/names', {}).then(resp => {
var data = resp.data.data
console.log(data.datas);
this.grouNames = data.datas;
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
console.log('error submit!!');
return false;
}
// alert('submit!');
axios.post('/user/set', this.ruleForm).then(resp => {
var data = resp.data
if (data.code === 0) {
this.$message.success(data.msg);
this.getData(1);
} else {
this.$message.error(data.msg);
}
console.log(data);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,196 @@
<template>
<div>
<el-card>
<el-table
ref="multipleTable"
:data="tableData"
border>
<el-table-column
sortable="true"
type="index"
label="序号"
width="50">
</el-table-column>
<el-table-column
prop="username"
label="用户名">
</el-table-column>
<el-table-column
prop="group"
label="登陆组">
</el-table-column>
<el-table-column
prop="mac_addr"
label="MAC地址">
</el-table-column>
<el-table-column
prop="ip"
label="IP地址"
width="140">
</el-table-column>
<el-table-column
prop="remote_addr"
label="远端地址">
</el-table-column>
<el-table-column
prop="tun_name"
label="虚拟网卡">
</el-table-column>
<el-table-column
prop="mtu"
label="MTU">
</el-table-column>
<el-table-column
prop="is_mobile"
label="客户端">
<template slot-scope="scope">
<i v-if="scope.row.client === 'mobile'" class="el-icon-mobile-phone" style="font-size: 20px;color: red"></i>
<i v-else class="el-icon-s-platform" style="font-size: 20px;color: blue"></i>
</template>
</el-table-column>
<el-table-column
prop="status"
label="实时 上行/下行"
width="220">
<template slot-scope="scope">
<el-tag type="success">{{ scope.row.bandwidth_up }}</el-tag>
<el-tag class="m-left-10">{{ scope.row.bandwidth_down }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="status"
label="总量 上行/下行"
width="200">
<template slot-scope="scope">
<el-tag effect="dark" type="success">{{ scope.row.bandwidth_up_all }}</el-tag>
<el-tag class="m-left-10" effect="dark">{{ scope.row.bandwidth_down_all }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="last_login"
label="登陆时间"
:formatter="tableDateFormat">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleReline(scope.row)">重连
</el-button>
<el-popconfirm
class="m-left-10"
@onConfirm="handleOffline(scope.row)"
title="确定要下线用户吗?">
<el-button
slot="reference"
size="mini"
type="danger">下线
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "Online",
components: {},
mixins: [],
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['用户信息', '在线用户'])
},
mounted() {
this.getData();
const chatTimer = setInterval(() => {
this.getData();
}, 2000);
this.$once('hook:beforeDestroy', () => {
clearInterval(chatTimer);
})
},
data() {
return {
tableData: [],
}
},
methods: {
handleOffline(row) {
axios.post('/user/offline?token=' + row.token).then(resp => {
var data = resp.data
if (data.code === 0) {
this.$message.success(data.msg);
this.getData();
} else {
this.$message.error(data.msg);
}
console.log(data);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleReline(row) {
axios.post('/user/reline?token=' + row.token).then(resp => {
var data = resp.data
if (data.code === 0) {
this.$message.success(data.msg);
this.getData();
} else {
this.$message.error(data.msg);
}
console.log(data);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
handleEdit(a, row) {
console.log(a, row)
},
getData() {
axios.get('/user/online').then(resp => {
var data = resp.data.data
console.log(data);
this.tableData = data.datas;
this.count = data.count
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
},
}
</script>
<style scoped>
</style>