asd123a20 il y a 4 ans
Parent
commit
32f36b5b8f
56 fichiers modifiés avec 15269 ajouts et 0 suppressions
  1. 3 0
      .browserslistrc
  2. 5 0
      .editorconfig
  3. 1 0
      .env
  4. 17 0
      .eslintrc.js
  5. 23 0
      .gitignore
  6. 24 0
      README.md
  7. 5 0
      babel.config.js
  8. 1 0
      client/css/app.fba0a0ac.css
  9. 1 0
      client/css/chunk-vendors.c470e980.css
  10. BIN
      client/favicon.ico
  11. BIN
      client/fonts/element-icons.535877f5.woff
  12. BIN
      client/fonts/element-icons.732389de.ttf
  13. BIN
      client/img/bg.4f6e6589.jpg
  14. BIN
      client/img/logo.48e50617.jpg
  15. 1 0
      client/index.html
  16. 1 0
      client/js/app.aa1e9780.js
  17. 33 0
      client/js/chunk-vendors.d4475822.js
  18. 12681 0
      package-lock.json
  19. 36 0
      package.json
  20. 51 0
      plub/axios.js
  21. 75 0
      plub/menu.js
  22. BIN
      public/favicon.ico
  23. 17 0
      public/index.html
  24. 1 0
      serve/css/app.fba0a0ac.css
  25. 1 0
      serve/css/chunk-vendors.c470e980.css
  26. BIN
      serve/favicon.ico
  27. BIN
      serve/fonts/element-icons.535877f5.woff
  28. BIN
      serve/fonts/element-icons.732389de.ttf
  29. BIN
      serve/img/bg.4f6e6589.jpg
  30. BIN
      serve/img/logo.48e50617.jpg
  31. 1 0
      serve/index.html
  32. 1 0
      serve/js/app.592703ac.js
  33. 33 0
      serve/js/chunk-vendors.d4475822.js
  34. 14 0
      src/App.vue
  35. BIN
      src/assets/bg.jpg
  36. BIN
      src/assets/logo.jpg
  37. 13 0
      src/main.js
  38. 116 0
      src/router/index.js
  39. 286 0
      src/store/index.js
  40. 64 0
      src/views/advancedConfig.vue
  41. 106 0
      src/views/cert/cacert.vue
  42. 244 0
      src/views/cert/cert.vue
  43. 105 0
      src/views/devinfo.vue
  44. 56 0
      src/views/home.vue
  45. 99 0
      src/views/log.vue
  46. 100 0
      src/views/login.vue
  47. 128 0
      src/views/network/lan.vue
  48. 125 0
      src/views/network/wan.vue
  49. 66 0
      src/views/systemctl/date.vue
  50. 64 0
      src/views/systemctl/ping.vue
  51. 62 0
      src/views/systemctl/systemctl.vue
  52. 142 0
      src/views/vpn/ipsecservicevpn.vue
  53. 135 0
      src/views/vpn/ipsecvpn.vue
  54. 166 0
      src/views/vpn/sslservicevpn.vue
  55. 145 0
      src/views/vpn/sslvpn.vue
  56. 21 0
      vue.config.js

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead

+ 5 - 0
.editorconfig

@@ -0,0 +1,5 @@
+[*.{js,jsx,ts,tsx,vue}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true

+ 1 - 0
.env

@@ -0,0 +1 @@
+VUE_APP_END=1

+ 17 - 0
.eslintrc.js

@@ -0,0 +1,17 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  extends: [
+    'plugin:vue/essential',
+    '@vue/standard'
+  ],
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  rules: {
+    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+  }
+}

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# 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?

+ 24 - 0
README.md

@@ -0,0 +1,24 @@
+# vpnweb
+
+## 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/).

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
client/css/app.fba0a0ac.css


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
client/css/chunk-vendors.c470e980.css


BIN
client/favicon.ico


BIN
client/fonts/element-icons.535877f5.woff


BIN
client/fonts/element-icons.732389de.ttf


BIN
client/img/bg.4f6e6589.jpg


BIN
client/img/logo.48e50617.jpg


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
client/index.html


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
client/js/app.aa1e9780.js


Fichier diff supprimé car celui-ci est trop grand
+ 33 - 0
client/js/chunk-vendors.d4475822.js


Fichier diff supprimé car celui-ci est trop grand
+ 12681 - 0
package-lock.json


+ 36 - 0
package.json

@@ -0,0 +1,36 @@
+{
+  "name": "vpnweb",
+  "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.21.1",
+    "core-js": "^3.6.5",
+    "element-ui": "^2.15.1",
+    "vue": "^2.6.11",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-eslint": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "@vue/eslint-config-standard": "^5.1.2",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-promise": "^4.2.1",
+    "eslint-plugin-standard": "^4.0.0",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "vue-template-compiler": "^2.6.11"
+  }
+}

+ 51 - 0
plub/axios.js

@@ -0,0 +1,51 @@
+/* eslint-disable no-const-assign */
+import axios from 'axios'
+import router from '../src/router/index'
+import { Message } from 'element-ui'
+// 构建axios实例
+axios.create({
+  baseURL: process.env.BASE_API,
+  timeout: 10000
+})
+axios.interceptors.request.use(config => {
+  const token = sessionStorage.getItem('token')
+  if (token) {
+    config.headers.Authorization = 'Bearer ' + token
+  }
+  return config
+},
+err => {
+  return Promise.reject(err)
+})
+axios.interceptors.response.use(
+  response => {
+    if (response.data.errcode !== 0 || response.data.errcode === 403) {
+      if (response.data.errmsg.cmd) {
+        Message.error(response.data.errmsg.cmd)
+        return false
+      }
+      Message.error(response.data.errmsg)
+      return false
+    }
+    return response
+  },
+  error => {
+    const { status, data } = error.response
+    console.log(error.response)
+    if (status === 401) {
+      Message.error('请重新登录')
+      router.push('/login')
+      return false
+    }
+    if (status === 400) {
+      Message.error(data.message || data.errmsg)
+      return false
+    }
+    if (status === 500) {
+      Message.error('系统错误')
+      return false
+    }
+    return data
+  }
+)
+export default axios

+ 75 - 0
plub/menu.js

@@ -0,0 +1,75 @@
+export default [
+  {
+    name: '网络配置',
+    children: [
+      {
+        name: 'WAN配置',
+        path: '/advancedConfig/wan'
+      },
+      {
+        name: 'LAN配置',
+        path: '/advancedConfig/lan'
+      }
+    ]
+  },
+  {
+    name: '系统管理',
+    children: [
+      {
+        name: '系统设置',
+        path: '/advancedConfig/systemctl'
+      },
+      {
+        name: '时间设置',
+        path: '/advancedConfig/date'
+      },
+      {
+        name: '网络工具',
+        path: '/advancedConfig/ping'
+      }
+    ]
+  },
+  {
+    name: '证书管理',
+    children: [
+      {
+        name: '证书链管理',
+        path: '/advancedConfig/cacert'
+      },
+      {
+        name: '设备证书管理',
+        path: '/advancedConfig/devcert'
+      }
+    ]
+  },
+  {
+    name: 'VPN管理',
+    children: [
+      {
+        name: 'SSLVPN',
+        path: '/advancedConfig/sslvpn'
+      },
+      {
+        name: 'IPSecVpn',
+        path: '/advancedConfig/ipsecvpn'
+      }
+    ]
+  },
+  {
+    name: '日志管理',
+    children: [
+      {
+        name: '系统日志',
+        path: '/advancedConfig/log/system'
+      },
+      {
+        name: 'SSLVPN日志',
+        path: '/advancedConfig/log/sslvpn'
+      },
+      {
+        name: 'IPSecVpn日志',
+        path: '/advancedConfig/log/ipsecvpn'
+      }
+    ]
+  }
+]

BIN
public/favicon.ico


+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+  <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><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> 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>

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
serve/css/app.fba0a0ac.css


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
serve/css/chunk-vendors.c470e980.css


BIN
serve/favicon.ico


BIN
serve/fonts/element-icons.535877f5.woff


BIN
serve/fonts/element-icons.732389de.ttf


BIN
serve/img/bg.4f6e6589.jpg


BIN
serve/img/logo.48e50617.jpg


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
serve/index.html


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
serve/js/app.592703ac.js


Fichier diff supprimé car celui-ci est trop grand
+ 33 - 0
serve/js/chunk-vendors.d4475822.js


+ 14 - 0
src/App.vue

@@ -0,0 +1,14 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<style lang="less">
+html, body {
+  padding: 0;
+  margin: 0;
+  height: 100vh;
+  overflow: hidden;
+}
+</style>

BIN
src/assets/bg.jpg


BIN
src/assets/logo.jpg


+ 13 - 0
src/main.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+Vue.config.productionTip = false
+Vue.use(ElementUI)
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app')

+ 116 - 0
src/router/index.js

@@ -0,0 +1,116 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import login from '../views/login.vue'
+import home from '../views/home.vue'
+import devinfo from '../views/devinfo.vue'
+import advancedConfig from '../views/advancedConfig.vue'
+import wan from '../views/network/wan.vue'
+import lan from '../views/network/lan.vue'
+import systemctl from '../views/systemctl/systemctl.vue'
+import date from '../views/systemctl/date.vue'
+import ping from '../views/systemctl/ping.vue'
+import log from '../views/log.vue'
+import cacert from '../views/cert/cacert.vue'
+import devcert from '../views/cert/cert.vue'
+import sslvpn from '../views/vpn/sslvpn.vue'
+import sslservicevpn from '../views/vpn/sslservicevpn.vue'
+import ipsecvpn from '../views/vpn/ipsecvpn.vue'
+import ipsecservicevpn from '../views/vpn/ipsecservicevpn.vue'
+const states = process.env.VUE_APP_END
+
+Vue.use(VueRouter)
+
+const routes = [
+  {
+    path: '/login',
+    name: 'login',
+    component: login
+  },
+  {
+    path: '/',
+    name: 'home',
+    redirect: '/devinfo',
+    component: home,
+    children: [
+      {
+        path: '/devinfo',
+        name: 'devinfo',
+        component: devinfo
+      },
+      {
+        path: '/advancedConfig',
+        name: 'advancedConfig',
+        redirect: '/advancedConfig/wan',
+        component: advancedConfig,
+        children: [
+          {
+            path: 'wan',
+            name: 'wan',
+            component: wan
+          },
+          {
+            path: 'lan',
+            name: 'lan',
+            component: lan
+          },
+          {
+            path: 'systemctl',
+            name: 'systemctl',
+            component: systemctl
+          },
+          {
+            path: 'date',
+            name: 'date',
+            component: date
+          },
+          {
+            path: 'ping',
+            name: 'ping',
+            component: ping
+          },
+          {
+            path: 'log/:type',
+            name: 'log',
+            component: log
+          },
+          {
+            path: 'cacert',
+            name: 'cacert',
+            component: cacert
+          },
+          {
+            path: 'devcert',
+            name: 'devcert',
+            component: devcert
+          },
+          {
+            path: 'sslvpn',
+            name: 'sslvpn',
+            component: states === '0' ? sslvpn : sslservicevpn
+          },
+          {
+            path: 'ipsecvpn',
+            name: 'ipsecvpn',
+            component: states === '0' ? ipsecvpn : ipsecservicevpn
+          }
+        ]
+      }
+    ]
+  }
+]
+
+const router = new VueRouter({
+  mode: 'history',
+  base: process.env.BASE_URL,
+  routes
+})
+router.beforeEach((to, from, next) => {
+  if (to.path === from.path) return
+  // 此处判断登录状态  失效则退回登录页  清空登录状态
+  const token = sessionStorage.getItem('token')
+  if (!token && !/\/login$/.test(to.path)) {
+    next('/login')
+  }
+  next()
+})
+export default router

+ 286 - 0
src/store/index.js

@@ -0,0 +1,286 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import axios from '../../plub/axios'
+Vue.use(Vuex)
+const api = {
+  login: '/api/login',
+  devinfo: '/api/devinfo',
+  cpu: '/api/cpu',
+  memory: '/api/memory',
+  vpnstate: '/api/vpnstate',
+  // wna
+  wan: '/api/wanupdate',
+  wanquery: '/api/wanquery',
+  wanup: '/api/wanup',
+  wandown: '/api/wanDown',
+  // lan
+  lan: '/api/lanupdate',
+  lanquery: '/api/lanquery',
+  lanup: '/api/lanup',
+  landown: '/api/lanDown',
+  // 设备重启
+  reboot: '/api/reboot',
+  // 设置时间
+  setdate: '/api/setdate',
+  // 链接测试
+  ping: '/api/ping',
+  // 日志查询
+  logquery: '/api/logquery',
+  // ca证书
+  cadelete: '/api/cadelete',
+  caquery: '/api/caquery',
+  // 设备证书
+  sigcertdelete: '/api/sigcertdelete',
+  sigcacertquery: '/api/sigcacertquery',
+  sigcertreq: '/api/sigcertreq',
+  // sslvpn
+  sslvpnstate: '/api/sslvpnstate',
+  sslquery: '/api/sslquery',
+  sslserivcequery: '/api/sslserivcequery',
+  sslvpnclient: '/api/sslvpnclient',
+  sslvpnservice: '/api/sslvpnservice',
+  // ipsecvpn
+  ipsecvpnstate: '/api/ipsecvpnstate',
+  secclient: '/api/secclient',
+  secservice: '/api/secservice',
+  secclientquery: '/api/secclientquery',
+  ipsecservicequery: '/api/ipsecservicequery'
+}
+export default new Vuex.Store({
+  state: {
+    devinfo: {},
+    cpudata: {},
+    memorydata: {},
+    vpnstate: null,
+    wandata: null,
+    landata: null,
+    logdata: null,
+    cadata: [],
+    sigdata: [],
+    secdata: null,
+    ssldata: null
+  },
+  mutations: {
+    devinfo (state, payload) {
+      state.devinfo = payload.data
+    },
+    memory (state, payload) {
+      state.memorydata = payload.data
+    },
+    cpu (state, payload) {
+      state.cpudata = payload.data
+    },
+    vpnstate (state, payload) {
+      state.vpnstate = payload.data
+    },
+    wanquery (state, payload) {
+      state.wandata = payload.data
+    },
+    lanquery (state, payload) {
+      state.landata = payload.data
+    },
+    logquery (state, payload) {
+      state.logdata = payload.data
+    },
+    caquery (state, payload) {
+      state.cadata = payload.data
+    },
+    sigcacertquery (state, payload) {
+      state.sigdata = payload.data
+    },
+    sslquery (state, payload) {
+      state.ssldata = payload.data
+    },
+    sslserivcequery (state, payload) {
+      state.ssldata = payload.data
+    },
+    ipsecservicequery (state, payload) {
+      state.secdata = payload.data
+    },
+    secclientquery (state, payload) {
+      state.secdata = payload.data
+    }
+  },
+  actions: {
+    // 登录
+    async login ({ commit }, { userName, password }) {
+      const res = await axios.post(api.login, { userName, password })
+      return res.data
+    },
+    // 获取设备信息
+    async getdevinfo ({ commit }) {
+      const res = await axios.get(api.devinfo)
+      commit('devinfo', res.data)
+      return res.data
+    },
+    // 获取cpu使用状态
+    async getcpu ({ commit }) {
+      const res = await axios.get(api.cpu)
+      if (res) {
+        commit('cpu', res.data)
+        return res.data
+      }
+    },
+    // 获取内存使用状态
+    async getmemory ({ commit }) {
+      const res = await axios.get(api.memory)
+      if (res) {
+        commit('memory', res.data)
+        return res.data
+      }
+    },
+    // 获取vpn链接状态
+    async getvpnstate ({ commit }) {
+      const res = await axios.get(api.vpnstate)
+      if (res) {
+        commit('vpnstate', res.data)
+        return res.data
+      }
+    },
+    // 修改wan
+    async setwan ({ commit }, payload) {
+      const res = await axios.post(api.wan, payload)
+      return res.data
+    },
+    // 查询wan
+    async wanquery ({ commit }) {
+      const res = await axios.get(api.wanquery)
+      commit('wanquery', res.data)
+      return res.data
+    },
+    // 停止wan网卡
+    async wandown ({ commit }) {
+      const res = await axios.get(api.wandown)
+      return res.data
+    },
+    // 启动wan网卡
+    async wanup ({ commit }) {
+      const res = await axios.get(api.wanup)
+      return res.data
+    },
+    // 修改lan
+    async setlan ({ commit }, payload) {
+      const res = await axios.post(api.lan, payload)
+      return res.data
+    },
+    // 查询lan
+    async lanquery ({ commit }) {
+      const res = await axios.get(api.lanquery)
+      commit('wanquery', res.data)
+      return res.data
+    },
+    // 停止lan网卡
+    async landown ({ commit }) {
+      const res = await axios.get(api.landown)
+      return res.data
+    },
+    // 启动lan网卡
+    async lanup ({ commit }) {
+      const res = await axios.get(api.lanup)
+      return res.data
+    },
+    // 设备重启
+    async reboot ({ commit }) {
+      await axios.get(api.reboot)
+    },
+    // 设置时间
+    async setdate ({ commit }, payload) {
+      const res = await axios.post(api.setdate, payload)
+      return res.data
+    },
+    // 链接测试
+    async ping ({ commit }, payload) {
+      const res = await axios.post(api.ping, payload)
+      return res.data
+    },
+    // 日志查询
+    async logquery ({ commit }, { type } = {}) {
+      const res = await axios.get(`${api.logquery}?type=${type}`)
+      commit('logquery', res.data)
+      return res.data
+    },
+    // 删除ca证书
+    async cadelete ({ commit }, payload) {
+      const res = await axios.get(`${api.cadelete}?uuid=${payload.uuid}`)
+      return res.data
+    },
+    // ca证书查询
+    async caquery ({ commit }) {
+      const res = await axios.get(`${api.caquery}`)
+      commit('caquery', res.data)
+      return res.data
+    },
+    // 删除设备证书
+    async sigcertdelete ({ commit }, payload) {
+      const res = await axios.get(`${api.sigcertdelete}?uuid=${payload.uuid}`)
+      return res.data
+    },
+    // 设备证书查询
+    async sigcacertquery ({ commit }) {
+      const res = await axios.get(`${api.sigcacertquery}`)
+      commit('sigcacertquery', res.data)
+      return res.data
+    },
+    // 申请书
+    async sigcertreq ({ commit }, payload) {
+      const res = await axios.post(`${api.sigcertreq}`, payload)
+      return res.data
+    },
+    // 启动停止重启sslvpnstate
+    async sslvpnstate ({ commit }, { type } = {}) {
+      const res = await axios.get(`${api.sslvpnstate}?type=${type}`)
+      return res.data
+    },
+    // 查询sslvpn客户端
+    async sslquery ({ commit }) {
+      const res = await axios.get(`${api.sslquery}`)
+      commit('sslquery', res)
+      return res.data
+    },
+    // 查询sslvpn服务端
+    async sslserivcequery ({ commit }) {
+      const res = await axios.get(`${api.sslserivcequery}`)
+      commit('sslserivcequery', res)
+      return res.data
+    },
+    // sslvpn客户端添加
+    async sslvpnclient ({ commit }, payload) {
+      const res = await axios.post(`${api.sslvpnclient}`, payload)
+      return res.data
+    },
+    // sslvpn服务端添加
+    async sslvpnservice ({ commit }, payload) {
+      const res = await axios.post(`${api.sslvpnservice}`, payload)
+      return res.data
+    },
+    // 启动停止重启ipsecvpnstate
+    async ipsecvpnstate ({ commit }, { type } = {}) {
+      const res = await axios.get(`${api.ipsecvpnstate}?type=${type}`)
+      return res.data
+    },
+    // 查询ipsecvpn客户端
+    async secclientquery ({ commit }) {
+      const res = await axios.get(`${api.secclientquery}`)
+      commit('secclientquery', res)
+      return res.data
+    },
+    // 查询ipsecvpn服务端
+    async ipsecservicequery ({ commit }) {
+      const res = await axios.get(`${api.ipsecservicequery}`)
+      commit('ipsecservicequery', res)
+      return res.data
+    },
+    // ipsecvpn客户端添加
+    async secclient ({ commit }, payload) {
+      const res = await axios.post(`${api.secclient}`, payload)
+      return res.data
+    },
+    // ipsecvpn服务端添加
+    async secservice ({ commit }, payload) {
+      const res = await axios.post(`${api.secservice}`, payload)
+      return res.data
+    }
+  },
+  modules: {
+  }
+})

