mirror of https://github.com/bjdgyc/anylink.git
更改目录结构
This commit is contained in:
parent
3464d1d10e
commit
0f91c779e3
|
@ -1,4 +1,5 @@
|
||||||
ignore:
|
ignore:
|
||||||
- "conf"
|
- "screenshot"
|
||||||
- "down_files"
|
- "web"
|
||||||
- "screenshot"
|
- "server/conf"
|
||||||
|
- "server/files"
|
|
@ -13,26 +13,29 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.15
|
go-version: 1.15
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Switch path
|
||||||
uses: actions/checkout@v2
|
run: cd server
|
||||||
|
|
||||||
- name: Get dependencies
|
- name: Check out code into the Go module directory
|
||||||
run: |
|
uses: actions/checkout@v2
|
||||||
go get -v -t -d ./...
|
|
||||||
|
|
||||||
- name: Build
|
- name: Get dependencies
|
||||||
run: |
|
run: |
|
||||||
go build -v -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
go get -v -t -d ./...
|
||||||
./anylink -rev
|
|
||||||
|
- name: Build
|
||||||
- name: Test coverage
|
run: |
|
||||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic -v ./...
|
go build -v -o anylink -ldflags "-X main.COMMIT_ID=`git rev-parse HEAD`"
|
||||||
|
./anylink -rev
|
||||||
- name: Upload coverage to Codecov
|
|
||||||
run: bash <(curl -s https://codecov.io/bash)
|
- name: Test coverage
|
||||||
|
run: go test -race -coverprofile=coverage.txt -covermode=atomic -v ./...
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
run: bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
|
@ -1,19 +1,3 @@
|
||||||
# Binaries for programs and plugins
|
# 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/
|
.idea/
|
||||||
anylink
|
anylink-deploy
|
||||||
|
|
|
@ -34,7 +34,7 @@ AnyLink 服务端仅在CentOS 7、Ubuntu 18.04测试通过,如需要安装在
|
||||||
git clone https://github.com/bjdgyc/anylink.git
|
git clone https://github.com/bjdgyc/anylink.git
|
||||||
|
|
||||||
cd anylink
|
cd anylink
|
||||||
sh deploy.sh
|
sh build.sh
|
||||||
|
|
||||||
# 注意使用root权限运行
|
# 注意使用root权限运行
|
||||||
cd anylink-deploy
|
cd anylink-deploy
|
||||||
|
@ -73,7 +73,7 @@ sudo ./anylink -conf="conf/server.toml"
|
||||||
./anylink -secret
|
./anylink -secret
|
||||||
```
|
```
|
||||||
|
|
||||||
[conf/server.toml](https://github.com/bjdgyc/anylink/blob/master/conf/server.toml)
|
[conf/server.toml](server/conf/server.toml)
|
||||||
|
|
||||||
## Setting
|
## 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 (
|
const (
|
||||||
APP_NAME = "AnyLink"
|
APP_NAME = "AnyLink"
|
||||||
APP_VER = "0.1.5"
|
APP_VER = "0.1.6"
|
||||||
)
|
)
|
|
@ -16,13 +16,14 @@ type User struct {
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
// Password string `json:"password"`
|
// Password string `json:"password"`
|
||||||
PinCode string `json:"pin_code"`
|
PinCode string `json:"pin_code"`
|
||||||
OtpSecret string `json:"otp_secret"`
|
OtpSecret string `json:"otp_secret"`
|
||||||
Groups []string `json:"groups"`
|
DisableOtp bool `json:"disable_otp"` // 禁用otp
|
||||||
Status int8 `json:"status"` // 1正常
|
Groups []string `json:"groups"`
|
||||||
SendEmail bool `json:"send_email"`
|
Status int8 `json:"status"` // 1正常
|
||||||
CreatedAt time.Time `json:"created_at"`
|
SendEmail bool `json:"send_email"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetUser(v *User) error {
|
func SetUser(v *User) error {
|
||||||
|
@ -39,7 +40,7 @@ func SetUser(v *User) error {
|
||||||
v.PinCode = planPass
|
v.PinCode = planPass
|
||||||
|
|
||||||
if v.OtpSecret == "" {
|
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信息
|
// 判断otp信息
|
||||||
otp := pwd[pl-6:]
|
if !v.DisableOtp {
|
||||||
if !checkOtp(name, otp) {
|
pwd = pwd[:pl-6]
|
||||||
return fmt.Errorf("%s %s", name, "动态码错误")
|
otp := pwd[pl-6:]
|
||||||
}
|
if !checkOtp(name, otp, v.OtpSecret) {
|
||||||
totp := gotp.NewDefaultTOTP(v.OtpSecret)
|
return fmt.Errorf("%s %s", name, "动态码错误")
|
||||||
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, "密码错误")
|
return fmt.Errorf("%s %s", name, "密码错误")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,18 +124,23 @@ func init() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 令牌只能使用一次
|
// 判断令牌信息
|
||||||
func checkOtp(username, otp string) bool {
|
func checkOtp(name, otp, secret string) bool {
|
||||||
key := fmt.Sprintf("%s:%s", username, otp)
|
key := fmt.Sprintf("%s:%s", name, otp)
|
||||||
|
|
||||||
userOtpMux.Lock()
|
userOtpMux.Lock()
|
||||||
defer userOtpMux.Unlock()
|
defer userOtpMux.Unlock()
|
||||||
|
|
||||||
|
// 令牌只能使用一次
|
||||||
if _, ok := userOtp[key]; ok {
|
if _, ok := userOtp[key]; ok {
|
||||||
// 已经存在
|
// 已经存在
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
userOtp[key] = time.Now()
|
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