Merge branch 'dev_master'

This commit is contained in:
不做码农 2021-11-07 21:05:14 +08:00
commit 9c536b4363
21 changed files with 482 additions and 318 deletions

View File

@ -6,4 +6,4 @@ ENV = 'development'
VUE_APP_BASE_API = 'http://localhost:8888/' VUE_APP_BASE_API = 'http://localhost:8888/'
# 路由前缀 # 路由前缀
VUE_APP_ROUTER_PREFIX = '/admin' VUE_APP_ROUTER_PREFIX = '/'

View File

@ -10,60 +10,57 @@
</template> </template>
<script> <script>
import pathToRegexp from 'path-to-regexp'
export default { export default {
data() { data() {
return { return {
levelList: null levelList: null,
} };
}, },
watch: { watch: {
$route(route) { $route(route) {
// if you go to the redirect page, do not update the breadcrumbs // if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) { if (route.path.startsWith("/redirect/")) {
return return;
} }
this.getBreadcrumb() this.getBreadcrumb();
} },
}, },
created() { created() {
this.getBreadcrumb() this.getBreadcrumb();
}, },
methods: { methods: {
getBreadcrumb() { getBreadcrumb() {
// only show routes with meta.title // only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title) let matched = this.$route.matched.filter(
const first = matched[0] (item) => item.meta && item.meta.title
);
const first = matched[0];
if (!this.isDashboard(first)) { if (!this.isDashboard(first)) {
matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched) matched = [{ path: "/index", meta: { title: "首页" } }].concat(matched);
} }
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false) this.levelList = matched.filter(
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
);
}, },
isDashboard(route) { isDashboard(route) {
const name = route && route.name const name = route && route.name;
if (!name) { if (!name) {
return false return false;
} }
return name.trim() === '首页' return name.trim() === "Index";
},
pathCompile(path) {
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
}, },
handleLink(item) { handleLink(item) {
const { redirect, path } = item const { redirect, path } = item;
if (redirect) { if (redirect) {
this.$router.push(redirect) this.$router.push(redirect);
return return;
} }
this.$router.push(this.pathCompile(path)) this.$router.push(path);
} },
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -82,7 +82,7 @@ export default {
// //
activeMenu() { activeMenu() {
const path = this.$route.path; const path = this.$route.path;
let activePath = this.routers[0].path; let activePath = this.defaultRouter();
if (path.lastIndexOf("/") > 0) { if (path.lastIndexOf("/") > 0) {
const tmpPath = path.substring(1, path.length); const tmpPath = path.substring(1, path.length);
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/")); activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
@ -95,7 +95,9 @@ export default {
} }
var routes = this.activeRoutes(activePath); var routes = this.activeRoutes(activePath);
if (routes.length === 0) { if (routes.length === 0) {
activePath = this.currentIndex || this.routers[0].path; activePath = this.currentIndex || this.defaultRouter();
console.log("activePath", activePath);
this.activeRoutes(activePath); this.activeRoutes(activePath);
} }
return activePath; return activePath;
@ -116,6 +118,17 @@ export default {
const width = document.body.getBoundingClientRect().width / 3; const width = document.body.getBoundingClientRect().width / 3;
this.visibleNumber = parseInt(width / 85); this.visibleNumber = parseInt(width / 85);
}, },
//
defaultRouter() {
let router;
Object.keys(this.routers).some((key) => {
if (!this.routers[key].hidden) {
router = this.routers[key].path;
return true;
}
});
return router;
},
// //
handleSelect(key, keyPath) { handleSelect(key, keyPath) {
this.currentIndex = key; this.currentIndex = key;
@ -153,25 +166,28 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.el-menu--horizontal > .el-menu-item { .topmenu-container.el-menu--horizontal > .el-menu-item {
float: left; float: left;
height: 50px; height: 50px !important;
line-height: 50px; line-height: 50px !important;
margin: 0; color: #999093 !important;
border-bottom: 3px solid transparent; padding: 0 5px !important;
color: #999093; margin: 0 10px !important;
padding: 0 5px;
margin: 0 10px;
} }
.el-menu--horizontal > .el-menu-item.is-active { .topmenu-container.el-menu--horizontal > .el-menu-item.is-active,
border-bottom: 3px solid #{"var(--theme)"}; .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
border-bottom: 2px solid #{"var(--theme)"} !important;
color: #303133; color: #303133;
} }
/* submenu item */ /* submenu item */
.el-menu--horizontal > .el-submenu .el-submenu__title { .topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title {
float: left;
height: 50px !important; height: 50px !important;
line-height: 50px !important; line-height: 50px !important;
color: #999093 !important;
padding: 0 5px !important;
margin: 0 10px !important;
} }
</style> </style>

View File

@ -4,8 +4,8 @@
<hamburger id="hamburger-container" class="hamburger-container" :is-active="sidebar.opened" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" class="hamburger-container" :is-active="sidebar.opened" @toggleClick="toggleSideBar" />
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
<!-- <top-nav id="topmenu-container" class="topmenu-container" v-if="!topNav"/> --> <top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device!=='mobile'">
@ -46,6 +46,7 @@
<script> <script>
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import Breadcrumb from "@/components/Breadcrumb"; import Breadcrumb from "@/components/Breadcrumb";
import TopNav from '@/components/TopNav'
import Hamburger from "@/components/Hamburger"; import Hamburger from "@/components/Hamburger";
import Screenfull from "@/components/Screenfull"; import Screenfull from "@/components/Screenfull";
import SizeSelect from "@/components/SizeSelect"; import SizeSelect from "@/components/SizeSelect";
@ -56,6 +57,7 @@ import ZrDoc from '@/components/Zr/Doc'
export default { export default {
components: { components: {
Breadcrumb, Breadcrumb,
TopNav,
Hamburger, Hamburger,
Screenfull, Screenfull,
SizeSelect, SizeSelect,
@ -76,6 +78,11 @@ export default {
}); });
}, },
}, },
topNav: {
get() {
return this.$store.state.settings.topNav
}
}
}, },
methods: { methods: {
toggleSideBar() { toggleSideBar() {
@ -128,6 +135,11 @@ export default {
float: left; float: left;
} }
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container { .errLog-container {
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;

View File

@ -33,6 +33,14 @@
</div> </div>
<el-divider /> <el-divider />
<h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item">
<span>开启 TopNav</span>
<el-switch v-model="topNav" class="drawer-switch" />
</div>
<div class="drawer-item"> <div class="drawer-item">
<span>开启 Tags-Views</span> <span>开启 Tags-Views</span>
<el-switch v-model="tagsView" class="drawer-switch" /> <el-switch v-model="tagsView" class="drawer-switch" />
@ -78,6 +86,20 @@ export default {
}); });
}, },
}, },
topNav: {
get() {
return this.$store.state.settings.topNav
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'topNav',
value: val
})
if (!val) {
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes);
}
}
},
tagsView: { tagsView: {
get() { get() {
return this.$store.state.settings.tagsView; return this.$store.state.settings.tagsView;
@ -128,6 +150,7 @@ export default {
localStorage.setItem( localStorage.setItem(
"layout-setting", "layout-setting",
`{ `{
"topNav":${this.topNav},
"tagsView":${this.tagsView}, "tagsView":${this.tagsView},
"fixedHeader":${this.fixedHeader}, "fixedHeader":${this.fixedHeader},
"sidebarLogo":${this.sidebarLogo}, "sidebarLogo":${this.sidebarLogo},

View File

@ -1,8 +1,7 @@
<template> <template>
<div v-if="!item.hidden"> <div v-if="!item.hidden">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item> </el-menu-item>
@ -15,7 +14,6 @@
</template> </template>
<sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" /> <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child" :base-path="resolvePath(child.path)" class="nest-menu" />
</el-submenu> </el-submenu>
</div> </div>
</template> </template>
@ -49,16 +47,17 @@ export default {
this.onlyOneChild = null; this.onlyOneChild = null;
return {}; return {};
}, },
mounted() {},
methods: { methods: {
hasOneShowingChild(children = [], parent) { hasOneShowingChild(children = [], parent) {
if (!children) {
children = [];
}
const showingChildren = children.filter((item) => { const showingChildren = children.filter((item) => {
if (item.hidden) { if (item.hidden) {
return false; return false;
} else { } else {
// Temp set(will be used if only has one showing child) // Temp set(will be used if only has one showing child)
this.onlyOneChild = item; this.onlyOneChild = item;
// console.log(JSON.stringify(item));
return true; return true;
} }
}); });
@ -76,14 +75,17 @@ export default {
return false; return false;
}, },
resolvePath(routePath) { resolvePath(routePath, routeQuery) {
// console.log(routePath);
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath; return routePath;
} }
if (isExternal(this.basePath)) { if (isExternal(this.basePath)) {
return this.basePath; return this.basePath;
} }
if (routeQuery) {
let query = JSON.parse(routeQuery);
return { path: path.resolve(this.basePath, routePath), query: query };
}
return path.resolve(this.basePath, routePath); return path.resolve(this.basePath, routePath);
}, },
}, },

View File

@ -2,9 +2,11 @@
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> <div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
<logo v-if="showLogo" :collapse="isCollapse" /> <logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper"> <el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor" :unique-opened="true" :active-text-color="settings.theme" :collapse-transition="false" mode="vertical"> <el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground" :text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor" :unique-opened="true"
<sidebar-item v-for="(route, index) in permission_routes" :key="route.path + index" :item="route" :base-path="route.path" /> :active-text-color="settings.theme" :collapse-transition="false" mode="vertical">
<sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
@ -20,7 +22,7 @@ export default {
components: { SidebarItem, Logo }, components: { SidebarItem, Logo },
computed: { computed: {
...mapState(["settings"]), ...mapState(["settings"]),
...mapGetters(["permission_routes", "sidebar"]), ...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() { activeMenu() {
const route = this.$route; const route = this.$route;
const { meta, path } = route; const { meta, path } = route;

View File

@ -5,75 +5,81 @@
</template> </template>
<script> <script>
const tagAndTagSpacing = 4 // tagAndTagSpacing const tagAndTagSpacing = 4; // tagAndTagSpacing
export default { export default {
name: 'ScrollPane', name: "ScrollPane",
data() { data() {
return { return {
left: 0 left: 0,
} };
}, },
computed: { computed: {
scrollWrapper() { scrollWrapper() {
return this.$refs.scrollContainer.$refs.wrap return this.$refs.scrollContainer.$refs.wrap;
} },
}, },
mounted() { mounted() {
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true) this.scrollWrapper.addEventListener("scroll", this.emitScroll, true);
}, },
beforeDestroy() { beforeDestroy() {
this.scrollWrapper.removeEventListener('scroll', this.emitScroll) this.scrollWrapper.removeEventListener("scroll", this.emitScroll);
}, },
methods: { methods: {
handleScroll(e) { handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40 const eventDelta = e.wheelDelta || -e.deltaY * 40;
const $scrollWrapper = this.scrollWrapper const $scrollWrapper = this.scrollWrapper;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
}, },
emitScroll() { emitScroll() {
this.$emit('scroll') this.$emit("scroll");
}, },
moveToTarget(currentTag) { moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el const $container = this.$refs.scrollContainer.$el;
const $containerWidth = $container.offsetWidth const $containerWidth = $container.offsetWidth;
const $scrollWrapper = this.scrollWrapper const $scrollWrapper = this.scrollWrapper;
const tagList = this.$parent.$refs.tag const tagList = this.$parent.$refs.tag;
let firstTag = null let firstTag = null;
let lastTag = null let lastTag = null;
// find first tag and last tag // find first tag and last tag
if (tagList.length > 0) { if (tagList.length > 0) {
firstTag = tagList[0] firstTag = tagList[0];
lastTag = tagList[tagList.length - 1] lastTag = tagList[tagList.length - 1];
} }
if (firstTag === currentTag) { if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0 $scrollWrapper.scrollLeft = 0;
} else if (lastTag === currentTag) { } else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth $scrollWrapper.scrollLeft =
$scrollWrapper.scrollWidth - $containerWidth;
} else { } else {
// find preTag and nextTag // find preTag and nextTag
const currentIndex = tagList.findIndex(item => item === currentTag) const currentIndex = tagList.findIndex((item) => item === currentTag);
const prevTag = tagList[currentIndex - 1] const prevTag = tagList[currentIndex - 1];
const nextTag = tagList[currentIndex + 1] const nextTag = tagList[currentIndex + 1];
// the tag's offsetLeft after of nextTag // the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing const afterNextTagOffsetLeft =
nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing;
// the tag's offsetLeft before of prevTag // the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing const beforePrevTagOffsetLeft =
prevTag.$el.offsetLeft - tagAndTagSpacing;
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { if (
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth afterNextTagOffsetLeft >
$scrollWrapper.scrollLeft + $containerWidth
) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
} }
} }
} },
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -82,7 +88,7 @@ export default {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
/deep/ { ::v-deep {
.el-scrollbar__bar { .el-scrollbar__bar {
bottom: 0px; bottom: 0px;
} }

View File

@ -1,34 +1,26 @@
<template> <template>
<div id="tags-view-container" class="tags-view-container"> <div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll"> <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
<router-link <router-link v-for="tag in visitedViews" ref="tag" :key="tag.path" :class="isActive(tag)?'active':''" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" tag="span" class="tags-view-item" :style="activeStyle(tag)" @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
v-for="tag in visitedViews" @contextmenu.prevent.native="openMenu(tag,$event)">
ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
class="tags-view-item"
:style="activeStyle(tag)"
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)"
>
{{ tag.title }} {{ tag.title }}
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
</router-link> </router-link>
</scroll-pane> </scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">刷新页面</li> <li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭当前</li> <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
<li @click="closeOthersTags">关闭其他</li> <li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
<li @click="closeAllTags(selectedTag)">关闭所有</li> <li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li>
<li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li>
</ul> </ul>
</div> </div>
</template> </template>
<script> <script>
import ScrollPane from './ScrollPane' import ScrollPane from "./ScrollPane";
import path from 'path' import path from "path";
export default { export default {
components: { ScrollPane }, components: { ScrollPane },
@ -38,174 +30,216 @@ export default {
top: 0, top: 0,
left: 0, left: 0,
selectedTag: {}, selectedTag: {},
affixTags: [] affixTags: [],
} };
}, },
computed: { computed: {
visitedViews() { visitedViews() {
return this.$store.state.tagsView.visitedViews return this.$store.state.tagsView.visitedViews;
}, },
routes() { routes() {
return this.$store.state.permission.routes return this.$store.state.permission.routes;
}, },
theme() { theme() {
return this.$store.state.settings.theme; return this.$store.state.settings.theme;
} },
}, },
watch: { watch: {
$route() { $route() {
this.addTags() this.addTags();
this.moveToCurrentTag() this.moveToCurrentTag();
}, },
visible(value) { visible(value) {
if (value) { if (value) {
document.body.addEventListener('click', this.closeMenu) document.body.addEventListener("click", this.closeMenu);
} else { } else {
document.body.removeEventListener('click', this.closeMenu) document.body.removeEventListener("click", this.closeMenu);
} }
} },
}, },
mounted() { mounted() {
this.initTags() this.initTags();
this.addTags() this.addTags();
}, },
methods: { methods: {
isActive(route) { isActive(route) {
return route.path === this.$route.path return route.path === this.$route.path;
}, },
activeStyle(tag) { activeStyle(tag) {
if (!this.isActive(tag)) return {}; if (!this.isActive(tag)) return {};
return { return {
"background-color": this.theme, "background-color": this.theme,
"border-color": this.theme "border-color": this.theme,
}; };
}, },
isAffix(tag) { isAffix(tag) {
return tag.meta && tag.meta.affix return tag.meta && tag.meta.affix;
}, },
filterAffixTags(routes, basePath = '/') { isFirstView() {
let tags = [] try {
routes.forEach(route => { return (
this.selectedTag.fullPath === this.visitedViews[1].fullPath ||
this.selectedTag.fullPath === "/index"
);
} catch (err) {
return false;
}
},
isLastView() {
try {
return (
this.selectedTag.fullPath ===
this.visitedViews[this.visitedViews.length - 1].fullPath
);
} catch (err) {
return false;
}
},
filterAffixTags(routes, basePath = "/") {
let tags = [];
routes.forEach((route) => {
if (route.meta && route.meta.affix) { if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path) const tagPath = path.resolve(basePath, route.path);
tags.push({ tags.push({
fullPath: tagPath, fullPath: tagPath,
path: tagPath, path: tagPath,
name: route.name, name: route.name,
meta: { ...route.meta } meta: { ...route.meta },
}) });
} }
if (route.children) { if (route.children) {
const tempTags = this.filterAffixTags(route.children, route.path) const tempTags = this.filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) { if (tempTags.length >= 1) {
tags = [...tags, ...tempTags] tags = [...tags, ...tempTags];
} }
} }
}) });
return tags return tags;
}, },
initTags() { initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routes) const affixTags = (this.affixTags = this.filterAffixTags(this.routes));
for (const tag of affixTags) { for (const tag of affixTags) {
// Must have tag name // Must have tag name
if (tag.name) { if (tag.name) {
this.$store.dispatch('tagsView/addVisitedView', tag) this.$store.dispatch("tagsView/addVisitedView", tag);
} }
} }
}, },
addTags() { addTags() {
const { name } = this.$route const { name } = this.$route;
if (name) { if (name) {
this.$store.dispatch('tagsView/addView', this.$route) this.$store.dispatch("tagsView/addView", this.$route);
} }
return false return false;
}, },
moveToCurrentTag() { moveToCurrentTag() {
const tags = this.$refs.tag const tags = this.$refs.tag;
this.$nextTick(() => { this.$nextTick(() => {
for (const tag of tags) { for (const tag of tags) {
if (tag.to.path === this.$route.path) { if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag) this.$refs.scrollPane.moveToTarget(tag);
// when query is different then update // when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) { if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route) this.$store.dispatch("tagsView/updateVisitedView", this.$route);
} }
break break;
} }
} }
}) });
}, },
refreshSelectedTag(view) { refreshSelectedTag(view) {
this.$store.dispatch('tagsView/delCachedView', view).then(() => { this.$store.dispatch("tagsView/delCachedView", view).then(() => {
const { fullPath } = view const { fullPath } = view;
this.$nextTick(() => { this.$nextTick(() => {
this.$router.replace({ this.$router.replace({
path: '/redirect' + fullPath path: "/redirect" + fullPath,
}) });
}) });
}) });
}, },
closeSelectedTag(view) { closeSelectedTag(view) {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { this.$store
if (this.isActive(view)) { .dispatch("tagsView/delView", view)
this.toLastView(visitedViews, view) .then(({ visitedViews }) => {
} if (this.isActive(view)) {
}) this.toLastView(visitedViews, view);
}
});
},
closeRightTags() {
this.$store
.dispatch("tagsView/delRightTags", this.selectedTag)
.then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews);
}
});
},
closeLeftTags() {
this.$store
.dispatch("tagsView/delLeftTags", this.selectedTag)
.then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews);
}
});
}, },
closeOthersTags() { closeOthersTags() {
this.$router.push(this.selectedTag) this.$router.push(this.selectedTag);//.catch(() => {});
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { this.$store
this.moveToCurrentTag() .dispatch("tagsView/delOthersViews", this.selectedTag)
}) .then(() => {
this.moveToCurrentTag();
});
}, },
closeAllTags(view) { closeAllTags(view) {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { this.$store.dispatch("tagsView/delAllViews").then(({ visitedViews }) => {
if (this.affixTags.some(tag => tag.path === this.$route.path)) { if (this.affixTags.some((tag) => tag.path === this.$route.path)) {
return return;
} }
this.toLastView(visitedViews, view) this.toLastView(visitedViews, view);
}) });
}, },
toLastView(visitedViews, view) { toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0] const latestView = visitedViews.slice(-1)[0];
if (latestView) { if (latestView) {
this.$router.push(latestView.fullPath) this.$router.push(latestView.fullPath);
} else { } else {
// now the default is to redirect to the home page if there is no tags-view, // now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs. // you can adjust it according to your needs.
if (view.name === 'Dashboard') { if (view.name === "Dashboard") {
// to reload home page // to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath }) this.$router.replace({ path: "/redirect" + view.fullPath });
} else { } else {
this.$router.push('/') this.$router.push("/");
} }
} }
}, },
openMenu(tag, e) { openMenu(tag, e) {
const menuMinWidth = 105 const menuMinWidth = 105;
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = this.$el.offsetWidth // container width const offsetWidth = this.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary const maxLeft = offsetWidth - menuMinWidth; // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right const left = e.clientX - offsetLeft + 15; // 15: margin right
if (left > maxLeft) { if (left > maxLeft) {
this.left = maxLeft this.left = maxLeft;
} else { } else {
this.left = left this.left = left;
} }
this.top = e.clientY this.top = e.clientY;
this.visible = true this.visible = true;
this.selectedTag = tag this.selectedTag = tag;
}, },
closeMenu() { closeMenu() {
this.visible = false this.visible = false;
}, },
handleScroll() { handleScroll() {
this.closeMenu() this.closeMenu();
} },
} },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -214,7 +248,7 @@ export default {
width: 100%; width: 100%;
background: #fff; background: #fff;
border-bottom: 1px solid #d8dce5; 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); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
display: inline-block; display: inline-block;
@ -240,7 +274,7 @@ export default {
color: #fff; color: #fff;
border-color: #42b983; border-color: #42b983;
&::before { &::before {
content: ''; content: "";
background: #fff; background: #fff;
display: inline-block; display: inline-block;
width: 8px; width: 8px;
@ -263,7 +297,7 @@ export default {
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
color: #333; color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li { li {
margin: 0; margin: 0;
padding: 7px 16px; padding: 7px 16px;
@ -286,10 +320,10 @@ export default {
vertical-align: 2px; vertical-align: 2px;
border-radius: 50%; border-radius: 50%;
text-align: center; text-align: center;
transition: all .3s cubic-bezier(.645, .045, .355, 1); transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%; transform-origin: 100% 50%;
&:before { &:before {
transform: scale(.6); transform: scale(0.6);
display: inline-block; display: inline-block;
vertical-align: -3px; vertical-align: -3px;
} }

View File

@ -64,45 +64,45 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/assets/styles/mixin.scss"; @import "~@/assets/styles/mixin.scss";
@import "~@/assets/styles/variables.scss"; @import "~@/assets/styles/variables.scss";
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;
position: relative; position: relative;
height: 100%; height: 100%;
width: 100%; width: 100%;
&.mobile.openSidebar { &.mobile.openSidebar {
position: fixed;
top: 0;
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
.fixed-header {
position: fixed; position: fixed;
top: 0; top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s;
} }
}
.hideSidebar .fixed-header { .drawer-bg {
width: calc(100% - 54px) background: #000;
} opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
.mobile .fixed-header { .fixed-header {
width: 100%; position: fixed;
} top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: calc(100% - 54px);
}
.mobile .fixed-header {
width: 100%;
}
</style> </style>

View File

@ -37,15 +37,15 @@ Vue.prototype.selectDictLabels = selectDictLabels
Vue.prototype.download = download Vue.prototype.download = download
Vue.prototype.handleTree = handleTree Vue.prototype.handleTree = handleTree
Vue.prototype.msgSuccess = function (msg) { Vue.prototype.msgSuccess = function(msg) {
this.$message({ showClose: true, message: msg, type: "success" }); this.$message({ showClose: true, message: msg, type: "success" });
} }
Vue.prototype.msgError = function (msg) { Vue.prototype.msgError = function(msg) {
this.$message({ showClose: true, message: msg, type: "error" }); this.$message({ showClose: true, message: msg, type: "error" });
} }
Vue.prototype.msgInfo = function (msg) { Vue.prototype.msgInfo = function(msg) {
this.$message.info(msg); this.$message.info(msg);
} }

View File

@ -25,17 +25,14 @@ import Layout from '@/layout'
*/ */
// 公共路由 // 公共路由
export const constantRoutes = [ export const constantRoutes = [{
{
path: '/redirect', path: '/redirect',
component: Layout, component: Layout,
hidden: true, hidden: true,
children: [ children: [{
{ path: '/redirect/:path(.*)',
path: '/redirect/:path(.*)', component: (resolve) => require(['@/views/redirect'], resolve)
component: (resolve) => require(['@/views/redirect'], resolve) }]
}
]
}, },
{ {
path: '/login', path: '/login',
@ -56,56 +53,25 @@ export const constantRoutes = [
path: '', path: '',
component: Layout, component: Layout,
redirect: 'index', redirect: 'index',
children: [ children: [{
{ path: 'index',
path: 'index', component: (resolve) => require(['@/views/index'], resolve),
component: (resolve) => require(['@/views/index'], resolve), name: 'Index',
name: '首页', meta: { title: '首页', icon: 'dashboard', affix: true }
meta: { title: '首页', icon: 'icon1', noCache: true, affix: true } }],
}
],
hidden: true
}, },
// {
// path: '',
// component: Layout,
// redirect: 'index',
// children: [
// {
// path: 'dashboard',
// component: (resolve) => require(['@/views/index_v1'], resolve),
// name: '控制台',
// meta: { title: '控制台', icon: 'dashboard', noCache: true, affix: true }
// }
// ]
// },
{ {
path: '/user', path: '/user',
component: Layout, component: Layout,
hidden: true, hidden: true,
redirect: 'noredirect', redirect: 'noredirect',
children: [ children: [{
{ path: 'profile',
path: 'profile', component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
component: (resolve) => require(['@/views/system/user/profile/index'], resolve), name: 'Profile',
name: 'Profile', meta: { title: '个人中心', icon: 'user' }
meta: { title: '个人中心', icon: 'user' } }]
}
]
}, },
// {
// path: '/dict',
// component: Layout,
// hidden: true,
// children: [
// {
// path: 'type/data/:dictId(\\d+)',
// component: (resolve) => require(['@/views/system/dict/data'], resolve),
// name: 'Data',
// meta: { title: '字典数据', icon: '' }
// }
// ]
// }
] ]
export default new Router({ export default new Router({

View File

@ -7,13 +7,18 @@ module.exports = {
/** /**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light * 侧边栏主题 深色主题theme-dark浅色主题theme-light
*/ */
sideTheme: 'theme-light', sideTheme: 'theme-light',
/** /**
* 显示是否系统布局配置 * 显示是否系统布局配置
*/ */
showSettings: false, showSettings: false,
/**
* 是否显示顶部导航
*/
topNav: false,
/** /**
* 是否显示 tagsView * 是否显示 tagsView
*/ */

View File

@ -1,16 +1,19 @@
const getters = { const getters = {
sidebar: state => state.app.sidebar, sidebar: state => state.app.sidebar,
size: state => state.app.size, size: state => state.app.size,
device: state => state.app.device, device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews, visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews, cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token, token: state => state.user.token,
avatar: state => state.user.avatar, avatar: state => state.user.avatar,
name: state => state.user.name, name: state => state.user.name,
introduction: state => state.user.introduction, introduction: state => state.user.introduction,
roles: state => state.user.roles, roles: state => state.user.roles,
permissions: state => state.user.permissions, permissions: state => state.user.permissions,
permission_routes: state => state.permission.routes, permission_routes: state => state.permission.routes,
userinfo: state => state.user.userInfo, userinfo: state => state.user.userInfo,
topbarRouters: state => state.permission.topbarRouters,
defaultRoutes: state => state.permission.defaultRoutes,
sidebarRouters: state => state.permission.sidebarRouters,
} }
export default getters export default getters

View File

@ -6,13 +6,30 @@ import ParentView from '@/components/ParentView';
const permission = { const permission = {
state: { state: {
routes: [], routes: [],
addRoutes: [] addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: []
}, },
mutations: { mutations: {
SET_ROUTES: (state, routes) => { SET_ROUTES: (state, routes) => {
state.addRoutes = routes state.addRoutes = routes
state.routes = constantRoutes.concat(routes) state.routes = constantRoutes.concat(routes)
} },
SET_DEFAULT_ROUTES: (state, routes) => {
state.defaultRoutes = constantRoutes.concat(routes)
},
SET_TOPBAR_ROUTES: (state, routes) => {
// 顶部导航菜单默认添加统计报表栏指向首页
// const index = [{
// path: 'index',
// meta: { title: '系统首页', icon: 'dashboard' }
// }]
state.topbarRouters = routes; //.concat(index);
},
SET_SIDEBAR_ROUTERS: (state, routes) => {
state.sidebarRouters = routes
},
}, },
actions: { actions: {
// 生成路由 // 生成路由
@ -20,10 +37,16 @@ const permission = {
return new Promise(resolve => { return new Promise(resolve => {
// 向后端请求路由数据 // 向后端请求路由数据
getRouters().then(res => { getRouters().then(res => {
const accessedRoutes = filterAsyncRouter(res.data) const sdata = JSON.parse(JSON.stringify(res.data))
// accessedRoutes.push({ path: '*', redirect: '/404', hidden: true }) const rdata = JSON.parse(JSON.stringify(res.data))
commit('SET_ROUTES', accessedRoutes) const sidebarRoutes = filterAsyncRouter(sdata)
resolve(accessedRoutes) const rewriteRoutes = filterAsyncRouter(rdata, false, true)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
commit('SET_ROUTES', rewriteRoutes)
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
commit('SET_TOPBAR_ROUTES', sidebarRoutes)
resolve(rewriteRoutes)
}) })
}) })
} }
@ -31,9 +54,11 @@ const permission = {
} }
// 遍历后台传来的路由字符串,转换为组件对象 // 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) { function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => { return asyncRouterMap.filter(route => {
// console.log(JSON.stringify(route)) if (type && route.children) {
route.children = filterChildren(route.children)
}
if (route.component) { if (route.component) {
// Layout ParentView 组件特殊处理 // Layout ParentView 组件特殊处理
if (route.component === 'Layout') { if (route.component === 'Layout') {
@ -45,12 +70,39 @@ function filterAsyncRouter(asyncRouterMap) {
} }
} }
if (route.children != null && route.children && route.children.length) { if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children) route.children = filterAsyncRouter(route.children, route, type)
} else {
delete route['children']
delete route['redirect']
} }
return true return true
}) })
} }
function filterChildren(childrenMap, lastRouter = false) {
var children = []
childrenMap.forEach((el, index) => {
if (el.children && el.children.length) {
if (el.component === 'ParentView') {
el.children.forEach(c => {
c.path = el.path + '/' + c.path
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c))
return
}
children.push(c)
})
return
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path
}
children = children.concat(el)
})
return children
}
export const loadView = (view) => { // 路由懒加载 export const loadView = (view) => { // 路由懒加载
return (resolve) => require([`@/views/${view}`], resolve) return (resolve) => require([`@/views/${view}`], resolve)
} }

View File

@ -1,16 +1,16 @@
import defaultSettings from '@/settings' import defaultSettings from '@/settings'
const { theme, sideTheme, showSettings, tagsView, fixedHeader, sidebarLogo } = defaultSettings const { theme, sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo } = defaultSettings
const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || ''
const state = { const state = {
theme: storageSetting.theme || theme,//主题颜色 theme: storageSetting.theme || theme, //主题颜色
sideTheme: storageSetting.sideTheme || sideTheme,//侧边主题样式 sideTheme: storageSetting.sideTheme || sideTheme, //侧边主题样式
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
showSettings: showSettings, showSettings: showSettings,
tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView,
fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader,
sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo,
// topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,
// dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle // dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle
} }
@ -35,4 +35,3 @@ export default {
mutations, mutations,
actions actions
} }

View File

@ -62,6 +62,40 @@ const mutations = {
break break
} }
} }
},
DEL_RIGHT_VIEWS: (state, view) => {
const index = state.visitedViews.findIndex(v => v.path === view.path)
if (index === -1) {
return
}
state.visitedViews = state.visitedViews.filter((item, idx) => {
if (idx <= index || (item.meta && item.meta.affix)) {
return true
}
const i = state.cachedViews.indexOf(item.name)
if (i > -1) {
state.cachedViews.splice(i, 1)
}
return false
})
},
DEL_LEFT_VIEWS: (state, view) => {
const index = state.visitedViews.findIndex(v => v.path === view.path)
if (index === -1) {
return
}
state.visitedViews = state.visitedViews.filter((item, idx) => {
if (idx >= index || (item.meta && item.meta.affix)) {
return true
}
const i = state.cachedViews.indexOf(item.name)
if (i > -1) {
state.cachedViews.splice(i, 1)
}
return false
})
} }
} }
@ -148,7 +182,21 @@ const actions = {
updateVisitedView({ commit }, view) { updateVisitedView({ commit }, view) {
commit('UPDATE_VISITED_VIEW', view) commit('UPDATE_VISITED_VIEW', view)
} },
delRightTags({ commit }, view) {
return new Promise(resolve => {
commit('DEL_RIGHT_VIEWS', view)
resolve([...state.visitedViews])
})
},
delLeftTags({ commit }, view) {
return new Promise(resolve => {
commit('DEL_LEFT_VIEWS', view)
resolve([...state.visitedViews])
})
},
} }
export default { export default {

View File

@ -39,7 +39,7 @@
import { getCodeImg } from "@/api/system/login"; import { getCodeImg } from "@/api/system/login";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt"; import { encrypt, decrypt } from "@/utils/jsencrypt";
import defaultSettings from '@/settings' import defaultSettings from "@/settings";
export default { export default {
name: "Login", name: "Login",
@ -98,8 +98,7 @@ export default {
this.loginForm = { this.loginForm = {
username: username === undefined ? this.loginForm.username : username, username: username === undefined ? this.loginForm.username : username,
password: password: password === undefined ? this.loginForm.password : password,
password === undefined ? this.loginForm.password : password,
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe), rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
}; };
}, },
@ -152,7 +151,7 @@ export default {
height: 100%; height: 100%;
// background-image: url("../assets/image/login-background.jpg"); // background-image: url("../assets/image/login-background.jpg");
background-size: cover; background-size: cover;
background-color: rgba(56,157,170,.82); background-color: rgba(56, 157, 170, 0.82);
} }
.title { .title {
margin: 0px auto 30px auto; margin: 0px auto 30px auto;
@ -164,7 +163,7 @@ export default {
.login-form { .login-form {
border-radius: 6px; border-radius: 6px;
// background: #ffffff; // background: #ffffff;
background-color: hsla(0,0%,100%,.3); background-color: hsla(0, 0%, 100%, 0.3);
width: 350px; width: 350px;
padding: 25px 25px 5px 25px; padding: 25px 25px 5px 25px;
.el-input { .el-input {

View File

@ -64,7 +64,7 @@
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body @close="cancel"> <el-dialog :title="title" :visible.sync="open" width="600px" append-to-body @close="cancel">
<el-form ref="form" :model="form" :rules="rules" label-width="100px"> <el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24" v-if="this.form.id">
<el-form-item label="任务ID"> <el-form-item label="任务ID">
<div>{{form.id}}</div> <div>{{form.id}}</div>
</el-form-item> </el-form-item>

View File

@ -260,7 +260,7 @@ INSERT INTO sys_menu VALUES (113, '缓存监控', 2, 12, 'cache', 'monitor/cache
INSERT INTO sys_menu VALUES (114, '表单构建', 3, 13, 'build', 'tool/build/index', 0, 0, 'C', '0', '0', 'tool:build:list', 'build', '', SYSDATE(), '', NULL, '表单构建菜单'); INSERT INTO sys_menu VALUES (114, '表单构建', 3, 13, 'build', 'tool/build/index', 0, 0, 'C', '0', '0', 'tool:build:list', 'build', '', SYSDATE(), '', NULL, '表单构建菜单');
INSERT INTO sys_menu VALUES (115, '代码生成', 3, 14, 'gen', 'tool/gen/index', 0, 0, 'C', '0', '0', 'tool:gen:list', 'code', '', SYSDATE(), '', NULL, '代码生成菜单'); INSERT INTO sys_menu VALUES (115, '代码生成', 3, 14, 'gen', 'tool/gen/index', 0, 0, 'C', '0', '0', 'tool:gen:list', 'code', '', SYSDATE(), '', NULL, '代码生成菜单');
INSERT INTO sys_menu VALUES (116, '系统接口', 3, 15, 'swagger', 'tool/swagger/index', 0, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', '', SYSDATE(), '', NULL, '系统接口菜单'); INSERT INTO sys_menu VALUES (116, '系统接口', 3, 15, 'swagger', 'tool/swagger/index', 0, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', '', SYSDATE(), '', NULL, '系统接口菜单');
INSERT INTO sys_menu VALUES (117, '发送邮件', 3, 16, '/sendEmail', 'tool/email/sendEmail', 0, 0, 'C', '0', '0', 'tool:email:send', 'message', '', SYSDATE(), '', NULL, '发送邮件菜单'); INSERT INTO sys_menu VALUES (117, '发送邮件', 3, 16, 'sendEmail', 'tool/email/sendEmail', 0, 0, 'C', '0', '0', 'tool:email:send', 'message', '', SYSDATE(), '', NULL, '发送邮件菜单');
INSERT INTO sys_menu VALUES (118, '文章列表', 4, 1, 'index', 'system/article/manager', 0, 0, 'C', '0', '0', 'system:article:list', 'list', '', SYSDATE(), '', NULL, NULL); INSERT INTO sys_menu VALUES (118, '文章列表', 4, 1, 'index', 'system/article/manager', 0, 0, 'C', '0', '0', 'system:article:list', 'list', '', SYSDATE(), '', NULL, NULL);
-- 三级菜单日志管理 -- 三级菜单日志管理

Binary file not shown.