mirror of https://github.com/bjdgyc/anylink.git
更改目录结构
This commit is contained in:
parent
3464d1d10e
commit
0f91c779e3
|
@ -1,4 +1,5 @@
|
|||
ignore:
|
||||
- "conf"
|
||||
- "down_files"
|
||||
- "screenshot"
|
||||
- "web"
|
||||
- "server/conf"
|
||||
- "server/files"
|
|
@ -19,6 +19,9 @@ jobs:
|
|||
go-version: 1.15
|
||||
id: go
|
||||
|
||||
- name: Switch path
|
||||
run: cd server
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
|
|
|
@ -1,19 +1,3 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
vendor/
|
||||
|
||||
ui/
|
||||
.idea/
|
||||
anylink
|
||||
anylink-deploy
|
||||
|
|
|
@ -34,7 +34,7 @@ AnyLink 服务端仅在CentOS 7、Ubuntu 18.04测试通过,如需要安装在
|
|||
git clone https://github.com/bjdgyc/anylink.git
|
||||
|
||||
cd anylink
|
||||
sh deploy.sh
|
||||
sh build.sh
|
||||
|
||||
# 注意使用root权限运行
|
||||
cd anylink-deploy
|
||||
|
@ -73,7 +73,7 @@ sudo ./anylink -conf="conf/server.toml"
|
|||
./anylink -secret
|
||||
```
|
||||
|
||||
[conf/server.toml](https://github.com/bjdgyc/anylink/blob/master/conf/server.toml)
|
||||
[conf/server.toml](server/conf/server.toml)
|
||||
|
||||
## Setting
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#当前目录
|
||||
cpath=$(pwd)
|
||||
|
||||
echo "编译二进制文件"
|
||||
cd $cpath/server
|
||||
go build -o anylink -ldflags "-X main.COMMIT_ID=$(git rev-parse HEAD)"
|
||||
|
||||
echo "编译前端项目"
|
||||
cd $cpath/web
|
||||
#国内可替换源加快速度
|
||||
npm install --registry=https://registry.npm.taobao.org
|
||||
npm run build --registry=https://registry.npm.taobao.org
|
||||
#npm install
|
||||
#npm run build
|
||||
|
||||
cd $cpath
|
||||
|
||||
echo "整理部署文件"
|
||||
deploy="anylink-deploy"
|
||||
rm -rf $deploy
|
||||
mkdir $deploy
|
||||
mkdir $deploy/log
|
||||
|
||||
cp -r server/anylink $deploy
|
||||
cp -r server/conf $deploy
|
||||
cp -r server/files $deploy
|
||||
cp -r server/bridge-init.sh $deploy
|
||||
|
||||
cp -r web/ui $deploy
|
||||
|
||||
#注意使用root权限运行
|
||||
#cd anylink-deploy
|
||||
#sudo ./anylink -conf="conf/server.toml"
|
29
deploy.sh
29
deploy.sh
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#编译二进制文件
|
||||
go build -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
||||
|
||||
#编译前端项目
|
||||
git clone https://github.com/bjdgyc/anylink-web.git
|
||||
cd anylink-web
|
||||
#国内可替换源加快速度
|
||||
#npm install --registry=https://registry.npm.taobao.org
|
||||
#npm run build --registry=https://registry.npm.taobao.org
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
cd ../
|
||||
|
||||
#整理部署文件
|
||||
mkdir anylink-deploy
|
||||
mkdir anylink-deploy/log
|
||||
|
||||
cp -r anylink anylink-deploy
|
||||
cp -r anylink-web/ui anylink-deploy
|
||||
cp -r conf anylink-deploy
|
||||
cp -r down_files anylink-deploy
|
||||
|
||||
#注意使用root权限运行
|
||||
#cd anylink-deploy
|
||||
#sudo ./anylink -conf="conf/server.toml"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
vendor/
|
||||
|
||||
ui/
|
||||
.idea/
|
||||
anylink
|
|
@ -2,5 +2,5 @@ package base
|
|||
|
||||
const (
|
||||
APP_NAME = "AnyLink"
|
||||
APP_VER = "0.1.5"
|
||||
APP_VER = "0.1.6"
|
||||
)
|
|
@ -18,6 +18,7 @@ type User struct {
|
|||
// Password string `json:"password"`
|
||||
PinCode string `json:"pin_code"`
|
||||
OtpSecret string `json:"otp_secret"`
|
||||
DisableOtp bool `json:"disable_otp"` // 禁用otp
|
||||
Groups []string `json:"groups"`
|
||||
Status int8 `json:"status"` // 1正常
|
||||
SendEmail bool `json:"send_email"`
|
||||
|
@ -39,7 +40,7 @@ func SetUser(v *User) error {
|
|||
v.PinCode = planPass
|
||||
|
||||
if v.OtpSecret == "" {
|
||||
v.OtpSecret = gotp.RandomSecret(24)
|
||||
v.OtpSecret = gotp.RandomSecret(32)
|
||||
}
|
||||
|
||||
// 判断组是否有效
|
||||
|
@ -85,19 +86,16 @@ func CheckUser(name, pwd, group string) error {
|
|||
}
|
||||
|
||||
// 判断otp信息
|
||||
if !v.DisableOtp {
|
||||
pwd = pwd[:pl-6]
|
||||
otp := pwd[pl-6:]
|
||||
if !checkOtp(name, otp) {
|
||||
if !checkOtp(name, otp, v.OtpSecret) {
|
||||
return fmt.Errorf("%s %s", name, "动态码错误")
|
||||
}
|
||||
totp := gotp.NewDefaultTOTP(v.OtpSecret)
|
||||
unix := time.Now().Unix()
|
||||
verify := totp.Verify(otp, int(unix))
|
||||
if !verify {
|
||||
return fmt.Errorf("%s %s", name, "动态码错误")
|
||||
}
|
||||
|
||||
pinCode := pwd[:pl-6]
|
||||
if pinCode != v.PinCode {
|
||||
// 判断用户密码
|
||||
if pwd != v.PinCode {
|
||||
return fmt.Errorf("%s %s", name, "密码错误")
|
||||
}
|
||||
|
||||
|
@ -126,18 +124,23 @@ func init() {
|
|||
}()
|
||||
}
|
||||
|
||||
// 令牌只能使用一次
|
||||
func checkOtp(username, otp string) bool {
|
||||
key := fmt.Sprintf("%s:%s", username, otp)
|
||||
// 判断令牌信息
|
||||
func checkOtp(name, otp, secret string) bool {
|
||||
key := fmt.Sprintf("%s:%s", name, otp)
|
||||
|
||||
userOtpMux.Lock()
|
||||
defer userOtpMux.Unlock()
|
||||
|
||||
// 令牌只能使用一次
|
||||
if _, ok := userOtp[key]; ok {
|
||||
// 已经存在
|
||||
return false
|
||||
}
|
||||
|
||||
userOtp[key] = time.Now()
|
||||
return true
|
||||
|
||||
totp := gotp.NewDefaultTOTP(secret)
|
||||
unix := time.Now().Unix()
|
||||
verify := totp.Verify(otp, int(unix))
|
||||
|
||||
return verify
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'eslint:recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
/ui
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 bjdgyc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,30 @@
|
|||
# anylink-web
|
||||
|
||||
## Repo
|
||||
|
||||
> github: https://github.com/bjdgyc/anylink-web
|
||||
|
||||
> gitee: https://gitee.com/bjdgyc/anylink-web
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "anylink-web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.20.0",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^4.9.0",
|
||||
"element-ui": "^2.4.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-count-to": "^1.0.13",
|
||||
"vue-router": "^3.4.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"vue-cli-plugin-element": "~1.0.1",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>AnyLink</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but AnyLink doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,84 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {},
|
||||
created() {
|
||||
const token = sessionStorage.getItem('jwtToken');
|
||||
console.log("App created", token)
|
||||
if (token) {
|
||||
this.$root.isLogin = true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
console.log("App mounted")
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
currentComponent: function () {
|
||||
var isLogin = this.$root.isLogin
|
||||
console.log("App isLogin", isLogin)
|
||||
if (isLogin) {
|
||||
return "layout";
|
||||
}
|
||||
return "login";
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
/*color: #2c3e50;*/
|
||||
/*border: 1px solid red;*/
|
||||
height: 100%;
|
||||
/*width:100%;*/
|
||||
/*box-sizing: border-box;*/
|
||||
/*padding: 4px;*/
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*space vertical*/
|
||||
.sh-10 {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.sh-20 {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/*space horizontal*/
|
||||
.sw-10 {
|
||||
height: 1px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.sw-20 {
|
||||
height: 1px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.m-left-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
</style>
|
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="monitor">
|
||||
<div class="monitor-left">{{ left }}</div>
|
||||
<div class="monitor-right">{{ right }}</div>
|
||||
</div>
|
||||
<el-divider v-if="divider"></el-divider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Cell",
|
||||
props: {
|
||||
left: {},
|
||||
right: {},
|
||||
divider: {type: Boolean}
|
||||
},
|
||||
}
|
||||
</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>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div id="line-chart" :style="{height:height,width:width}"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'LineChart',
|
||||
props: {
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '350px'
|
||||
},
|
||||
// title,xAxis,series
|
||||
chartData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
mounted() {
|
||||
this.initChart()
|
||||
},
|
||||
beforeDestroy() {
|
||||
},
|
||||
methods: {
|
||||
initChart() {
|
||||
let chart = echarts.init(this.$el)
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: this.chartData.title || '折线图'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
// toolbox: {
|
||||
// feature: {
|
||||
// saveAsImage: {}
|
||||
// }
|
||||
// },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: this.chartData.xname
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [],
|
||||
};
|
||||
|
||||
let xdata = this.chartData.xdata
|
||||
for (let key in xdata) {
|
||||
// window.console.log(key);
|
||||
let a = {
|
||||
name: key,
|
||||
type: 'line',
|
||||
data: xdata[key]
|
||||
};
|
||||
option.series.push(a)
|
||||
}
|
||||
|
||||
chart.setOption(option)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<el-container style="height: 100%;">
|
||||
<!--侧边栏菜单-->
|
||||
<el-aside :width="is_active?'200':'64'">
|
||||
<LayoutAside :is_active="is_active" :route_path="route_path"/>
|
||||
</el-aside>
|
||||
|
||||
<el-container>
|
||||
<!--正文头部内容-->
|
||||
<el-header>
|
||||
<!--监听子组件的变量事件-->
|
||||
<LayoutHeader :is_active.sync="is_active" :route_name="route_name"/>
|
||||
</el-header>
|
||||
<!--正文内容-->
|
||||
<!--style="background-color: rgb(240, 242, 245);"-->
|
||||
<el-main style="background-color: #fbfbfb">
|
||||
<!-- 对应的组件内容渲染到router-view中 -->
|
||||
<!--子组件上报route信息-->
|
||||
<router-view :route_path.sync="route_path" :route_name.sync="route_name"></router-view>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LayoutAside from "@/layout/LayoutAside";
|
||||
import LayoutHeader from "@/layout/LayoutHeader";
|
||||
|
||||
export default {
|
||||
name: "Layout",
|
||||
components: {LayoutHeader, LayoutAside},
|
||||
data() {
|
||||
return {
|
||||
is_active: true,
|
||||
route_path: '/index',
|
||||
route_name: ['首页'],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
route_path: function (val) {
|
||||
// var w = document.getElementById('layout-menu').clientWidth;
|
||||
window.console.log('is_active', val)
|
||||
},
|
||||
},
|
||||
created() {
|
||||
window.console.log('layout-route', this.$route)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-header {
|
||||
background-color: #fff;
|
||||
/*box-shadow: 0 1px 4px rgba(0, 21, 41, .08);*/
|
||||
color: #333;
|
||||
line-height: 60px;
|
||||
/*width: 100%;*/
|
||||
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
|
||||
<!--background-color="#304156"-->
|
||||
<!--text-color="#bfcbd9"-->
|
||||
<!--active-text-color="#409EFF"-->
|
||||
<!--:unique-opened="false"-->
|
||||
|
||||
<!--<div class="layout-aside" :style="aside_style">-->
|
||||
|
||||
<el-menu :collapse="!is_active"
|
||||
:default-active="route_path"
|
||||
:style="is_active?'width:200px':''"
|
||||
router
|
||||
class="layout-menu"
|
||||
:collapse-transition="false"
|
||||
|
||||
background-color="#545c64"
|
||||
text-color="#fff"
|
||||
active-text-color="#ffd04b"
|
||||
>
|
||||
<el-menu-item index="/admin/home">
|
||||
<i class="el-icon-s-home"></i>
|
||||
<span slot="title">首页</span>
|
||||
</el-menu-item>
|
||||
|
||||
<el-submenu index="1">
|
||||
<template slot="title">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">基础信息</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="/admin/set/system">系统信息</el-menu-item>
|
||||
<el-menu-item index="/admin/set/soft">软件配置</el-menu-item>
|
||||
<el-menu-item index="/admin/set/other">其他设置</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
<el-submenu index="2">
|
||||
<template slot="title">
|
||||
<i class="el-icon-s-custom"></i>
|
||||
<span slot="title">用户信息</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="/admin/user/list">用户列表</el-menu-item>
|
||||
<el-menu-item index="/admin/user/online">在线用户</el-menu-item>
|
||||
<el-menu-item index="/admin/user/ip_map">IP映射</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
<el-submenu index="3">
|
||||
<template slot="title">
|
||||
<i class="el-icon-s-order"></i>
|
||||
<span slot="title">用户组信息</span>
|
||||
</template>
|
||||
|
||||
<el-menu-item index="/admin/group/list">用户组列表</el-menu-item>
|
||||
</el-submenu>
|
||||
|
||||
|
||||
</el-menu>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LayoutAside",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: ['is_active', 'route_path'],
|
||||
mounted() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.layout-menu {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
|
||||
<div class="layout-header">
|
||||
<div>
|
||||
<i @click="toggleClick" :class="is_active ? 'el-icon-s-fold' : 'el-icon-s-unfold'" class="toggle-icon"
|
||||
style="font-size: 26px;"></i>
|
||||
|
||||
<el-breadcrumb separator="/" class="app-breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in route_name" :key="index">{{ item }}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
<el-dropdown trigger="click" @command="handleCommand">
|
||||
<i class="el-icon-setting" style="margin-right: 15px"></i>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="logout">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
<span style="font-size: 12px;">{{ admin_user }}</span>
|
||||
</el-dropdown>
|
||||
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getUser, removeToken} from "@/plugins/token";
|
||||
|
||||
export default {
|
||||
name: "Layoutheader",
|
||||
props: ['route_name'],
|
||||
data() {
|
||||
return {
|
||||
is_active: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
admin_user() {
|
||||
return getUser();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 菜单栏开关按钮
|
||||
toggleClick() {
|
||||
this.is_active = !this.is_active
|
||||
// 触发事件,抛出到上层
|
||||
this.$emit('update:is_active', this.is_active)
|
||||
},
|
||||
handleCommand() {
|
||||
console.log("handleCommand")
|
||||
// 退出 删除登录信息
|
||||
removeToken()
|
||||
this.$router.push("/login");
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.layout-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
cursor: pointer;
|
||||
transition: background .3s;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.toggle-icon:hover {
|
||||
background: rgba(0, 0, 0, .025)
|
||||
}
|
||||
|
||||
.app-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
/*line-height: 20;*/
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,22 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import './plugins/element'
|
||||
import "./plugins/mixin";
|
||||
import request from './plugins/request'
|
||||
import router from "./plugins/router";
|
||||
|
||||
|
||||
//TODO
|
||||
Vue.config.productionTip = false
|
||||
|
||||
|
||||
const vm = new Vue({
|
||||
data: {
|
||||
// 判断是否登录
|
||||
isLogin: false,
|
||||
},
|
||||
router,
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
|
||||
request(vm)
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
||||
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
|||
import Vue from 'vue'
|
||||
import Element from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
|
||||
Vue.use(Element)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue