Browse Source

releases 1.1.3

releases 1.1.3
wenjianzhang 5 years ago
parent
commit
56b6942484

+ 4 - 1
package.json

@@ -62,6 +62,7 @@
     "normalize.css": "8.0.1",
     "nprogress": "0.2.0",
     "path-to-regexp": "6.1.0",
+    "sass-resources-loader": "^2.0.3",
     "screenfull": "5.0.2",
     "showdown": "^1.9.1",
     "solarlunar": "^2.0.7",
@@ -72,10 +73,11 @@
     "vue-count-to": "1.0.13",
     "vue-cropper": "^0.5.5",
     "vue-particles": "^1.0.9",
-    "vue-router": "3.4.1",
+    "vue-router": "3.4.2",
     "vue-splitpane": "1.0.6",
     "vuedraggable": "2.24.0",
     "vuex": "3.5.1",
+    "webpack-bundle-analyzer": "^3.8.0",
     "xlsx": "0.16.5"
   },
   "devDependencies": {
@@ -93,6 +95,7 @@
     "beautifier": "^0.1.7",
     "chalk": "4.1.0",
     "chokidar": "3.4.2",
+    "compression-webpack-plugin": "^4.0.0",
     "connect": "3.7.0",
     "eslint": "7.6.0",
     "eslint-plugin-vue": "6.2.2",

+ 1 - 0
public/index.html

@@ -18,6 +18,7 @@
       height: 100%;
       margin: 0px;
       padding: 0px;
+      background: #f0f1f5;
     }
 
     .chromeframe {

+ 61 - 0
src/api/sysjob.js

@@ -0,0 +1,61 @@
+import request from '@/utils/request'
+
+// 查询SysJob列表
+export function listSysJob(query) {
+  return request({
+    url: '/api/v1/sysjob',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询SysJob详细
+export function getSysJob(jobId) {
+  return request({
+    url: '/api/v1/sysjob/' + jobId,
+    method: 'get'
+  })
+}
+
+// 新增SysJob
+export function addSysJob(data) {
+  return request({
+    url: '/api/v1/sysjob',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改SysJob
+export function updateSysJob(data) {
+  return request({
+    url: '/api/v1/sysjob',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除SysJob
+export function delSysJob(jobId) {
+  return request({
+    url: '/api/v1/sysjob/' + jobId,
+    method: 'get'
+  })
+}
+
+// 移除SysJob
+export function removeJob(jobId) {
+  return request({
+    url: '/api/v1/job/remove/' + jobId,
+    method: 'get'
+  })
+}
+
+// 启动SysJob
+export function startJob(jobId) {
+  return request({
+    url: '/api/v1/job/start/' + jobId,
+    method: 'get'
+  })
+}
+

+ 13 - 0
src/api/tools/gen.js

@@ -64,4 +64,17 @@ export function toProjectTable(tableId) {
     method: 'get'
   })
 }
+export function toProjectTableCheckRole(tableId, ischeckrole) {
+  return request({
+    url: '/api/v1/gen/toproject/' + tableId + '?ischeckrole=' + ischeckrole,
+    method: 'get'
+  })
+}
 
+// 生成菜单到数据库
+export function toDBTable(tableId) {
+  return request({
+    url: '/api/v1/gen/todb/' + tableId,
+    method: 'get'
+  })
+}

+ 39 - 0
src/assets/dark.svg

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" 
+    xmlns="http://www.w3.org/2000/svg" 
+    xmlns:xlink="http://www.w3.org/1999/xlink">
+    <defs>
+        <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+        <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
+        <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="配置面板" width="48" height="40" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="setting-copy-2" width="48" height="40" transform="translate(-1190.000000, -136.000000)">
+            <g id="Group-8" width="48" height="40" transform="translate(1167.000000, 0.000000)">
+                <g id="Group-5-Copy-5" filter="url(#filter-1)" transform="translate(25.000000, 137.000000)">
+                    <mask id="mask-3" fill="white">
+                        <use xlink:href="#path-2"></use>
+                    </mask>
+                    <g id="Rectangle-18">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                        <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    </g>
+                    <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
+                    <rect id="Rectangle-18" fill="#303648" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 39 - 0
src/assets/light.svg

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="52px" height="45px" viewBox="0 0 52 45" version="1.1" 
+    xmlns="http://www.w3.org/2000/svg" 
+    xmlns:xlink="http://www.w3.org/1999/xlink">
+    <defs>
+        <filter x="-9.4%" y="-6.2%" width="118.8%" height="122.5%" filterUnits="objectBoundingBox" id="filter-1">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+        <rect id="path-2" x="0" y="0" width="48" height="40" rx="4"></rect>
+        <filter x="-4.2%" y="-2.5%" width="108.3%" height="110.0%" filterUnits="objectBoundingBox" id="filter-4">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="配置面板" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="setting-copy-2" transform="translate(-1254.000000, -136.000000)">
+            <g id="Group-8" transform="translate(1167.000000, 0.000000)">
+                <g id="Group-5" filter="url(#filter-1)" transform="translate(89.000000, 137.000000)">
+                    <mask id="mask-3" fill="white">
+                        <use xlink:href="#path-2"></use>
+                    </mask>
+                    <g id="Rectangle-18">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                        <use fill="#F0F2F5" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    </g>
+                    <rect id="Rectangle-18" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="16" height="40"></rect>
+                    <rect id="Rectangle-11" fill="#FFFFFF" mask="url(#mask-3)" x="0" y="0" width="48" height="10"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

BIN
src/assets/logo/logo.png


+ 7 - 7
src/components/Screenfull/index.vue

@@ -22,13 +22,13 @@ export default {
   },
   methods: {
     click() {
-      if (!screenfull.enabled) {
-        this.$message({
-          message: 'you browser can not work',
-          type: 'warning'
-        })
-        return false
-      }
+      // if (!screenfull.enabled) {
+      //   this.$message({
+      //     message: 'you browser can not work',
+      //     type: 'warning'
+      //   })
+      //   return false
+      // }
       screenfull.toggle()
     },
     change() {

+ 2 - 5
src/components/ThemePicker/index.vue

@@ -9,7 +9,7 @@
 
 <script>
 const version = require('element-ui/package.json').version // element-ui version from node_modules
-const ORIGINAL_THEME = '#409EFF' // default color
+const ORIGINAL_THEME = '#1890FF' // default color
 
 export default {
   data() {
@@ -35,10 +35,9 @@ export default {
       if (typeof val !== 'string') return
       const themeCluster = this.getThemeCluster(val.replace('#', ''))
       const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
-      console.log(themeCluster, originalCluster)
 
       const $message = this.$message({
-        message: '  Compiling the theme',
+        message: '编译主题中',
         customClass: 'theme-message',
         type: 'success',
         duration: 0,
@@ -79,9 +78,7 @@ export default {
         if (typeof innerText !== 'string') return
         style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
       })
-
       this.$emit('change', val)
-
       $message.close()
     }
   },

+ 2 - 2
src/layout/components/AppMain.vue

@@ -38,11 +38,11 @@ export default {
 .hasTagsView {
   .app-main {
     /* 84 = navbar + tags-view = 50 + 34 */
-    min-height: calc(100vh - 84px);
+    min-height: calc(100vh - 93px);
   }
 
   .fixed-header+.app-main {
-    padding-top: 84px;
+    padding-top: 93px;
   }
 }
 </style>

+ 98 - 17
src/layout/components/Settings/index.vue

@@ -1,28 +1,61 @@
 <template>
   <div class="drawer-container">
     <div>
-      <h3 class="drawer-title">页面设置</h3>
-
-      <div class="drawer-item">
-        <span>主题颜色</span>
-        <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
+      <div class="setting-drawer-content">
+        <div class="setting-drawer-title">
+          页面设置
+        </div>
+        <div class="setting-drawer-block-checbox">
+          <div class="setting-drawer-block-checbox-item" @click="handleTheme('light')">
+            <img src="@/assets/light.svg" alt="light">
+            <div v-if="themeStyle === 'light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+              <i aria-label="图标: check" class="anticon anticon-check">
+                <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class=""><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
+                </svg>
+              </i>
+            </div>
+          </div>
+          <div class="setting-drawer-block-checbox-item" @click="handleTheme('dark')">
+            <img src="@/assets/dark.svg" alt="dark">
+            <div v-if="themeStyle === 'dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+              <i aria-label="图标: check" class="anticon anticon-check">
+                <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class=""><path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
+                </svg>
+              </i>
+            </div>
+          </div>
+        </div>
       </div>
-
-      <div class="drawer-item">
-        <span>开启任务栏</span>
-        <el-switch v-model="tagsView" class="drawer-switch" />
+      <el-divider />
+      <div class="setting-drawer-content">
+        <div class="setting-drawer-title">
+          主题设置
+        </div>
+        <div class="drawer-item">
+          <span>主题颜色</span>
+          <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
+        </div>
       </div>
+      <el-divider />
+      <div class="setting-drawer-content">
+        <div class="setting-drawer-title">
+          布局设置
+        </div>
+        <div class="drawer-item">
+          <span>开启任务栏</span>
+          <el-switch v-model="tagsView" class="drawer-switch" />
+        </div>
 
-      <div class="drawer-item">
-        <span>Header 固定</span>
-        <el-switch v-model="fixedHeader" class="drawer-switch" />
-      </div>
+        <div class="drawer-item">
+          <span>Header 固定</span>
+          <el-switch v-model="fixedHeader" class="drawer-switch" />
+        </div>
 
-      <div class="drawer-item">
-        <span>侧边栏Logo</span>
-        <el-switch v-model="sidebarLogo" class="drawer-switch" />
+        <div class="drawer-item">
+          <span>侧边栏Logo</span>
+          <el-switch v-model="sidebarLogo" class="drawer-switch" />
+        </div>
       </div>
-
     </div>
   </div>
 </template>
@@ -36,6 +69,12 @@ export default {
     return {}
   },
   computed: {
+    theme() {
+      return this.$store.state.settings.theme
+    },
+    themeStyle() {
+      return this.$store.state.settings.themeStyle
+    },
     fixedHeader: {
       get() {
         return this.$store.state.settings.fixedHeader
@@ -76,6 +115,12 @@ export default {
         key: 'theme',
         value: val
       })
+    },
+    handleTheme(val) {
+      this.$store.dispatch('settings/changeSetting', {
+        key: 'themeStyle',
+        value: val
+      })
     }
   }
 }
@@ -105,4 +150,40 @@ export default {
     float: right
   }
 }
+.setting-drawer-content{
+  .setting-drawer-title{
+    margin-bottom: 12px;
+    color: rgba(0,0,0,.85);
+    font-size: 14px;
+    line-height: 22px;
+    font-weight: bold;
+  }
+  .setting-drawer-block-checbox{
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    .setting-drawer-block-checbox-item {
+        position: relative;
+        margin-right: 16px;
+        border-radius: 2px;
+        cursor: pointer;
+        img{
+          width: 48px;
+          height: 48px;
+        }
+        .setting-drawer-block-checbox-selectIcon{
+            position: absolute;
+            top: 0;
+            right: 0;
+            width: 100%;
+            height: 100%;
+            padding-top: 15px;
+            padding-left: 24px;
+            color: #1890ff;
+            font-weight: 700;
+            font-size: 14px;
+        }
+    }
+  }
+}
 </style>

+ 8 - 4
src/layout/components/Sidebar/Logo.vue

@@ -1,13 +1,13 @@
 <template>
-  <div class="sidebar-logo-container" :class="{'collapse':collapse}">
+  <div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: $store.state.settings.themeStyle === 'dark' ? variables.menuBg : variables.menuLightBg }">
     <transition name="sidebarLogoFade">
       <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
         <img v-if="appInfo.logo" :src="appInfo.logo" class="sidebar-logo">
-        <h1 v-else class="sidebar-title">{{ appInfo.name }} </h1>
+        <h1 v-else class="sidebar-title" :style="{ color: $store.state.settings.themeStyle === 'dark' ? variables.sidebarTitle : variables.sidebarLightTitle }">{{ appInfo.name }} </h1>
       </router-link>
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
         <img v-if="appInfo.logo" :src="appInfo.logo" class="sidebar-logo">
-        <h1 class="sidebar-title">{{ appInfo.name }} </h1>
+        <h1 class="sidebar-title" :style="{ color: $store.state.settings.themeStyle === 'dark' ? variables.sidebarTitle : variables.sidebarLightTitle }">{{ appInfo.name }} </h1>
       </router-link>
     </transition>
   </div>
@@ -15,6 +15,7 @@
 
 <script>
 
+import variables from '@/styles/variables.scss'
 import { mapGetters } from 'vuex'
 
 export default {
@@ -28,7 +29,10 @@ export default {
   computed: {
     ...mapGetters([
       'appInfo'
-    ])
+    ]),
+    variables() {
+      return variables
+    }
   }
 }
 </script>

+ 1 - 1
src/layout/components/Sidebar/SidebarItem.vue

@@ -8,7 +8,7 @@
       </app-link>
     </template>
 
-    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
+    <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body :style="{ backgroundColor: '#000c17' }">
       <template slot="title">
         <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
       </template>

+ 3 - 2
src/layout/components/Sidebar/index.vue

@@ -5,8 +5,9 @@
       <el-menu
         :default-active="activeMenu"
         :collapse="isCollapse"
-        :background-color="variables.menuBg"
-        :text-color="variables.menuText"
+        :background-color=" $store.state.settings.themeStyle === 'light' ? variables.menuLightBg : variables.menuBg"
+        :text-color="$store.state.settings.themeStyle === 'light' ? 'rgba(0,0,0,.65)' : '#fff'"
+        :active-text-color="$store.state.settings.theme"
         :unique-opened="true"
         :collapse-transition="true"
         mode="vertical"

+ 1 - 0
src/layout/components/TagsView/ScrollPane.vue

@@ -26,6 +26,7 @@ export default {
       $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
     },
     moveToTarget(currentTag) {
+      console.log(currentTag)
       const $container = this.$refs.scrollContainer.$el
       const $containerWidth = $container.offsetWidth
       const $scrollWrapper = this.scrollWrapper

+ 103 - 0
src/layout/components/TagsView/TagsView.vue

@@ -0,0 +1,103 @@
+<template>
+  <div>
+    <el-tabs
+      v-model="editableTabsValue"
+      type="card"
+      closable
+    >
+      <el-tab-pane
+        v-for="item in visitedViews"
+        :key="item.path"
+        :label="item.title"
+        :name="item.path"
+      />
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import path from 'path'
+export default {
+  name: 'TagsView',
+  data() {
+    return {
+      editableTabsValue: '1'
+    }
+  },
+  computed: {
+    visitedViews() {
+      return this.$store.state.tagsView.visitedViews
+    },
+    routes() {
+      return this.$store.state.permission.routes
+    },
+    theme() {
+      return this.$store.state.settings.theme
+    }
+  },
+  watch: {
+    $route() {
+      this.addTags()
+      this.moveToCurrentTag()
+    },
+    visible(value) {
+      if (value) {
+        document.body.addEventListener('click', this.closeMenu)
+      } else {
+        document.body.removeEventListener('click', this.closeMenu)
+      }
+    }
+  },
+  mounted() {
+    this.initTags()
+    this.addTags()
+  },
+  methods: {
+    isActive(route) {
+      return route.path === this.$route.path
+    },
+    isAffix(tag) {
+      return tag.meta && tag.meta.affix
+    },
+    filterAffixTags(routes, basePath = '/') {
+      let tags = []
+      routes.forEach(route => {
+        if (route.meta && route.meta.affix) {
+          const tagPath = path.resolve(basePath, route.path)
+          tags.push({
+            fullPath: tagPath,
+            path: tagPath,
+            name: route.name,
+            meta: { ...route.meta }
+          })
+        }
+        if (route.children) {
+          const tempTags = this.filterAffixTags(route.children, route.path)
+          if (tempTags.length >= 1) {
+            tags = [...tags, ...tempTags]
+          }
+        }
+      })
+      return tags
+    },
+    initTags() {
+      const affixTags = (this.affixTags = this.filterAffixTags(this.routes))
+      for (const tag of affixTags) {
+        // Must have tag name
+        if (tag.name) {
+          this.$store.dispatch('tagsView/addVisitedView', tag)
+        }
+      }
+    },
+    addTags() {
+      const { name } = this.$route
+      if (name) {
+        this.$store.dispatch('tagsView/addView', this.$route)
+      }
+      return false
+    }
+  }
+}
+</script>
+
+<style></style>

+ 60 - 33
src/layout/components/TagsView/index.vue

@@ -1,22 +1,30 @@
 <template>
   <div id="tags-view-container" class="tags-view-container">
-    <scroll-pane ref="scrollPane" class="tags-view-wrapper">
-      <router-link
-        v-for="tag in visitedViews"
-        ref="tag"
-        :key="tag.path"
-        :class="isActive(tag)?'active':''"
-        :style="{ backgroundColor: isActive(tag) ? theme : '', borderColor: isActive(tag) ? theme : '', }"
-        :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
-        tag="span"
-        class="tags-view-item"
-        @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
-        @contextmenu.prevent.native="openMenu(tag,$event)"
+    <el-tabs
+      v-model="editableTabsValue"
+      type="card"
+      @tab-remove="closeSelectedTag"
+    >
+      <el-tab-pane
+        v-for="item in visitedViews"
+        :key="item.path"
+        :closable="item.fullPath === '/dashboard' ? false : true"
+        :name="item.fullPath"
       >
-        {{ tag.title }}
-        <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
-      </router-link>
-    </scroll-pane>
+        <template #label>
+          <router-link
+            ref="tag"
+            tag="span"
+            class="tags-view-item"
+            :style="{ color: item.fullPath === $route.fullPath ? theme : '' }"
+            :to="{ path: item.path, query: item.query, fullPath: item.fullPath }"
+            @contextmenu.prevent.native="openMenu(item,$event)"
+          >
+            {{ item.title }}
+          </router-link>
+        </template>
+      </el-tab-pane>
+    </el-tabs>
     <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
       <li @click="refreshSelectedTag(selectedTag)">刷新当前标签页</li>
       <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前标签页</li>
@@ -27,18 +35,17 @@
 </template>
 
 <script>
-import ScrollPane from './ScrollPane'
 import path from 'path'
 
 export default {
-  components: { ScrollPane },
   data() {
     return {
-      visible: false,
+      editableTabsValue: '/',
       top: 0,
       left: 0,
       selectedTag: {},
-      affixTags: []
+      affixTags: [],
+      visible: false
     }
   },
   computed: {
@@ -55,7 +62,6 @@ export default {
   watch: {
     $route() {
       this.addTags()
-      this.moveToCurrentTag()
     },
     visible(value) {
       if (value) {
@@ -68,10 +74,13 @@ export default {
   mounted() {
     this.initTags()
     this.addTags()
+    this.isActive()
   },
   methods: {
-    isActive(route) {
-      return route.path === this.$route.path
+    isActive() {
+      const index = this.visitedViews.findIndex(item => item.fullPath === this.$route.fullPath)
+      const pathIndex = index > -1 ? index : 0
+      this.editableTabsValue = this.visitedViews[pathIndex].fullPath
     },
     isAffix(tag) {
       return tag.meta && tag.meta.affix
@@ -110,6 +119,7 @@ export default {
       const { name } = this.$route
       if (name) {
         this.$store.dispatch('tagsView/addView', this.$route)
+        this.isActive()
       }
       return false
     },
@@ -118,7 +128,7 @@ export default {
       this.$nextTick(() => {
         for (const tag of tags) {
           if (tag.to.path === this.$route.path) {
-            this.$refs.scrollPane.moveToTarget(tag)
+            // this.$refs.scrollPane.moveToTarget(tag)
             // when query is different then update
             if (tag.to.fullPath !== this.$route.fullPath) {
               this.$store.dispatch('tagsView/updateVisitedView', this.$route)
@@ -139,14 +149,19 @@ export default {
       })
     },
     closeSelectedTag(view) {
-      this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
-        if (this.isActive(view)) {
-          this.toLastView(visitedViews, view)
-        }
-      })
+      const routerPath = view.fullPath ? view.fullPath : view
+      const index = this.visitedViews.findIndex(item => item.fullPath === routerPath)
+      if (index > -1) {
+        const path = this.visitedViews[index]
+        this.$store.dispatch('tagsView/delView', path).then(({ visitedViews }) => {
+          if (this.editableTabsValue === path.fullPath) {
+            this.toLastView(visitedViews, path)
+          }
+        })
+      }
     },
     closeOthersTags() {
-      this.$router.push(this.selectedTag)
+      this.$router.push(this.selectedTag.path).catch(e => e)
       this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
         this.moveToCurrentTag()
       })
@@ -162,7 +177,7 @@ export default {
     toLastView(visitedViews, view) {
       const latestView = visitedViews.slice(-1)[0]
       if (latestView) {
-        this.$router.push(latestView.fullPath)
+        this.$router.push(latestView.fullPath).catch(err => err)
       } else {
         // now the default is to redirect to the home page if there is no tags-view,
         // you can adjust it according to your needs.
@@ -199,12 +214,24 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.tags-view-container {
-  height: 34px;
+
+.tags-view-container /deep/{
+  height: 43px;
   width: 100%;
   background: #fff;
   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);
+  padding: 0 15px;
+  box-sizing: border-box;
+  .el-tabs__item{
+    &:hover{
+      color: #000;
+    }
+  }
+  .tags-view-item{
+    height: 40px;
+    display: inline-block;
+  }
   .tags-view-wrapper {
     .tags-view-item {
       display: inline-block;

+ 6 - 2
src/layout/index.vue

@@ -1,7 +1,7 @@
 <template>
-  <div :class="classObj" class="app-wrapper">
+  <div :class="classObj" class="app-wrapper" :style="{'--current-color': $store.state.settings.theme}">
     <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
-    <sidebar class="sidebar-container" />
+    <sidebar class="sidebar-container" :style="{ backgroundColor: $store.state.settings.themeStyle === 'dark' ? variables.menuBg : variables.menuLightBg }" />
     <div :class="{hasTagsView:needTagsView}" class="main-container">
       <div :class="{'fixed-header':fixedHeader}">
         <navbar />
@@ -20,6 +20,7 @@ import RightPanel from '@/components/RightPanel'
 import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
 import ResizeMixin from './mixin/ResizeHandler'
 import { mapState } from 'vuex'
+import variables from '@/styles/variables.scss'
 
 export default {
   name: 'Layout',
@@ -47,6 +48,9 @@ export default {
         withoutAnimation: this.sidebar.withoutAnimation,
         mobile: this.device === 'mobile'
       }
+    },
+    variables() {
+      return variables
     }
   },
   methods: {

+ 3 - 1
src/settings.js

@@ -31,5 +31,7 @@ module.exports = {
    * The default is only used in the production env
    * If you want to also use it in dev, you can pass ['production', 'development']
    */
-  errorLog: 'production'
+  errorLog: 'production',
+
+  themeStyle: 'dark'
 }

+ 4 - 2
src/store/modules/settings.js

@@ -1,14 +1,15 @@
 import variables from '@/styles/element-variables.scss'
 import defaultSettings from '@/settings'
 
-const { showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings
+const { showSettings, tagsView, fixedHeader, sidebarLogo, themeStyle } = defaultSettings
 
 const state = {
   theme: variables.theme,
   showSettings: showSettings,
   tagsView: tagsView,
   fixedHeader: fixedHeader,
-  sidebarLogo: sidebarLogo
+  sidebarLogo: sidebarLogo,
+  themeStyle: themeStyle
 }
 
 const mutations = {
@@ -22,6 +23,7 @@ const mutations = {
 
 const actions = {
   changeSetting({ commit }, data) {
+    console.log(data)
     commit('CHANGE_SETTING', data)
   }
 }

+ 7 - 5
src/store/modules/tagsView.js

@@ -38,12 +38,14 @@ const mutations = {
     })
   },
   DEL_OTHERS_CACHED_VIEWS: (state, view) => {
-    const index = state.cachedViews.indexOf(view.name)
-    if (index > -1) {
-      state.cachedViews = state.cachedViews.slice(index, index + 1)
-    } else {
+    if (state.cachedViews.length > 0) {
+      const index = state.cachedViews.indexOf(view.name)
+      if (index > -1) {
+        state.cachedViews = state.cachedViews.slice(index, index + 1)
+      } else {
       // if index = -1, there is no cached tags
-      state.cachedViews = []
+        state.cachedViews = []
+      }
     }
   },
 

+ 4 - 0
src/styles/admin.scss

@@ -187,4 +187,8 @@
 	border-radius: 50%;
 	box-shadow: 0 0 4px #ccc;
 	overflow: hidden;
+}
+
+.el-tabs__item{
+	font-size: 13px;
 }

+ 21 - 13
src/styles/sidebar.scss

@@ -68,25 +68,41 @@
     .submenu-title-noDropdown,
     .el-submenu__title {
       &:hover {
-        background-color: $menuHover !important;
+        background-color: rgba(0,0,0,0.06)!important;
+        // background-color: transparent!important;
       }
     }
 
     .is-active>.el-submenu__title {
-      color: $subMenuActiveText !important;
+      // color: $subMenuActiveText !important;
     }
 
     & .nest-menu .el-submenu>.el-submenu__title,
     & .el-submenu .el-menu-item {
       min-width: $sideBarWidth !important;
-      background-color: $subMenuBg !important;
+      // background-color: $subMenuBg !important;
+      position: relative;
 
-      &:hover {
-        background-color: $subMenuHover !important;
+     &:hover {
+        background-color: rgba(0,0,0,0.06)!important;
+        // &::before{
+        //   content: "";
+        //   position: absolute;
+        //   top: 0;
+        //   bottom: 0;
+        //   width: 2px;
+        //   background-color: $menuActiveText;
+        //   height: 100%;
+        //   right: 0;
+        // }
       }
     }
   }
 
+  .el-submenu{
+    background-color: transparent!important;
+  }
+
   .hideSidebar {
     .sidebar-container {
       width: 54px !important;
@@ -181,14 +197,6 @@
     }
   }
 
-  .nest-menu .el-submenu>.el-submenu__title,
-  .el-menu-item {
-    &:hover {
-      // you can use $subMenuHover
-      background-color: $menuHover !important;
-    }
-  }
-
   // the scroll bar appears when the subMenu is too long
   >.el-menu--popup {
     max-height: 100vh;

+ 8 - 5
src/styles/variables.css

@@ -1,10 +1,13 @@
 :export {
-  menuText: #bfcbd9;
   menuActiveText: #409EFF;
   subMenuActiveText: #f4f4f5;
-  menuBg: #304156;
-  menuHover: #263445;
-  subMenuBg: #1f2d3d;
+  menuBg: #001529;
+  menuHover: #000c17;
+  menuLightBg: #ffffff;
+  menuLightHover: #f0f1f5;
+  subMenuBg: #000c17;
   subMenuHover: #001528;
-  sideBarWidth: 253px;
+  sideBarWidth: 256px;
+  sidebarTitle: #ffffff;
+  sidebarLightTitle: #001529;
 }

+ 1 - 1
src/styles/variables.min.css

@@ -1 +1 @@
-:export{menuText:#bfcbd9;menuActiveText:#409EFF;subMenuActiveText:#f4f4f5;menuBg:#304156;menuHover:#263445;subMenuBg:#1f2d3d;subMenuHover:#001528;sideBarWidth:253px}
+:export{menuActiveText:#409EFF;subMenuActiveText:#f4f4f5;menuBg:#001529;menuHover:#000c17;menuLightBg:#fff;menuLightHover:#f0f1f5;subMenuBg:#000c17;subMenuHover:#001528;sideBarWidth:256px;sidebarTitle:#fff;sidebarLightTitle:#001529}

+ 10 - 2
src/styles/variables.scss

@@ -9,13 +9,17 @@ $yellow:#FEC171;
 $panGreen: #30B08F;
 
 // sidebar
-$menuText:#bfcbd9;
+// $menuText:#bfcbd9;
 $menuActiveText:#409EFF;
 $subMenuActiveText:#f4f4f5; // https://github.com/ElemeFE/element/issues/12951
 
 $menuBg:#001529;
 $menuHover:#000c17;
+$sidebarTitle: #ffffff;
 
+$menuLightBg:#ffffff;
+$menuLightHover:#f0f1f5;
+$sidebarLightTitle: #001529;
 
 $subMenuBg:#000c17;
 $subMenuHover:#001528;
@@ -25,12 +29,16 @@ $sideBarWidth: 256px;
 // the :export directive is the magic sauce for webpack
 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
 :export {
-  menuText: $menuText;
+  // menuText: $menuText;
   menuActiveText: $menuActiveText;
   subMenuActiveText: $subMenuActiveText;
   menuBg: $menuBg;
   menuHover: $menuHover;
+  menuLightBg: $menuLightBg;
+  menuLightHover: $menuLightHover;
   subMenuBg: $subMenuBg;
   subMenuHover: $subMenuHover;
   sideBarWidth: $sideBarWidth;
+  sidebarTitle: $sidebarTitle;
+  sidebarLightTitle: $sidebarLightTitle
 }

+ 4 - 4
src/views/role/index.vue

@@ -241,10 +241,13 @@ import { listRole, getRole, delRole, addRole, updateRole, dataScope, changeRoleS
 import { treeselect as menuTreeselect, roleMenuTreeselect } from '@/api/system/menu'
 import { treeselect as deptTreeselect, roleDeptTreeselect } from '@/api/system/dept'
 import { formatJson } from '@/utils'
-import BasicLayout from "@/layout/BasicLayout";
+import BasicLayout from '@/layout/BasicLayout'
 
 export default {
   name: 'Role',
+  components: {
+    BasicLayout
+  },
   data() {
     return {
       // 遮罩层
@@ -325,9 +328,6 @@ export default {
       }
     }
   },
-  components: {
-    BasicLayout
-  },
   created() {
     this.getList()
     this.getDicts('sys_normal_disable').then(response => {

+ 545 - 0
src/views/sysjob/index.vue

@@ -0,0 +1,545 @@
+
+<template>
+  <div>
+    <BasicLayout>
+      <template #wrapper>
+        <el-card class="box-card">
+          <el-form ref="queryForm" :model="queryParams" :inline="true" label-position="left" label-width="68px">
+            <el-form-item label="名称" prop="jobName">
+              <el-input
+                v-model="queryParams.jobName"
+                placeholder="请输入名称"
+                clearable
+                size="small"
+                @keyup.enter.native="handleQuery"
+              />
+            </el-form-item>
+            <el-form-item label="任务分组" prop="jobGroup">
+              <el-select
+                v-model="queryParams.jobGroup"
+                placeholder="定时任务任务分组"
+                clearable
+                size="small"
+              >
+                <el-option
+                  v-for="dict in jobGroupOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="状态" prop="status">
+              <el-select
+                v-model="queryParams.status"
+                placeholder="定时任务状态"
+                clearable
+                size="small"
+              >
+                <el-option
+                  v-for="dict in statusOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+
+            <el-form-item>
+              <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+              <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+            </el-form-item>
+          </el-form>
+
+          <el-row :gutter="10" class="mb8">
+            <el-col :span="1.5">
+              <el-button
+                v-permisaction="['sysjob:sysjob:add']"
+                type="primary"
+                icon="el-icon-plus"
+                size="mini"
+                @click="handleAdd"
+              >新增
+              </el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button
+                v-permisaction="['sysjob:sysjob:edit']"
+                type="success"
+                icon="el-icon-edit"
+                size="mini"
+                :disabled="single"
+                @click="handleUpdate"
+              >修改
+              </el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button
+                v-permisaction="['sysjob:sysjob:remove']"
+                type="danger"
+                icon="el-icon-delete"
+                size="mini"
+                :disabled="multiple"
+                @click="handleDelete"
+              >删除
+              </el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button
+                v-permisaction="['sysjob:sysjob:log']"
+                type="danger"
+                icon="el-icon-delete"
+                size="mini"
+                @click="handleLog"
+              >日志
+              </el-button>
+
+            </el-col>
+          </el-row>
+
+          <el-table v-loading="loading" :data="sysjobList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" /><el-table-column
+              label="编码"
+              align="center"
+              prop="jobId"
+              :show-overflow-tooltip="true"
+            /><el-table-column
+              label="名称"
+              align="center"
+              prop="jobName"
+              :show-overflow-tooltip="true"
+            /><el-table-column
+              label="任务分组"
+              align="center"
+              prop="jobGroup"
+              :formatter="jobGroupFormat"
+              width="100"
+            >
+              <template slot-scope="scope">
+                {{ jobGroupFormat(scope.row) }}
+              </template>
+            </el-table-column><el-table-column
+              label="cron表达式"
+              align="center"
+              prop="cronExpression"
+              :show-overflow-tooltip="true"
+            /><el-table-column
+              label="调用目标"
+              align="center"
+              prop="invokeTarget"
+              :show-overflow-tooltip="true"
+            /><el-table-column
+              label="状态"
+              align="center"
+              prop="status"
+              :formatter="statusFormat"
+              width="100"
+            >
+              <template slot-scope="scope">
+                {{ statusFormat(scope.row) }}
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+              <template slot-scope="scope">
+                <el-button
+                  v-permisaction="['sysjob:sysjob:edit']"
+                  size="mini"
+                  type="text"
+                  icon="el-icon-edit"
+                  @click="handleUpdate(scope.row)"
+                >修改
+                </el-button>
+                <el-button
+                  v-if="scope.row.entry_id!==0"
+                  v-permisaction="['sysjob:job:remove']"
+                  size="mini"
+                  type="text"
+                  icon="el-icon-edit"
+                  @click="handleRemove(scope.row)"
+                >停止
+                </el-button>
+                <el-button
+                  v-if="scope.row.entry_id==0"
+                  v-permisaction="['sysjob:job:start']"
+                  size="mini"
+                  type="text"
+                  icon="el-icon-edit"
+                  @click="handleStart(scope.row)"
+                >启动
+                </el-button>
+                <el-button
+                  v-permisaction="['sysjob:sysjob:remove']"
+                  size="mini"
+                  type="text"
+                  icon="el-icon-delete"
+                  @click="handleDelete(scope.row)"
+                >删除
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <pagination
+            v-show="total>0"
+            :total="total"
+            :page.sync="queryParams.pageIndex"
+            :limit.sync="queryParams.pageSize"
+            @pagination="getList"
+          />
+
+          <!-- 添加或修改对话框 -->
+          <el-dialog v-dialogDrag :title="title" :visible.sync="open" width="700px" append-to-body>
+            <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+              <el-row>
+                <el-col :span="12">
+                  <el-form-item label="名称" prop="jobName">
+                    <el-input
+                      v-model="form.jobName"
+                      placeholder="名称"
+                    />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="任务分组" prop="jobGroup">
+                    <el-select
+                      v-model="form.jobGroup"
+                      placeholder="请选择"
+                    >
+                      <el-option
+                        v-for="dict in jobGroupOptions"
+                        :key="dict.dictValue"
+                        :label="dict.dictLabel"
+                        :value="dict.dictValue"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="调用目标" prop="invokeTarget">
+                    <span slot="label">
+                      调用目标
+                      <el-tooltip placement="top">
+                        <div slot="content">
+                          调用示例:func (t *EXEC) ExamplesNoParam(){..} 填写 ExamplesNoParam 即可;
+                          <br>参数说明:目前不支持带参调用
+                        </div>
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input
+                      v-model="form.invokeTarget"
+                      placeholder="调用目标"
+                    />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="cron表达式" prop="cronExpression">
+                    <el-input
+                      v-model="form.cronExpression"
+                      placeholder="cron表达式"
+                    />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="是否并发" prop="concurrent">
+                    <el-radio-group v-model="form.concurrent" size="small">
+                      <el-radio-button label="0">允许</el-radio-button>
+                      <el-radio-button label="1">禁止</el-radio-button>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item label="调用类型" prop="jobType">
+                    <el-radio-group v-model="form.jobType" size="small">
+                      <el-radio-button label="1">接口方式</el-radio-button>
+                      <el-radio-button label="2">函数【无参】</el-radio-button>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+
+                <el-col :span="24">
+                  <el-form-item label="错误策略" prop="misfirePolicy">
+                    <el-radio-group v-model="form.misfirePolicy" size="small">
+                      <el-radio-button label="1">立即执行</el-radio-button>
+                      <el-radio-button label="2">执行一次</el-radio-button>
+                      <el-radio-button label="3">放弃执行</el-radio-button>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item label="状态" prop="status">
+                    <el-select
+                      v-model="form.status"
+                      placeholder="请选择"
+                    >
+                      <el-option
+                        v-for="dict in statusOptions"
+                        :key="dict.dictValue"
+                        :label="dict.dictLabel"
+                        :value="dict.dictValue"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+              <el-button type="primary" @click="submitForm">确 定</el-button>
+              <el-button @click="cancel">取 消</el-button>
+            </div>
+          </el-dialog>
+
+        </el-card>
+      </template>
+    </BasicLayout>
+  </div>
+</template>
+
+<script>
+import { addSysJob, delSysJob, getSysJob, listSysJob, updateSysJob, removeJob, startJob } from '@/api/sysjob'
+import BasicLayout from '@/layout/BasicLayout'
+
+export default {
+  name: 'Config',
+  components: {
+    BasicLayout
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 总条数
+      total: 0,
+      // 弹出层标题
+      title: '',
+      // 是否显示弹出层
+      open: false,
+      isEdit: false,
+      // 类型数据字典
+      typeOptions: [],
+      sysjobList: [],
+      jobGroupOptions: [],
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageIndex: 1,
+        pageSize: 10,
+        jobName:
+            undefined,
+        jobGroup:
+            undefined,
+        status:
+            undefined
+
+      },
+      // 表单参数
+      form: {
+      },
+      // 表单校验
+      rules: { jobId:
+                [
+                  { required: true, message: '编码不能为空', trigger: 'blur' }
+                ],
+      jobName:
+                [
+                  { required: true, message: '名称不能为空', trigger: 'blur' }
+                ],
+      jobGroup:
+                [
+                  { required: true, message: '任务分组不能为空', trigger: 'blur' }
+                ],
+      cronExpression:
+                [
+                  { required: true, message: 'cron表达式不能为空', trigger: 'blur' }
+                ],
+      invokeTarget:
+                [
+                  { required: true, message: '调用目标不能为空', trigger: 'blur' }
+                ],
+      status:
+                [
+                  { required: true, message: '状态不能为空', trigger: 'blur' }
+                ]
+      }
+    }
+  },
+  created() {
+    this.getList()
+    this.getDicts('sys_job_group').then(response => {
+      this.jobGroupOptions = response.data
+    })
+
+    this.getDicts('sys_job_status').then(response => {
+      this.statusOptions = response.data
+    })
+  },
+  methods: {
+    /** 查询参数列表 */
+    getList() {
+      this.loading = true
+      listSysJob(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.sysjobList = response.data.list
+        this.total = response.data.count
+        this.loading = false
+      }
+      )
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        jobId: undefined,
+        jobName: undefined,
+        jobGroup: undefined,
+        cronExpression: undefined,
+        invokeTarget: undefined,
+        misfirePolicy: 1,
+        concurrent: 1,
+        jobType: 1,
+        status: 0
+      }
+      this.resetForm('form')
+    },
+    jobGroupFormat(row) {
+      return this.selectDictLabel(this.jobGroupOptions, row.jobGroup)
+    },
+    statusFormat(row) {
+      return this.selectDictLabel(this.statusOptions, row.status)
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageIndex = 1
+      this.getList()
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = []
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '添加定时任务'
+      this.isEdit = false
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.jobId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset()
+      const jobId =
+                row.jobId || this.ids
+      getSysJob(jobId).then(response => {
+        this.form = response.data
+        this.form.status = String(this.form.status)
+        this.form.misfirePolicy = String(this.form.misfirePolicy)
+        this.form.concurrent = String(this.form.concurrent)
+        this.form.jobType = String(this.form.jobType)
+        this.open = true
+        this.title = '修改定时任务'
+        this.isEdit = true
+      })
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      console.log(this.form)
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          if (this.form.jobId !== undefined) {
+            this.form.status = parseInt(this.form.status)
+            this.form.misfirePolicy = parseInt(this.form.misfirePolicy)
+            this.form.concurrent = parseInt(this.form.concurrent)
+            this.form.jobType = parseInt(this.form.jobType)
+            updateSysJob(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess('修改成功')
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          } else {
+            this.form.status = parseInt(this.form.status)
+            this.form.misfirePolicy = parseInt(this.form.misfirePolicy)
+            this.form.concurrent = parseInt(this.form.concurrent)
+            this.form.jobType = parseInt(this.form.jobType)
+            addSysJob(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess('新增成功')
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          }
+        }
+      })
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const Ids = row.jobId || this.ids
+      this.$confirm('是否确认删除编号为"' + Ids + '"的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return delSysJob(Ids)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('删除成功')
+      }).catch(function() {
+      })
+    },
+    /** 开始按钮操作 */
+    handleStart(row) {
+      this.$confirm('是否确认启动编号为"' + row.jobId + '"的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return startJob(row.jobId)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('启动成功')
+      }).catch(function() {
+      })
+    },
+    /** 停止按钮操作 */
+    handleRemove(row) {
+      this.$confirm('是否确认关闭编号为"' + row.jobId + '"的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return removeJob(row.jobId)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('关闭成功')
+      }).catch(function() {
+      })
+    },
+    handleLog() {
+      this.$router.push({ name: 'job_log', params: { }})
+    }
+  }
+}
+</script>

+ 84 - 0
src/views/sysjob/log.vue

@@ -0,0 +1,84 @@
+
+<template>
+  <div class="app-container">
+    <el-form>
+      <el-form-item>
+        <el-button type="success" icon="el-icon-search" size="mini">状态</el-button>
+        <el-button type="primary" icon="el-icon-search" size="mini">清空</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row ref="log" :gutter="10" class="mb8">
+      <el-scrollbar style="height:500px">
+        <ul
+          style="
+    background-color: black;
+    color: cornflowerblue;
+    line-height: 25px;
+    padding-top: 15px;
+    margin: 0;"
+        >
+          <li v-for="(item,index) in arrs" :key="index">
+
+            {{ item }}
+          </li>
+        </ul>
+      </el-scrollbar>
+    </el-row>
+  </div>
+
+</template>
+
+<script>
+export default {
+  name: 'Test',
+  data() {
+    return {
+      websock: null,
+      arrs: []
+    }
+  },
+  created() {
+    this.initWebSocket()
+  },
+  destroyed() {
+    this.websock.close() // 离开路由之后断开websocket连接
+  },
+  methods: {
+    initWebSocket() { // 初始化weosocket
+      console.log(this.$store.state.user.token)
+      const wsuri = 'ws://127.0.0.1:8000/ws?token=oooooooo'
+      this.websock = new WebSocket(wsuri, ['aaaaaaaaaaaaaaaaaaaaaaa'])
+      //   this.websock.on('headers', headers => {
+      //     headers.push('Authorization:Bearer sssss')
+      //   })
+      this.websock.onmessage = this.websocketonmessage
+      this.websock.onopen = this.websocketonopen
+      this.websock.onerror = this.websocketonerror
+      this.websock.onclose = this.websocketclose
+    },
+    websocketonopen() { // 连接建立之后执行send方法发送数据
+      console.log('连接打开')
+    //   const actions = { 'test': '12345' }
+    //   this.websocketsend(JSON.stringify(actions))
+    },
+    websocketonerror() { // 连接建立失败重连
+      this.initWebSocket()
+    },
+    websocketonmessage(e) { // 数据接收
+      console.log(e.data)
+      //   console.log(this.binaryAgent(e))
+      //   const redata = JSON.parse(e.data)
+      //   console.log(redata)
+      //   this.$refs.log.innerText = e.data + '\n' + this.$refs.log.innerText
+      this.arrs.unshift(e.data)
+    },
+    websocketsend(Data) { // 数据发送
+    //   this.websock.send(Data)
+    },
+    websocketclose(e) { // 关闭
+      console.log('断开连接', e)
+    }
+  }
+}
+
+</script>

+ 65 - 26
src/views/tools/gen/index.vue

@@ -28,7 +28,7 @@
         </el-form>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <!-- <el-button
+          <!-- <el-button
               type="primary"
               icon="el-icon-download"
               size="mini"
@@ -95,11 +95,6 @@
               <span>{{ parseTime(scope.row.createdAt) }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="更新时间" align="center" prop="updatedAt" width="165">
-            <template slot-scope="scope">
-              <span>{{ parseTime(scope.row.updatedAt) }}</span>
-            </template>
-          </el-table-column>
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template slot-scope="scope">
               <el-button
@@ -115,7 +110,21 @@
                 size="small"
                 icon="el-icon-view"
                 @click="handleToProject(scope.row)"
-              >生成到项目</el-button>
+              >代码生成</el-button>
+              <el-button
+
+                type="text"
+                size="small"
+                icon="el-icon-view"
+                @click="handleToProjectCheckRole(scope.row)"
+              >代码生成[带权限]</el-button>
+              <el-button
+
+                type="text"
+                size="small"
+                icon="el-icon-view"
+                @click="handleToDB(scope.row)"
+              >配置生成</el-button>
               <el-button
 
                 type="text"
@@ -141,29 +150,31 @@
           @pagination="getList"
         />
       </el-card>
+
+      <!-- 预览界面 -->
+
+      <el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh">
+        <el-tabs v-model="preview.activeName">
+          <el-tab-pane
+            v-for="(value, key) in preview.data"
+            :key="key"
+            :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.template'))"
+            :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.template'))"
+          >
+            <pre>{{ value }}</pre>
+          </el-tab-pane>
+        </el-tabs>
+      </el-dialog>
+      <import-table ref="importTB" @ok="handleQuery" />
     </template>
-    <!-- 预览界面 -->
-    <el-dialog :title="preview.title" :visible.sync="preview.open" width="80%" top="5vh">
-      <el-tabs v-model="preview.activeName">
-        <el-tab-pane
-          v-for="(value, key) in preview.data"
-          :key="key"
-          :label="key.substring(key.lastIndexOf('/')+1,key.indexOf('.template'))"
-          :name="key.substring(key.lastIndexOf('/')+1,key.indexOf('.template'))"
-        >
-          <pre>{{ value }}</pre>
-        </el-tab-pane>
-      </el-tabs>
-    </el-dialog>
-    <import-table ref="import" @ok="handleQuery" />
   </BasicLayout>
 </template>
 
 <script>
-import { listTable, previewTable, delTable, toProjectTable } from '@/api/tools/gen'
+import { listTable, previewTable, delTable, toProjectTable, toDBTable, toProjectTableCheckRole } from '@/api/tools/gen'
 import importTable from './importTable'
 import { downLoadFile } from '@/utils/zipdownload'
-import BasicLayout from "@/layout/BasicLayout";
+import BasicLayout from '@/layout/BasicLayout'
 export default {
   name: 'Gen',
   components: { importTable, BasicLayout },
@@ -240,7 +251,7 @@ export default {
     },
     /** 打开导入表弹窗 */
     openImportTable() {
-      this.$refs.import.show()
+      this.$refs.importTB.show()
     },
     /** 重置按钮操作 */
     resetQuery() {
@@ -256,9 +267,37 @@ export default {
       })
     },
     handleToProject(row) {
-      toProjectTable(row.tableId).then(response => {
+      this.$confirm('正在使用代码生成请确认?', '提示', {
+        confirmButtonText: '生成',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return toProjectTableCheckRole(row.tableId, false)
+      }).then((response) => {
         this.msgSuccess(response.msg)
-      })
+      }).catch(function() {})
+    },
+    handleToProjectCheckRole(row) {
+      this.$confirm('正在使用代码生成【带权限】请确认?', '提示', {
+        confirmButtonText: '生成',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return toProjectTableCheckRole(row.tableId, true)
+      }).then((response) => {
+        this.msgSuccess(response.msg)
+      }).catch(function() {})
+    },
+    handleToDB(row) {
+      this.$confirm('正在使用【菜单以及API生成到数据库】请确认?', '提示', {
+        confirmButtonText: '写入DB',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return toDBTable(row.tableId)
+      }).then((response) => {
+        this.msgSuccess(response.msg)
+      }).catch(function() {})
     },
     // 多选框选中数据
     handleSelectionChange(selection) {

+ 24 - 1
vue.config.js

@@ -1,5 +1,6 @@
 'use strict'
 const path = require('path')
+const CompressionPlugin = require('compression-webpack-plugin');//引入gzip压缩插件
 const defaultSettings = require('./src/settings.js')
 
 function resolve(dir) {
@@ -42,7 +43,14 @@ module.exports = {
   },
   configureWebpack: {
     plugins: [
-      new MonacoWebpackPlugin()
+      new CompressionPlugin({
+        algorithm: 'gzip',
+        test:/\.js$|\.html$|\.css/,//匹配文件名
+        threshold:10240,//对超过10kb的数据进行压缩
+        deleteOriginalAssets:false,//是否删除原文件
+        minRatio: 0.8
+      }),
+      new MonacoWebpackPlugin(),
     ],
     name: name,
     resolve: {
@@ -125,5 +133,20 @@ module.exports = {
           config.optimization.runtimeChunk('single')
         }
       )
+  },
+  css: {
+    loaderOptions: {
+      less: {
+        modifyVars: {
+          // less vars,customize ant design theme
+
+          // 'primary-color': '#F5222D',
+          // 'link-color': '#F5222D',
+          'border-radius-base': '2px'
+        },
+        // DO NOT REMOVE THIS LINE
+        javascriptEnabled: true
+      }
+    }
   }
 }