+ 64 - 0
src/views/advancedConfig.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h5>高级设置</h5>
+    </el-header>
+    <el-main class="main">
+      <el-menu :default-active="defaultActive" :unique-opened="true" class="el-menu-vertical-demo menu">
+        <el-submenu :index="String(index)" v-for="(item, index) in menu" :key="index">
+          <template slot="title">
+            <span>{{ item.name }}</span>
+          </template>
+          <el-menu-item @click="handleOpen(i)" v-for="(i, x) in item.children" :index="`${index}-${x}`" :key="x">{{ i.name }}</el-menu-item>
+        </el-submenu>
+      </el-menu>
+      <router-view class="view" />
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import menu from '../../plub/menu'
+export default {
+  name: 'advancedConfig',
+  components: {},
+  data () {
+    return {
+      menu,
+      defaultActive: '0-0'
+    }
+  },
+  mounted () {
+    // this.$router.push('advancedConfig/wan')
+  },
+  methods: {
+    handleOpen (e) {
+      this.$router.push(`${e.path}`)
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h5 {
+  width: 100%;
+  text-align: left;
+  text-indent: 1em;
+  border-bottom: 2px solid #999;
+  color: #999;
+  line-height: 3em;
+  margin: 0;
+}
+.main {
+  display: flex;
+  height: 73vh;
+  overflow: hidden;
+}
+.menu {
+  width: 20%;
+  height: 100%;
+}
+.view {
+  width: 80%;
+  height: 100%;
+}
+</style>

+ 106 - 0
src/views/cert/cacert.vue

@@ -0,0 +1,106 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>证书链管理</h3>
+    </el-header>
+    <el-main class="main">
+      <div class="btnbox">
+        <el-upload
+          ref="upload"
+          class="upload-demo"
+          action="/api/caupload"
+          :multiple="false"
+          :limit="1"
+          :on-success="handleSuccess"
+          :on-error="handleError"
+          :headers="{ Authorization: 'Bearer ' + token}"
+          :show-file-list="false">
+          <el-button size="small" type="text">上传证书</el-button>
+        </el-upload>
+      </div>
+      <el-table :data="cadata" border style="width: 100%">
+        <el-table-column label="DN" width="500">
+          <template slot-scope="scope">
+            <span style="margin-left: 10px">{{ scope.row.dn }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
+            <el-button size="mini" type="text" icon="el-icon-download" @click="certdw(scope.row)">下载证书</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex'
+const token = sessionStorage.getItem('token')
+export default {
+  name: 'cacert',
+  components: {},
+  data () {
+    return {
+      token
+    }
+  },
+  computed: {
+    ...mapState(['cadata'])
+  },
+  mounted () {
+    this.query()
+  },
+  methods: {
+    ...mapActions(['caquery', 'cadelete']),
+    async query () {
+      await this.caquery()
+    },
+    // 文件上传成功钩子
+    handleSuccess () {
+      this.$message.success('上传成功')
+      this.$refs.upload.clearFiles()
+      this.query()
+    },
+    // 上传失败钩子
+    handleError () {
+      this.$message.error('上传失败')
+      this.$refs.upload.clearFiles()
+    },
+    // 删除数据
+    async handleDelete (e) {
+      const res = await this.cadelete({ uuid: e.uuid })
+      if (res.errcode === 0) {
+        this.$message.success('删除成功')
+        this.query()
+      }
+    },
+    // 下载ca证书
+    certdw (e) {
+      window.open(`/api/cacertdownload?uuid=${e.uuid}`)
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 70%;
+  margin: 0 auto;
+  .btnbox {
+    width: 100%;
+    overflow: hidden;
+    margin: 3% auto;
+    .upload-demo {
+      float: right;
+    }
+  }
+}
+</style>

+ 244 - 0
src/views/cert/cert.vue

@@ -0,0 +1,244 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>证书链管理</h3>
+    </el-header>
+    <el-main class="main">
+      <div class="btnbox">
+          <el-button size="small" class="upload-demo" type="text" @click="certs">上传证书密钥</el-button>
+          <el-button size="small" class="upload-demo" type="text" @click="reqs">生成申请书</el-button>
+      </div>
+      <el-table :data="sigdata" border style="width: 100%">
+        <el-table-column label="名称" width="200">
+          <template slot-scope="scope">
+            <span style="margin-left: 10px">{{ scope.row.name }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="DN" width="400">
+          <template slot-scope="scope">
+            <span style="margin-left: 10px">{{ scope.row.dn }}</span>
+          </template>
+        </el-table-column>
+         <el-table-column label="算法类型" width="200">
+          <template slot-scope="scope">
+            <span style="margin-left: 10px">{{ scope.row.pwatype }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作">
+          <template slot-scope="scope">
+            <div class="czbox">
+              <el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>
+              <el-button size="mini" type="text" v-if="scope.row.state == 0" @click="reqdw(scope.row)">下载申请书</el-button>
+              <el-upload
+                v-if="scope.row.state == 0"
+                class="upload-demo"
+                action="/api/sigcacertupload"
+                :multiple="false"
+                :limit="1"
+                :data="{ uuid: scope.row.uuid }"
+                :on-success="handleSuccess"
+                :on-error="handleError"
+                :headers="{ Authorization: 'Bearer ' + token}"
+                :show-file-list="false">
+                <el-button size="small" type="text">导入证书</el-button>
+              </el-upload>
+              <el-button v-if="scope.row.state == 1"  size="mini" type="text" @click="certdw(scope.row)">下载证书</el-button>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-main>
+    <!-- 弹窗 -->
+    <el-dialog :title="title" :visible.sync="dialogFormVisible">
+      <el-form :model="form" label-width="120px">
+        <el-form-item label="证书名称">
+          <el-input v-model="form.name" ></el-input>
+        </el-form-item>
+        <el-form-item v-if="type == 'cert'" label="加密密码">
+          <el-input v-model="form.password"></el-input>
+        </el-form-item>
+        <el-form-item v-if="type == 'req'" label="算法类型">
+          <el-radio v-model="form.pwatype" label="sm2">SM2</el-radio>
+          <el-radio v-model="form.pwatype" label="rsa">RSA</el-radio>
+        </el-form-item>
+        <el-form-item label="DN" v-if="type == 'req'">
+          <el-input v-model="form.dn" ></el-input>
+        </el-form-item>
+        <el-form-item v-if="type == 'cert'">
+          <el-upload
+            ref="upload"
+            class="upload-demo"
+            action="/api/enccertupload"
+            :multiple="false"
+            :limit="1"
+            :on-success="handleSuccess"
+            :on-error="handleError"
+            :data="{ password: form.password, name: form.name }"
+            :headers="{ Authorization: 'Bearer ' + token}"
+            :file-list="fileList"
+            :auto-upload="false">
+            <el-button size="small" type="primary">选择证书</el-button>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <!-- 提交按钮 -->
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogFormVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submit">提 交</el-button>
+      </div>
+    </el-dialog>
+  </el-container>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex'
+const token = sessionStorage.getItem('token')
+export default {
+  name: 'cert',
+  components: {},
+  data () {
+    return {
+      token,
+      dialogFormVisible: false,
+      title: null,
+      type: 'req',
+      form: {
+        pwatype: 'sm2',
+        name: null,
+        password: null,
+        dn: null
+      },
+      fileList: []
+    }
+  },
+  computed: {
+    ...mapState(['sigdata'])
+  },
+  mounted () {
+    this.query()
+  },
+  methods: {
+    ...mapActions(['sigcacertquery', 'sigcertdelete', 'sigcertreq']),
+    async query () {
+      await this.sigcacertquery()
+    },
+    // 文件上传成功钩子
+    handleSuccess () {
+      this.$message.success('上传成功')
+      this.query()
+      this.$refs.upload.clearFiles()
+      this.form = {
+        pwatype: 'sm2',
+        name: null,
+        password: null,
+        dn: null
+      }
+    },
+    // 上传失败钩子
+    handleError () {
+      this.$message.error('上传失败')
+    },
+    // 删除数据
+    async handleDelete (e) {
+      const res = await this.sigcertdelete({ uuid: e.uuid })
+      if (res.errcode === 0) {
+        this.$message.success('删除成功')
+        this.query()
+      }
+    },
+    // 下载证书
+    certdw (e) {
+      window.open(`/api/sigcertdownload?uuid=${e.uuid}`)
+    },
+    // 下载申请书
+    reqdw (e) {
+      window.open(`/api/reqdownload?uuid=${e.uuid}`)
+    },
+    // 上传加密证书
+    certs () {
+      this.title = '上传证书密钥'
+      this.type = 'cert'
+      this.dialogFormVisible = true
+    },
+    // 生成申请书
+    reqs () {
+      this.title = '生成申请书'
+      this.type = 'req'
+      this.dialogFormVisible = true
+    },
+    // 表单提交
+    async submit () {
+      if (this.type === 'cert') {
+        // 上传p12
+        if (this.form.password == null) {
+          this.$message.error('请填写密码')
+          return false
+        }
+        if (this.form.name == null) {
+          this.$message.error('请填写名称')
+          return false
+        }
+        if (this.$refs.upload.uploadFiles >= 0) {
+          this.$message.error('请选择证书')
+          return false
+        }
+        this.$refs.upload.submit()
+      } else {
+        // 生成申请书
+        if (this.form.pwatype == null) {
+          this.$message.error('请选择算法类型')
+          return false
+        }
+        if (this.form.name == null) {
+          this.$$message.error('请填写名称')
+          return false
+        }
+        if (this.form.dn == null) {
+          this.$message.error('请填写DN')
+          return false
+        }
+        const res = await this.sigcertreq(this.form)
+        if (res.errcode === 0) {
+          this.$message.success('生成成功')
+          this.query()
+          this.form = {
+            pwatype: 'sm2',
+            name: null,
+            password: null,
+            dn: null
+          }
+          this.dialogFormVisible = false
+        }
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 90%;
+  margin: 0 auto;
+  .btnbox {
+    width: 100%;
+    overflow: hidden;
+    margin: 3% auto;
+    .upload-demo {
+      float: right;
+      margin-left: 1%;
+    }
+  }
+  /deep/ .czbox {
+    display: flex;
+    .el-button {
+      margin-left: 8px;
+    }
+  }
+}
+</style>

+ 105 - 0
src/views/devinfo.vue

@@ -0,0 +1,105 @@
+<template>
+  <el-container>
+    <el-main class="main">
+      <h5>设备详情</h5>
+      <div class="info">
+        <h3>设备信息:</h3>
+        <div class="input_info">
+          <el-input disabled :value="devinfos.model">
+              <template slot="prepend">产品型号</template>
+          </el-input>
+          <el-input disabled  :value="devinfos.version">
+              <template slot="prepend">软件版本</template>
+          </el-input>
+        </div>
+      </div>
+      <div class="usex">
+        <h3>状态监控:</h3>
+        <div class="box">
+          <div class="item">
+            <span class="span1">CPU使用率</span>
+            <span>{{ cpudata && cpudata.cpu }}%</span>
+          </div>
+          <div class="item">
+            <span class="span1">内存使用率</span>
+            <span>{{ memorydata && memorydata.memory }}%</span>
+          </div>
+          <div class="item">
+            <span class="span1">VPN链接状态</span>
+            <span>{{ vpnstate }}</span>
+          </div>
+        </div>
+      </div>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'systemctl',
+  components: {},
+  computed: {
+    ...mapState(['devinfo', 'cpudata', 'vpnstate', 'memorydata']),
+    devinfos () {
+      return { model: this.devinfo?.model, version: this.devinfo?.version }
+    }
+  },
+  data () {
+    return {
+      kib: null
+    }
+  },
+  async mounted () {
+    await this.getdevinfo()
+    await this.getvpnstate()
+    await this.getcpu()
+    await this.getmemory()
+  },
+  methods: {
+    ...mapActions(['getdevinfo', 'getvpnstate', 'getcpu', 'getmemory'])
+  }
+}
+</script>
+<style lang="less" scoped>
+h5 {
+    width: 100%;
+    text-align: left;
+    text-indent: 1em;
+    border-bottom: 2px solid #999;
+    color: #999;
+    line-height: 3em;
+    margin: 0;
+}
+.info {
+    width: 90%;
+    margin: 3% auto;
+    border-bottom: 1px solid #999;
+}
+.input_info {
+  width: 90%;
+  display: flex;
+  margin: 3% auto;
+}
+.usex {
+  width: 90%;
+  margin: 3% auto;
+}
+.box {
+  width: 100%;
+  display: flex;
+  margin-top: 5%;
+  .item {
+    width: 33.3%;
+    span {
+      text-align: center;
+      width: 100%;
+      display: block;
+      line-height: 2em;
+    }
+    .span1 {
+      color: #999;
+    }
+  }
+}
+</style>

+ 56 - 0
src/views/home.vue

@@ -0,0 +1,56 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <img src="../assets/logo.jpg" alt="logo" class="logo">
+      <el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" :active-text-color="'#409EFF'" @select="handleSelect">
+        <el-menu-item index="1">设备详情</el-menu-item>
+        <el-menu-item index="2">高级配置</el-menu-item>
+      </el-menu>
+    </el-header>
+    <el-main class="main">
+      <router-view/>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+// import { mapActions } from 'vuex'
+// mapState
+export default {
+  name: 'home',
+  components: {},
+  data () {
+    return {
+      activeIndex: '1'
+    }
+  },
+  mounted () {},
+  methods: {
+    handleSelect (key, keyPath) {
+      if (key === '1' && this.$route.path !== '/devinfo') {
+        this.$router.push({ path: '/devinfo' })
+      }
+      if (key === '2' && this.$route.path !== '/advancedConfig') {
+        this.$router.push({ path: '/advancedConfig' })
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.header {
+  display: flex;
+  margin-top: 1%;
+}
+.logo{
+  width: 15%;
+  height: 100%;
+  margin: 0 5%;
+}
+.main {
+  width: 90%;
+  height: 85vh;
+  border: 1px solid #000;
+  margin: 2% auto;
+}
+</style>

+ 99 - 0
src/views/log.vue

@@ -0,0 +1,99 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>{{ title }}</h3>
+    </el-header>
+    <el-main class="main">
+      <div class="btnbox">
+        <el-button type="primary" @click="logdw">下载日志</el-button>
+        <el-button type="primary" @click="query">刷新日志</el-button>
+      </div>
+      <div class="per">{{ logdata }}</div>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapActions, mapState } from 'vuex'
+export default {
+  name: 'log',
+  components: {},
+  data () {
+    return {
+      title: null,
+      type: null
+    }
+  },
+  computed: {
+    ...mapState(['logdata'])
+  },
+  mounted () {
+    const type = this.$route.params.type
+    this.parms(type)
+  },
+  methods: {
+    ...mapActions(['logquery']),
+    logdw () {
+      window.open(`/api/logdownload?type=${this.type}`)
+    },
+    async query () {
+      await this.logquery({ type: this.type })
+    },
+    parms (type) {
+      switch (type) {
+        case 'system':
+          this.title = '系统日志'
+          this.type = 'systemct'
+          break
+        case 'sslvpn':
+          this.title = 'SSLVPN日志'
+          this.type = 'ssl'
+          break
+        case 'ipsecvpn':
+          this.title = 'IPSecVPN日志'
+          this.type = 'sec'
+          break
+      }
+      this.query()
+    }
+  },
+  watch: {
+    $route: {
+      handler () {
+        const type = this.$route.params.type
+        this.parms(type)
+      },
+      deep: true
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 70%;
+  margin: 0 auto;
+  .per {
+    width: 100%;
+    height: 85%;
+    margin: 0 auto;
+    border: 1px solid #999;
+    overflow-y: auto;
+  }
+  .btnbox {
+    margin: 2% 0;
+    width: 100%;
+    overflow: hidden;
+    .el-button {
+      float: right;
+      margin-left: 2%;
+    }
+  }
+}
+</style>

+ 100 - 0
src/views/login.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="login">
+    <div class="input_box">
+      <img src="../assets/logo.jpg" alt="logo" class="logo">
+      <div class="right">
+        <el-form  class="elform">
+          <el-form-item>
+           <h2>安全接入网关</h2>
+          </el-form-item>
+          <el-form-item>
+            <el-input v-model="userName" placeholder="请输入用户名" />
+          </el-form-item>
+          <el-form-item>
+            <el-input v-model="password" type="password" placeholder="请输入密码" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="success" class="submit" @click="onSubmit">登录</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapActions } from 'vuex'
+// mapState
+export default {
+  name: 'login',
+  components: {},
+  data () {
+    return {
+      userName: null,
+      password: null
+    }
+  },
+  methods: {
+    ...mapActions(['login']),
+    async onSubmit () {
+      if (this.userName === null) {
+        this.$message.error('请输入用户名')
+        return false
+      }
+      if (this.password === null) {
+        this.$message.error('请输入密码')
+        return false
+      }
+      const res = await this.login({ userName: this.userName, password: this.password })
+      if (res.errcode === 0) {
+        sessionStorage.setItem('token', res.token)
+        this.$router.push({ path: '/home/devinfo' })
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+  .login {
+    width: 100%;
+    height: 100vh;
+    background: url('../assets/bg.jpg') no-repeat;
+    background-size: 100%, 100%;
+    position: relative;
+  }
+  .input_box {
+    width: 40%;
+    height: 40vh;
+    background: #fff;
+    display: flex;
+    position: absolute;
+    top: 30vh;
+    left: 30%;
+    border-radius: 12px;
+  }
+  .logo {
+    width: 40%;
+    height: 20%;
+    margin-top: 20%;
+    margin-left: 5%;
+  }
+  .right{
+    width: 50%;
+    height: 100%;
+    margin-left: 5%;
+  }
+  .elform {
+    width: 80%;
+    margin: 0 auto;
+    margin-top: 15%;
+  }
+  .submit {
+    width: 100%;
+  }
+  h2 {
+    width: 100%;
+    text-align: center;
+    color: #4660f1;
+    margin: 0;
+  }
+</style>

+ 128 - 0
src/views/network/lan.vue

@@ -0,0 +1,128 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>LAN配置</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot"
+          >重启网卡</el-button
+        >
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px" class="demo-ruleForm">
+        <el-form-item label="IP地址" prop="address">
+          <el-input v-model="ruleForm.address" placeholder="请输入IP地址"></el-input>
+        </el-form-item>
+        <el-form-item label="DHCP服务" prop="staticip">
+          <el-switch v-model="ruleForm.type" :active-value="'0'" :inactive-value="'1'" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
+        </el-form-item>
+        <el-form-item class="items" v-if="ruleForm.type == '0'" label="IP地址分配范围">
+          <span>{{ ruleForm.address | addresstop }}</span>
+          <el-input v-model="ruleForm.start"></el-input>
+          <span>——</span>
+          <el-input v-model="ruleForm.end"></el-input>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'lan',
+  components: {},
+  computed: {
+    ...mapState(['landata'])
+  },
+  data () {
+    return {
+      ruleForm: {
+        type: '0',
+        address: '192.168.4.1'
+      },
+      rules: {
+        address: [
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        netmask: [
+          { pattern: /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/, message: '请输入正确掩码' }
+        ],
+        gateway: [
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确网关' }
+        ]
+      }
+    }
+  },
+  filters: {
+    addresstop (e) {
+      const index = e.lastIndexOf('.')
+      const text = e.slice(0, index + 1)
+      return text
+    }
+  },
+  async mounted () {
+    const res = await this.lanquery()
+    if (res && res.errcode === 0) {
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['setlan', 'landown', 'lanup', 'lanquery']),
+    // 提交数据
+    async onSubmit () {
+      const res = await this.setlan(this.ruleForm)
+      if (res && res.errcode === 0) {
+        this.$message.success('设置成功')
+      } else {
+        this.$message.success('设置失败')
+      }
+    },
+    // 重启网卡  (先调用停止,在调用启动)
+    async reboot () {
+      const dw = await this.landown()
+      if (dw.errcode === 0) {
+        const up = await this.lanup()
+        if (up.errcode === 0) {
+          this.$message.success('重启成功')
+        }
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 5% auto;
+  /deep/ .el-form-item .el-form-item__content .select {
+    width: 100%;
+  }
+}
+.items /deep/ .el-form-item__content {
+  display: flex;
+  .el-input {
+    display: block;
+    width: 25%;
+    margin: 0 2%;
+  }
+}
+</style>

+ 125 - 0
src/views/network/wan.vue

@@ -0,0 +1,125 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>WAN配置</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot">重启网卡</el-button>
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
+        <el-form-item label="上网方式" prop="type">
+          <el-select class="select" @change="typeChage" v-model="ruleForm.type" placeholder="请选择上网方式">
+            <el-option label="自动获取IP" :value="'0'"></el-option>
+            <el-option label="手动获取IP" :value="'1'"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="ruleForm.type == '0'" label="静态DNS" prop="staticip">
+          <el-switch v-model="ruleForm.staticip" :active-value="'0'" :inactive-value="'1'" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
+        </el-form-item>
+        <el-form-item v-if="ruleForm.type == '1'" label="IP地址" prop="address">
+          <el-input v-model="ruleForm.address"></el-input>
+        </el-form-item>
+        <el-form-item v-if="ruleForm.type == '1'" label="子网掩码" prop="netmask">
+          <el-input v-model="ruleForm.netmask"></el-input>
+        </el-form-item>
+        <el-form-item v-if="ruleForm.type == '1'" label="网关" prop="gateway">
+          <el-input v-model="ruleForm.gateway"></el-input>
+        </el-form-item>
+        <el-form-item v-if="ruleForm.staticip == '0'" label="DNS地址" prop="dns">
+          <el-input v-model="ruleForm.dns"></el-input>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'wan',
+  components: {},
+  computed: {
+    ...mapState(['wandata'])
+  },
+  data () {
+    return {
+      ruleForm: {
+        type: '0',
+        staticip: '0'
+      },
+      rules: {
+        address: [
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        netmask: [
+          { pattern: /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/, message: '请输入正确掩码' }
+        ],
+        gateway: [
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确网关' }
+        ]
+      }
+    }
+  },
+  async mounted () {
+    const res = await this.wanquery()
+    if (res && res.errcode === 0) {
+      console.log(res)
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['setwan', 'wandown', 'wanup', 'wanquery']),
+    // type类型改变
+    typeChage () {
+      this.ruleForm.staticip = 0
+    },
+    // 提交数据
+    async onSubmit () {
+      const res = await this.setwan(this.ruleForm)
+      if (res.errcode && res.errcode === 0) {
+        this.$message.success('设置成功')
+      } else {
+        this.$message.success('设置成功')
+      }
+    },
+    // 重启网卡  (先调用停止,在调用启动)
+    reboot () {
+      const dw = this.wandown()
+      if (dw.errcode === 0) {
+        const up = this.wanup()
+        if (up.errcode === 0) {
+          this.$message.success('重启成功')
+        }
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 5% auto;
+  /deep/ .el-form-item .el-form-item__content .select {
+    width: 100%;
+  }
+}
+</style>

+ 66 - 0
src/views/systemctl/date.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>系统设置</h3>
+    </el-header>
+    <el-main class="main">
+      <el-form ref="form" :model="form" label-width="150px">
+        <el-form-item label="VPN服务器同步开关:">
+          <el-switch v-model="form.type" :active-value="'0'" :inactive-value="'1'" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
+        </el-form-item>
+        <el-form-item label="服务器地址:" v-if="form.type == '0'">
+          <el-input v-model="form.ip" />
+        </el-form-item>
+        <el-form-item label="服务器地址:" v-else>
+          <el-date-picker v-model="form.date" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间"></el-date-picker>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="syncdate" v-if="form.type == '0'">执行同步</el-button>
+          <el-button type="primary" @click="setupdate" v-else>保存</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapActions } from 'vuex'
+export default {
+  name: 'date',
+  components: {},
+  data () {
+    return {
+      form: {}
+    }
+  },
+  mounted () {},
+  methods: {
+    ...mapActions(['setdate']),
+    // 同步时间
+    syncdate () {
+      console.log('同步时间')
+    },
+    // 设置时间
+    async setupdate () {
+      console.log(this.form)
+      const res = await this.setdate(this.form)
+      if (res && res.errcode === 0) {
+        this.$message.success('设置成功')
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 80%;
+  margin: 5% auto;
+}
+</style>

+ 64 - 0
src/views/systemctl/ping.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>网络工具</h3>
+    </el-header>
+    <el-main class="main">
+      <div class="box">
+        <span>VPN链接目标IP:</span>
+        <el-input v-model="ip" />
+        <el-button type="primary" @click="linkTest">测试链接</el-button>
+      </div>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapActions } from 'vuex'
+// mapState
+export default {
+  name: 'ping',
+  components: {},
+  data () {
+    return {
+      ip: null
+    }
+  },
+  mounted () {},
+  methods: {
+    ...mapActions(['ping']),
+    async linkTest () {
+      const res = await this.ping({ address: this.ip })
+      if (res.errcode === 0) {
+        this.$message.success('链接成功')
+      } else {
+        this.$message.error(res.errmsg)
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 70%;
+  margin: 10% auto;
+  .box {
+    display: flex;
+    span {
+      width: 25%;
+      color: #666;
+      line-height: 2em;
+    }
+    /deep/ .el-input {
+      margin-right: 5%;
+    }
+  }
+}
+</style>

+ 62 - 0
src/views/systemctl/systemctl.vue

@@ -0,0 +1,62 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <h3>系统设置</h3>
+    </el-header>
+    <el-main class="main">
+      <div>
+        <span>恢复出厂设置</span>
+        <el-button type="primary">执行恢复</el-button>
+      </div>
+      <div>
+        <span>设备重启</span>
+        <el-button type="primary" @click="rebootClick">执行重启</el-button>
+      </div>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapActions } from 'vuex'
+// mapState
+export default {
+  name: 'systemctl',
+  components: {},
+  data () {
+    return {}
+  },
+  mounted () {},
+  methods: {
+    ...mapActions(['reboot']),
+    rebootClick () {
+      this.reboot()
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+  line-height: 3em;
+  border-bottom: 2px solid #999;
+}
+.main {
+  width: 70%;
+  margin: 10% auto;
+  display: flex;
+  div {
+    width: 50%;
+    span {
+      display: block;
+      color: #999;
+      text-align: center;
+    }
+    /deep/ .el-button {
+      display: block;
+      margin: 5% auto;
+    }
+  }
+}
+</style>

+ 142 - 0
src/views/vpn/ipsecservicevpn.vue

@@ -0,0 +1,142 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>IPSecVPN</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('start')">启动服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('stop')">停止服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('restart')">重启服务</el-button>
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :model="ruleForm" :rules="rules" ref="form" label-width="150px" class="demo-ruleForm">
+        <el-form-item label="本地子网地址" prop="address">
+            <el-input v-model="ruleForm.address"></el-input>
+        </el-form-item>
+        <el-form-item label="本地子网掩码长度" prop="digit">
+            <el-input v-model="ruleForm.digit"></el-input>
+        </el-form-item>
+        <el-form-item label="签名证书" prop="cert">
+            <el-select v-model="ruleForm.cert" placeholder="选择签名证书">
+            <el-option v-for="(item, index) in devitem" :label="item.name" :value="item.uuid" :key="index"></el-option>
+            </el-select>
+        </el-form-item>
+        <el-form-item label="加密证书" prop="pwa">
+            <el-select v-model="ruleForm.pwa" placeholder="选择加密证书">
+            <el-option v-for="(item, index) in devitem" :label="item.name" :value="item.uuid" :key="index"></el-option>
+            </el-select>
+        </el-form-item>
+        <el-form-item label="虚拟地址" prop="addressTow">
+            <el-input v-model="ruleForm.addressTow"></el-input>
+        </el-form-item>
+        <el-form-item label="虚拟地址掩码长度" prop="digitTow">
+            <el-input v-model="ruleForm.digitTow"></el-input>
+        </el-form-item>
+        <el-form-item label="日志级别" prop="loglevel">
+          <el-select v-model="ruleForm.loglevel" placeholder="请选择日志级别">
+            <el-option label="级别1" :value="1"></el-option>
+            <el-option label="级别2" :value="2"></el-option>
+            <el-option label="级别3" :value="3"></el-option>
+            <el-option label="级别4" :value="4"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'lan',
+  components: {},
+  computed: {
+    ...mapState(['cadata', 'sigdata', 'ssldata'])
+  },
+  data () {
+    return {
+      ruleForm: {},
+      rules: {
+        address: [
+          { required: true, message: '请输入内网地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        digit: [
+          { required: true, message: '请输入内网地址掩码长度', trigger: 'blur' }
+        ],
+        cert: [
+          { required: true, message: '请选择签名证书', trigger: 'chage' }
+        ],
+        addressTow: [
+          { required: true, message: '请输入虚拟地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        digitTow: [
+          { required: true, message: '请输入虚拟地址掩码长度', trigger: 'blur' }
+        ],
+        pwa: [
+          { required: true, message: '请选择加密证书', trigger: 'chage' }
+        ],
+        loglevel: [
+          { required: true, message: '请选择日志级别', trigger: 'chage' }
+        ]
+      }
+    }
+  },
+  async mounted () {
+    await this.caquery()
+    await this.sigcacertquery()
+    const res = this.ipsecservicequery()
+    if (res.errcode === 0) {
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['sigcacertquery', 'ipsecvpnstate', 'caquery', 'secservice', 'ipsecservicequery']),
+    // 提交数据
+    async onSubmit () {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) return false
+        const res = await this.secservice(this.ruleForm)
+        if (res.errcode === 0) {
+          this.$message.success('设置成功')
+        }
+      })
+    },
+    // 启动停止重启
+    async reboot (e) {
+      const dw = await this.ipsecvpnstate({ type: e })
+      if (dw.errcode === 0) {
+        this.$message.success('操作成功')
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 2% auto;
+  /deep/ .el-form-item .el-form-item__content .el-select {
+    width: 100%;
+  }
+}
+</style>

+ 135 - 0
src/views/vpn/ipsecvpn.vue

@@ -0,0 +1,135 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>IPSecVPN</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('start')">启动服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('stop')">停止服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('restart')">重启服务</el-button>
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :rules="rules" :model="ruleForm" ref="form" label-width="150px" class="demo-ruleForm" >
+        <el-form-item label="VPN服务器IP地址" prop="address">
+            <el-input v-model="ruleForm.address"></el-input>
+        </el-form-item>
+        <el-form-item label="签名证书" prop="cert">
+        <el-select v-model="ruleForm.cert" placeholder="选择签名证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+        </el-select>
+        </el-form-item>
+        <el-form-item label="加密证书" prop="pwa">
+          <el-select v-model="ruleForm.pwa" placeholder="选择加密证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="对端子网地址" prop="addressTow">
+          <el-input v-model="ruleForm.addressTow"></el-input>
+        </el-form-item>
+        <el-form-item label="对端子网掩码长度" prop="digit">
+          <el-input v-model="ruleForm.digit"></el-input>
+        </el-form-item>
+        <el-form-item label="日志级别" prop="loglevel">
+          <el-select v-model="ruleForm.loglevel" placeholder="请选择日志级别">
+            <el-option label="级别1" :value="1"></el-option>
+            <el-option label="级别2" :value="2"></el-option>
+            <el-option label="级别3" :value="3"></el-option>
+            <el-option label="级别4" :value="4"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'lan',
+  components: {},
+  computed: {
+    ...mapState(['sigdata', 'secdata'])
+  },
+  data () {
+    return {
+      ruleForm: {},
+      rules: {
+        address: [
+          { required: true, message: '请输入服务器地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        cert: [
+          { required: true, message: '请选择签名证书', trigger: 'chage' }
+        ],
+        addressTow: [
+          { required: true, message: '请输入子网地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        digit: [
+          { required: true, message: '请输入掩码长度', trigger: 'blur' }
+        ],
+        pwa: [
+          { required: true, message: '请选择加密证书', trigger: 'chage' }
+        ],
+        loglevel: [
+          { required: true, message: '请选择日志级别', trigger: 'chage' }
+        ]
+      }
+    }
+  },
+  async mounted () {
+    await this.sigcacertquery()
+    const res = await this.secclientquery()
+    if (res.errcode === 0) {
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['sigcacertquery', 'ipsecvpnstate', 'caquery', 'secclient', 'secclientquery']),
+    // 提交数据
+    async onSubmit () {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) return false
+        const res = await this.secclient(this.ruleForm)
+        if (res.errcode === 0) {
+          this.$message.success('设置成功')
+        }
+      })
+    },
+    // 启动停止重启
+    async reboot (e) {
+      const dw = await this.ipsecvpnstate({ type: e })
+      if (dw.errcode === 0) {
+        this.$message.success('操作成功')
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 2% auto;
+  /deep/ .el-form-item .el-form-item__content .el-select {
+    width: 100%;
+  }
+}
+</style>

+ 166 - 0
src/views/vpn/sslservicevpn.vue

@@ -0,0 +1,166 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>SSLVPN</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('start')">启动服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('stop')">停止服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('restart')">重启服务</el-button>
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :rules="rules" :model="ruleForm" ref="form" label-width="150px" class="demo-ruleForm">
+         <el-form-item prop="address" label="虚拟子网地址">
+          <el-input v-model="ruleForm.address"></el-input>
+        </el-form-item>
+         <el-form-item prop="netmask" label="虚拟子网掩码">
+          <el-input v-model="ruleForm.netmask"></el-input>
+        </el-form-item>
+         <el-form-item prop="port" label="端口">
+          <el-input v-model="ruleForm.port" placeholder="请输入端口"></el-input>
+        </el-form-item>
+         <el-form-item prop="agreement" label="传输协议">
+          <el-select v-model="ruleForm.agreement" placeholder="请选择传输协议">
+            <el-option label="TCP" value="tcp"></el-option>
+            <el-option label="UDP" value="udp"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="routerAddress" label="本地子网地址">
+          <el-input v-model="ruleForm.routerAddress"></el-input>
+        </el-form-item>
+         <el-form-item prop="routerNetmask" label="本地子网掩码">
+          <el-input v-model="ruleForm.routerNetmask"></el-input>
+        </el-form-item>
+         <el-form-item prop="ca" label="证书链">
+          <el-select v-model="ruleForm.ca" placeholder="请选择ca证书">
+            <el-option v-for="(item, index) in cadata" :label="item.dn" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="cert" label="签名证书">
+          <el-select v-model="ruleForm.cert" placeholder="选择签名证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="pwa" label="加密证书">
+          <el-select v-model="ruleForm.pwa" placeholder="选择加密证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="loglevel" label="日志级别">
+          <el-select v-model="ruleForm.loglevel" placeholder="请选择日志级别">
+            <el-option label="1级" value="1"></el-option>
+            <el-option label="2级" value="2"></el-option>
+            <el-option label="3级" value="3"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item>
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'lan',
+  components: {},
+  computed: {
+    ...mapState(['cadata', 'sigdata', 'ssldata'])
+  },
+  data () {
+    return {
+      ruleForm: {},
+      rules: {
+        address: [
+          { required: true, message: '请输入地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        routerAddress: [
+          { required: true, message: '请输入路由地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        routerNetmask: [
+          { required: true, message: '请输入路由子网掩码', trigger: 'blur' },
+          { pattern: /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/, message: '请输入正确掩码' }
+        ],
+        port: [
+          { required: true, message: '请输入端口', trigger: 'blur' }
+        ],
+        netmask: [
+          { required: true, message: '请输入子网掩码', trigger: 'blur' },
+          { pattern: /^(254|252|248|240|224|192|128|0)\.0\.0\.0|255\.(254|252|248|240|224|192|128|0)\.0\.0|255\.255\.(254|252|248|240|224|192|128|0)\.0|255\.255\.255\.(254|252|248|240|224|192|128|0)$/, message: '请输入正确掩码' }
+        ],
+        agreement: [
+          { required: true, message: '请选择传输协议', trigger: 'chage' }
+        ],
+        ca: [
+          { required: true, message: '请选择CA证书', trigger: 'chage' }
+        ],
+        cert: [
+          { required: true, message: '请选择签名证书', trigger: 'chage' }
+        ],
+        loglevel: [
+          { required: true, message: '请选择日志等级', trigger: 'chage' }
+        ],
+        pwa: [
+          { required: true, message: '请选择加密证书', trigger: 'chage' }
+        ]
+      }
+    }
+  },
+  async mounted () {
+    await this.caquery()
+    await this.sigcacertquery()
+    const res = await this.sslserivcequery()
+    if (res.errcode === 0) {
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['sigcacertquery', 'sslvpnstate', 'caquery', 'sslvpnservice', 'sslserivcequery']),
+    // 提交数据
+    async onSubmit () {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) return false
+        const res = await this.sslvpnservice(this.ruleForm)
+        if (res.errcode === 0) {
+          this.$message.success('设置成功')
+        }
+      })
+    },
+    // 启动停止重启
+    async reboot (e) {
+      const dw = await this.sslvpnstate({ type: e })
+      if (dw.errcode === 0) {
+        this.$message.success('操作成功')
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 2% auto;
+  /deep/ .el-form-item .el-form-item__content .el-select {
+    width: 100%;
+  }
+}
+</style>

+ 145 - 0
src/views/vpn/sslvpn.vue

@@ -0,0 +1,145 @@
+<template>
+  <el-container>
+    <el-header class="header">
+      <div class="headers">
+        <h3>SSLVPN</h3>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('start')">启动服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('stop')">停止服务</el-button>
+        <el-button class="rebootcard" size="mini" type="primary" @click="reboot('restart')">重启服务</el-button>
+      </div>
+    </el-header>
+    <el-main class="main">
+      <el-form :rules="rules" :model="ruleForm" ref="form" label-width="150px" class="demo-ruleForm">
+         <el-form-item prop="address" label="VPN服务器IP地址">
+          <el-input v-model="ruleForm.address" placeholder="请输入IP地址"></el-input>
+        </el-form-item>
+         <el-form-item prop="port" label="端口">
+          <el-input v-model="ruleForm.port" placeholder="请输入端口"></el-input>
+        </el-form-item>
+         <el-form-item prop="agreement" label="传输协议">
+          <el-select v-model="ruleForm.agreement" placeholder="请选择传输协议">
+            <el-option label="TCP" value="tcp"></el-option>
+            <el-option label="UDP" value="udp"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="ca" label="证书链">
+          <el-select v-model="ruleForm.ca" placeholder="请选择ca证书">
+            <el-option v-for="(item, index) in cadata" :label="item.dn" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="cert" label="签名证书">
+          <el-select v-model="ruleForm.cert" placeholder="选择签名证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="pwa" label="加密证书">
+          <el-select v-model="ruleForm.pwa" placeholder="选择加密证书">
+            <el-option v-for="(item, index) in sigdata" :label="item.name" :value="item.uuid" :key="index"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="loglevel" label="日志级别">
+          <el-select v-model="ruleForm.loglevel" placeholder="请选择日志级别">
+            <el-option label="1级" value="1"></el-option>
+            <el-option label="2级" value="2"></el-option>
+            <el-option label="3级" value="3"></el-option>
+          </el-select>
+        </el-form-item>
+         <el-form-item prop="">
+          <el-button type="primary" @click="onSubmit">立即提交</el-button>
+        </el-form-item>
+      </el-form>
+    </el-main>
+  </el-container>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex'
+export default {
+  name: 'lan',
+  components: {},
+  computed: {
+    ...mapState(['cadata', 'sigdata', 'ssldata'])
+  },
+  data () {
+    return {
+      ruleForm: {},
+      rules: {
+        address: [
+          { required: true, message: '请输入地址', trigger: 'blur' },
+          { pattern: /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/, message: '请输入正确地址' }
+        ],
+        port: [
+          { required: true, message: '请输入端口', trigger: 'blur' }
+        ],
+        agreement: [
+          { required: true, message: '请选择传输协议', trigger: 'chage' }
+        ],
+        ca: [
+          { required: true, message: '请选择CA证书', trigger: 'chage' }
+        ],
+        cert: [
+          { required: true, message: '请选择签名证书', trigger: 'chage' }
+        ],
+        pwa: [
+          { required: true, message: '请选择加密证书', trigger: 'chage' }
+        ],
+        loglevel: [
+          { required: true, message: '请选择日志等级', trigger: 'chage' }
+        ]
+      }
+    }
+  },
+  async mounted () {
+    await this.caquery()
+    await this.sigcacertquery()
+    const res = await this.sslquery()
+    if (res.errcode === 0) {
+      this.ruleForm = res.data
+    }
+  },
+  methods: {
+    ...mapActions(['sigcacertquery', 'sslvpnstate', 'caquery', 'sslvpnclient', 'sslquery']),
+    // 提交数据
+    async onSubmit () {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) return false
+        const res = await this.sslvpnclient(this.ruleForm)
+        if (res.errcode === 0) {
+          this.$message.success('设置成功')
+        }
+      })
+    },
+    // 启动停止重启
+    async reboot (e) {
+      const dw = await this.sslvpnstate({ type: e })
+      if (dw.errcode === 0) {
+        this.$message.success('操作成功')
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.headers {
+  display: flex;
+  width: 100%;
+  border-bottom: 1px solid #999;
+}
+.rebootcard {
+  line-height: 1em;
+  height: 3em;
+  margin-top: 1%;
+}
+h3 {
+  width: 90%;
+  text-align: left;
+  text-indent: 1em;
+}
+.demo-ruleForm {
+  width: 50%;
+  margin: 2% auto;
+  /deep/ .el-form-item .el-form-item__content .el-select {
+    width: 100%;
+  }
+}
+</style>

+ 21 - 0
vue.config.js

@@ -0,0 +1,21 @@
+
+const baseUrl = '/public'
+// const baseUrl = '/'
+module.exports = {
+  publicPath: baseUrl,
+  productionSourceMap: false,
+
+  configureWebpack: {
+    resolve: {}
+  },
+
+  devServer: {
+    proxy: {
+      '/api/': {
+        // target: 'http://localhost:7001'
+        target: 'http://192.168.100.100:36449'
+      }
+    }
+  }
+}
+// C=cn,ST=jl,L=cc,O=jit,OU=pla,CN=vpnroot