Pre Merge pull request !410 from 璐先生/master

This commit is contained in:
璐先生 2022-03-05 14:12:10 +00:00 committed by Gitee
commit 69218f84d7
7 changed files with 851 additions and 850 deletions

View File

@ -92,16 +92,13 @@ export default {
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("/"));
} else if ("/index" == path || "" == path) { } else if ("/index" == path || "" == path) {
if (!this.isFrist) { this.isFrist = true;
this.isFrist = true;
} else {
activePath = "index";
}
} }
var routes = this.activeRoutes(activePath); var routes = this.activeRoutes(activePath);
if (routes.length === 0) { if (routes.length === 0) {
activePath = this.currentIndex || this.defaultRouter() this.$store.commit("SET_SIDEBAR_MENU_HIDE", true);
this.activeRoutes(activePath); }else {
this.$store.commit("SET_SIDEBAR_MENU_HIDE", false);
} }
return activePath; return activePath;
}, },
@ -142,22 +139,26 @@ export default {
// /redirect // /redirect
this.$router.push({ path: key.replace("/redirect", "") }); this.$router.push({ path: key.replace("/redirect", "") });
} else { } else {
// //
this.activeRoutes(key); this.activeRoutes(key,true);
} }
}, },
// //
activeRoutes(key) { activeRoutes(key,isTop) {
var routes = []; var routes = [];
if (this.childrenMenus && this.childrenMenus.length > 0) { if (this.childrenMenus && this.childrenMenus.length > 0) {
this.childrenMenus.map((item) => { this.childrenMenus.map((item) => {
if (key == item.parentPath || (key == "index" && "" == item.path)) { if (key == item.parentPath) {
routes.push(item); routes.push(item);
} }
}); });
} }
if(routes.length > 0) { if(routes.length > 0) {
//TODO
this.$store.commit("SET_SIDEBAR_ROUTERS", routes); this.$store.commit("SET_SIDEBAR_ROUTERS", routes);
if(isTop){
this.$router.push({path:routes[0].path});
}
} }
return routes; return routes;
}, },

View File

@ -1,256 +1,256 @@
<template> <template>
<div class="drawer-container"> <div class="drawer-container">
<div> <div>
<div class="setting-drawer-content"> <div class="setting-drawer-content">
<div class="setting-drawer-title"> <div class="setting-drawer-title">
<h3 class="drawer-title">主题风格设置</h3> <h3 class="drawer-title">主题风格设置</h3>
</div> </div>
<div class="setting-drawer-block-checbox"> <div class="setting-drawer-block-checbox">
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')"> <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
<img src="@/assets/images/dark.svg" alt="dark"> <img src="@/assets/images/dark.svg" alt="dark">
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check"> <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" <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
focusable="false" class=""> focusable="false" class="">
<path <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"/> 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> </svg>
</i> </i>
</div> </div>
</div> </div>
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')"> <div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
<img src="@/assets/images/light.svg" alt="light"> <img src="@/assets/images/light.svg" alt="light">
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;"> <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check"> <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" <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
focusable="false" class=""> focusable="false" class="">
<path <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"/> 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> </svg>
</i> </i>
</div> </div>
</div> </div>
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
<span>主题颜色</span> <span>主题颜色</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
</div> </div>
</div> </div>
<el-divider/> <el-divider/>
<h3 class="drawer-title">系统布局配置</h3> <h3 class="drawer-title">系统布局配置</h3>
<div class="drawer-item"> <div class="drawer-item">
<span>开启 TopNav</span> <span>开启 TopNav</span>
<el-switch v-model="topNav" class="drawer-switch" /> <el-switch v-model="topNav" class="drawer-switch" />
</div> </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" />
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
<span>固定 Header</span> <span>固定 Header</span>
<el-switch v-model="fixedHeader" class="drawer-switch" /> <el-switch v-model="fixedHeader" class="drawer-switch" />
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
<span>显示 Logo</span> <span>显示 Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" /> <el-switch v-model="sidebarLogo" class="drawer-switch" />
</div> </div>
<div class="drawer-item"> <div class="drawer-item">
<span>动态标题</span> <span>动态标题</span>
<el-switch v-model="dynamicTitle" class="drawer-switch" /> <el-switch v-model="dynamicTitle" class="drawer-switch" />
</div> </div>
<el-divider/> <el-divider/>
<el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">保存配置</el-button> <el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">保存配置</el-button>
<el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">重置配置</el-button> <el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">重置配置</el-button>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ThemePicker from '@/components/ThemePicker' import ThemePicker from '@/components/ThemePicker'
export default { export default {
components: { ThemePicker }, components: { ThemePicker },
data() { data() {
return { return {
theme: this.$store.state.settings.theme, theme: this.$store.state.settings.theme,
sideTheme: this.$store.state.settings.sideTheme sideTheme: this.$store.state.settings.sideTheme
}; };
}, },
computed: { computed: {
fixedHeader: { fixedHeader: {
get() { get() {
return this.$store.state.settings.fixedHeader return this.$store.state.settings.fixedHeader
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'fixedHeader', key: 'fixedHeader',
value: val value: val
}) })
} }
}, },
topNav: { topNav: {
get() { get() {
return this.$store.state.settings.topNav return this.$store.state.settings.topNav
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'topNav', key: 'topNav',
value: val value: val
}) })
if (!val) { if (!val) {
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes); 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
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'tagsView', key: 'tagsView',
value: val value: val
}) })
} }
}, },
sidebarLogo: { sidebarLogo: {
get() { get() {
return this.$store.state.settings.sidebarLogo return this.$store.state.settings.sidebarLogo
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'sidebarLogo', key: 'sidebarLogo',
value: val value: val
}) })
} }
}, },
dynamicTitle: { dynamicTitle: {
get() { get() {
return this.$store.state.settings.dynamicTitle return this.$store.state.settings.dynamicTitle
}, },
set(val) { set(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'dynamicTitle', key: 'dynamicTitle',
value: val value: val
}) })
} }
}, },
}, },
methods: { methods: {
themeChange(val) { themeChange(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'theme', key: 'theme',
value: val value: val
}) })
this.theme = val; this.theme = val;
}, },
handleTheme(val) { handleTheme(val) {
this.$store.dispatch('settings/changeSetting', { this.$store.dispatch('settings/changeSetting', {
key: 'sideTheme', key: 'sideTheme',
value: val value: val
}) })
this.sideTheme = val; this.sideTheme = val;
}, },
saveSetting() { saveSetting() {
this.$modal.loading("正在保存到本地,请稍候..."); this.$modal.loading("正在保存到本地,请稍候...");
this.$cache.local.set( this.$cache.local.set(
"layout-setting", "layout-setting",
`{ `{
"topNav":${this.topNav}, "topNav":${this.topNav},
"tagsView":${this.tagsView}, "tagsView":${this.tagsView},
"fixedHeader":${this.fixedHeader}, "fixedHeader":${this.fixedHeader},
"sidebarLogo":${this.sidebarLogo}, "sidebarLogo":${this.sidebarLogo},
"dynamicTitle":${this.dynamicTitle}, "dynamicTitle":${this.dynamicTitle},
"sideTheme":"${this.sideTheme}", "sideTheme":"${this.sideTheme}",
"theme":"${this.theme}" "theme":"${this.theme}"
}` }`
); );
setTimeout(this.$modal.closeLoading(), 1000) setTimeout(this.$modal.closeLoading(), 1000)
}, },
resetSetting() { resetSetting() {
this.$modal.loading("正在清除设置缓存并刷新,请稍候..."); this.$modal.loading("正在清除设置缓存并刷新,请稍候...");
this.$cache.local.remove("layout-setting") this.$cache.local.remove("layout-setting")
setTimeout("window.location.reload()", 1000) setTimeout("window.location.reload()", 1000)
} }
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.setting-drawer-content { .setting-drawer-content {
.setting-drawer-title { .setting-drawer-title {
margin-bottom: 12px; margin-bottom: 12px;
color: rgba(0, 0, 0, .85); color: rgba(0, 0, 0, .85);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
font-weight: bold; font-weight: bold;
} }
.setting-drawer-block-checbox { .setting-drawer-block-checbox {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
margin-top: 10px; margin-top: 10px;
margin-bottom: 20px; margin-bottom: 20px;
.setting-drawer-block-checbox-item { .setting-drawer-block-checbox-item {
position: relative; position: relative;
margin-right: 16px; margin-right: 16px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
img { img {
width: 48px; width: 48px;
height: 48px; height: 48px;
} }
.setting-drawer-block-checbox-selectIcon { .setting-drawer-block-checbox-selectIcon {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding-top: 15px; padding-top: 15px;
padding-left: 24px; padding-left: 24px;
color: #1890ff; color: #1890ff;
font-weight: 700; font-weight: 700;
font-size: 14px; font-size: 14px;
} }
} }
} }
} }
.drawer-container { .drawer-container {
padding: 24px; padding: 24px;
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
word-wrap: break-word; word-wrap: break-word;
.drawer-title { .drawer-title {
margin-bottom: 12px; margin-bottom: 12px;
color: rgba(0, 0, 0, .85); color: rgba(0, 0, 0, .85);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
} }
.drawer-item { .drawer-item {
color: rgba(0, 0, 0, .65); color: rgba(0, 0, 0, .65);
font-size: 14px; font-size: 14px;
padding: 12px 0; padding: 12px 0;
} }
.drawer-switch { .drawer-switch {
float: right float: right
} }
} }
</style> </style>

View File

@ -1,57 +1,57 @@
<template> <template>
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"> <div v-show="!sidebarMenuHide" :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 <el-menu
:default-active="activeMenu" :default-active="activeMenu"
:collapse="isCollapse" :collapse="isCollapse"
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground" :background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor" :text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
:unique-opened="true" :unique-opened="true"
:active-text-color="settings.theme" :active-text-color="settings.theme"
:collapse-transition="false" :collapse-transition="false"
mode="vertical" mode="vertical"
> >
<sidebar-item <sidebar-item
v-for="(route, index) in sidebarRouters" v-for="(route, index) in sidebarRouters"
:key="route.path + index" :key="route.path + index"
:item="route" :item="route"
:base-path="route.path" :base-path="route.path"
/> />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapState } from "vuex"; import { mapGetters, mapState } from "vuex";
import Logo from "./Logo"; import Logo from "./Logo";
import SidebarItem from "./SidebarItem"; import SidebarItem from "./SidebarItem";
import variables from "@/assets/styles/variables.scss"; import variables from "@/assets/styles/variables.scss";
export default { export default {
components: { SidebarItem, Logo }, components: { SidebarItem, Logo },
computed: { computed: {
...mapState(["settings"]), ...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]), ...mapGetters(["sidebarRouters", "sidebar","sidebarMenuHide"]),
activeMenu() { activeMenu() {
const route = this.$route; const route = this.$route;
const { meta, path } = route; const { meta, path } = route;
// if set path, the sidebar will highlight the path you set // if set path, the sidebar will highlight the path you set
if (meta.activeMenu) { if (meta.activeMenu) {
return meta.activeMenu; return meta.activeMenu;
} }
return path; return path;
}, },
showLogo() { showLogo() {
return this.$store.state.settings.sidebarLogo; return this.$store.state.settings.sidebarLogo;
}, },
variables() { variables() {
return variables; return variables;
}, },
isCollapse() { isCollapse() {
return !this.sidebar.opened; return !this.sidebar.opened;
} }
} }
}; };
</script> </script>

View File

@ -1,326 +1,326 @@
<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" v-for="tag in visitedViews"
ref="tag" ref="tag"
:key="tag.path" :key="tag.path"
:class="isActive(tag)?'active':''" :class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span" tag="span"
class="tags-view-item" class="tags-view-item"
:style="activeStyle(tag)" :style="activeStyle(tag)"
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''" @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)" @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)"><i class="el-icon-refresh-right"></i> 刷新页面</li> <li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li> <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
<li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li> <li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
<li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</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 v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></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 },
data() { data() {
return { return {
visible: false, visible: false,
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
}, },
isFirstView() { isFirstView() {
try { try {
return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index' return this.selectedTag.fullPath === this.visitedViews[1].fullPath || this.selectedTag.fullPath === '/index'
} catch (err) { } catch (err) {
return false return false
} }
}, },
isLastView() { isLastView() {
try { try {
return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
} catch (err) { } catch (err) {
return false return false
} }
}, },
filterAffixTags(routes, basePath = '/') { filterAffixTags(routes, basePath = '/') {
let tags = [] let tags = []
routes.forEach(route => { 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.$tab.refreshPage(view); this.$tab.refreshPage(view);
}, },
closeSelectedTag(view) { closeSelectedTag(view) {
this.$tab.closePage(view).then(({ visitedViews }) => { this.$tab.closePage(view).then(({ visitedViews }) => {
if (this.isActive(view)) { if (this.isActive(view)) {
this.toLastView(visitedViews, view) this.toLastView(visitedViews, view)
} }
}) })
}, },
closeRightTags() { closeRightTags() {
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => { this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews) this.toLastView(visitedViews)
} }
}) })
}, },
closeLeftTags() { closeLeftTags() {
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => { this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews) this.toLastView(visitedViews)
} }
}) })
}, },
closeOthersTags() { closeOthersTags() {
this.$router.push(this.selectedTag).catch(()=>{}); this.$router.push(this.selectedTag).catch(()=>{});
this.$tab.closeOtherPage(this.selectedTag).then(() => { this.$tab.closeOtherPage(this.selectedTag).then(() => {
this.moveToCurrentTag() this.moveToCurrentTag()
}) })
}, },
closeAllTags(view) { closeAllTags(view) {
this.$tab.closeAllPage().then(({ visitedViews }) => { this.$tab.closeAllPage().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>
.tags-view-container { .tags-view-container {
height: 34px; height: 34px;
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, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
display: inline-block; display: inline-block;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
height: 26px; height: 26px;
line-height: 26px; line-height: 26px;
border: 1px solid #d8dce5; border: 1px solid #d8dce5;
color: #495060; color: #495060;
background: #fff; background: #fff;
padding: 0 8px; padding: 0 8px;
font-size: 12px; font-size: 12px;
margin-left: 5px; margin-left: 5px;
margin-top: 4px; margin-top: 4px;
&:first-of-type { &:first-of-type {
margin-left: 15px; margin-left: 15px;
} }
&:last-of-type { &:last-of-type {
margin-right: 15px; margin-right: 15px;
} }
&.active { &.active {
background-color: #42b983; background-color: #42b983;
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;
height: 8px; height: 8px;
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
margin-right: 2px; margin-right: 2px;
} }
} }
} }
} }
.contextmenu { .contextmenu {
margin: 0; margin: 0;
background: #fff; background: #fff;
z-index: 3000; z-index: 3000;
position: absolute; position: absolute;
list-style-type: none; list-style-type: none;
padding: 5px 0; padding: 5px 0;
border-radius: 4px; border-radius: 4px;
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, .3);
li { li {
margin: 0; margin: 0;
padding: 7px 16px; padding: 7px 16px;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: #eee; background: #eee;
} }
} }
} }
} }
</style> </style>
<style lang="scss"> <style lang="scss">
//reset element css of el-icon-close //reset element css of el-icon-close
.tags-view-wrapper { .tags-view-wrapper {
.tags-view-item { .tags-view-item {
.el-icon-close { .el-icon-close {
width: 16px; width: 16px;
height: 16px; height: 16px;
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 .3s cubic-bezier(.645, .045, .355, 1);
transform-origin: 100% 50%; transform-origin: 100% 50%;
&:before { &:before {
transform: scale(.6); transform: scale(.6);
display: inline-block; display: inline-block;
vertical-align: -3px; vertical-align: -3px;
} }
&:hover { &:hover {
background-color: #b4bccc; background-color: #b4bccc;
color: #fff; color: #fff;
} }
} }
} }
} }
</style> </style>

View File

@ -1,44 +1,44 @@
module.exports = { module.exports = {
/** /**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light * 侧边栏主题 深色主题theme-dark浅色主题theme-light
*/ */
sideTheme: 'theme-dark', sideTheme: 'theme-dark',
/** /**
* 是否系统布局配置 * 是否系统布局配置
*/ */
showSettings: false, showSettings: false,
/** /**
* 是否显示顶部导航 * 是否显示顶部导航
*/ */
topNav: false, topNav: true,
/** /**
* 是否显示 tagsView * 是否显示 tagsView
*/ */
tagsView: true, tagsView: true,
/** /**
* 是否固定头部 * 是否固定头部
*/ */
fixedHeader: false, fixedHeader: false,
/** /**
* 是否显示logo * 是否显示logo
*/ */
sidebarLogo: true, sidebarLogo: true,
/** /**
* 是否显示动态标题 * 是否显示动态标题
*/ */
dynamicTitle: false, dynamicTitle: false,
/** /**
* @type {string | array} 'production' | ['production', 'development'] * @type {string | array} 'production' | ['production', 'development']
* @description Need show err logs component. * @description Need show err logs component.
* The default is only used in the production env * The default is only used in the production env
* If you want to also use it in dev, you can pass ['production', 'development'] * If you want to also use it in dev, you can pass ['production', 'development']
*/ */
errorLog: 'production' errorLog: 'production'
} }

View File

@ -1,18 +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,
topbarRouters:state => state.permission.topbarRouters, topbarRouters:state => state.permission.topbarRouters,
defaultRoutes:state => state.permission.defaultRoutes, defaultRoutes:state => state.permission.defaultRoutes,
sidebarRouters:state => state.permission.sidebarRouters, sidebarRouters:state => state.permission.sidebarRouters,
} sidebarMenuHide:state => state.permission.sidebarMenuHide,
export default getters }
export default getters

View File

@ -1,138 +1,137 @@
import auth from '@/plugins/auth' import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router' import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu' import { getRouters } from '@/api/menu'
import Layout from '@/layout/index' import Layout from '@/layout/index'
import ParentView from '@/components/ParentView' import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink' import InnerLink from '@/layout/components/InnerLink'
const permission = { const permission = {
state: { state: {
routes: [], routes: [],
addRoutes: [], addRoutes: [],
defaultRoutes: [], defaultRoutes: [],
topbarRouters: [], topbarRouters: [],
sidebarRouters: [] sidebarRouters: [],
}, sidebarMenuHide: false
mutations: { },
SET_ROUTES: (state, routes) => { mutations: {
state.addRoutes = routes SET_ROUTES: (state, routes) => {
state.routes = constantRoutes.concat(routes) state.addRoutes = routes
}, state.routes = constantRoutes.concat(routes)
SET_DEFAULT_ROUTES: (state, routes) => { },
state.defaultRoutes = constantRoutes.concat(routes) SET_DEFAULT_ROUTES: (state, routes) => {
}, state.defaultRoutes = constantRoutes.concat(routes)
SET_TOPBAR_ROUTES: (state, routes) => { },
// 顶部导航菜单默认添加统计报表栏指向首页 SET_TOPBAR_ROUTES: (state, routes) => {
const index = [{ state.topbarRouters = routes;
path: 'index', },
meta: { title: '统计报表', icon: 'dashboard' } SET_SIDEBAR_ROUTERS: (state, routes) => {
}] state.sidebarRouters = routes
state.topbarRouters = routes.concat(index); }
}, ,SET_SIDEBAR_MENU_HIDE: (state, sidebarMenuHide) => {
SET_SIDEBAR_ROUTERS: (state, routes) => { state.sidebarMenuHide = sidebarMenuHide
state.sidebarRouters = routes },
}, },
}, actions: {
actions: { // 生成路由
// 生成路由 GenerateRoutes({ commit }) {
GenerateRoutes({ commit }) { return new Promise(resolve => {
return new Promise(resolve => { // 向后端请求路由数据
// 向后端请求路由数据 getRouters().then(res => {
getRouters().then(res => { const sdata = JSON.parse(JSON.stringify(res.data))
const sdata = JSON.parse(JSON.stringify(res.data)) const rdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data)) const sidebarRoutes = filterAsyncRouter(sdata)
const sidebarRoutes = filterAsyncRouter(sdata) const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const rewriteRoutes = filterAsyncRouter(rdata, false, true) const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
const asyncRoutes = filterDynamicRoutes(dynamicRoutes); rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) router.addRoutes(asyncRoutes);
router.addRoutes(asyncRoutes); commit('SET_ROUTES', rewriteRoutes)
commit('SET_ROUTES', rewriteRoutes) commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) commit('SET_DEFAULT_ROUTES', sidebarRoutes)
commit('SET_DEFAULT_ROUTES', sidebarRoutes) commit('SET_TOPBAR_ROUTES', sidebarRoutes)
commit('SET_TOPBAR_ROUTES', sidebarRoutes) resolve(rewriteRoutes)
resolve(rewriteRoutes) })
}) })
}) }
} }
} }
}
// 遍历后台传来的路由字符串,转换为组件对象
// 遍历后台传来的路由字符串,转换为组件对象 function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { return asyncRouterMap.filter(route => {
return asyncRouterMap.filter(route => { if (type && route.children) {
if (type && route.children) { route.children = filterChildren(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') { route.component = Layout
route.component = Layout } else if (route.component === 'ParentView') {
} else if (route.component === 'ParentView') { route.component = ParentView
route.component = ParentView } else if (route.component === 'InnerLink') {
} else if (route.component === 'InnerLink') { route.component = InnerLink
route.component = InnerLink } else {
} else { route.component = loadView(route.component)
route.component = loadView(route.component) }
} }
} if (route.children != null && route.children && route.children.length) {
if (route.children != null && route.children && route.children.length) { route.children = filterAsyncRouter(route.children, route, type)
route.children = filterAsyncRouter(route.children, route, type) } else {
} else { delete route['children']
delete route['children'] delete route['redirect']
delete route['redirect'] }
} return true
return true })
}) }
}
function filterChildren(childrenMap, lastRouter = false) {
function filterChildren(childrenMap, lastRouter = false) { var children = []
var children = [] childrenMap.forEach((el, index) => {
childrenMap.forEach((el, index) => { if (el.children && el.children.length) {
if (el.children && el.children.length) { if (el.component === 'ParentView' && !lastRouter) {
if (el.component === 'ParentView' && !lastRouter) { el.children.forEach(c => {
el.children.forEach(c => { c.path = el.path + '/' + c.path
c.path = el.path + '/' + c.path if (c.children && c.children.length) {
if (c.children && c.children.length) { children = children.concat(filterChildren(c.children, c))
children = children.concat(filterChildren(c.children, c)) return
return }
} children.push(c)
children.push(c) })
}) return
return }
} }
} if (lastRouter) {
if (lastRouter) { el.path = lastRouter.path + '/' + el.path
el.path = lastRouter.path + '/' + el.path }
} children = children.concat(el)
children = children.concat(el) })
}) return children
return children }
}
// 动态路由遍历,验证是否具备权限
// 动态路由遍历,验证是否具备权限 export function filterDynamicRoutes(routes) {
export function filterDynamicRoutes(routes) { const res = []
const res = [] routes.forEach(route => {
routes.forEach(route => { if (route.permissions) {
if (route.permissions) { if (auth.hasPermiOr(route.permissions)) {
if (auth.hasPermiOr(route.permissions)) { res.push(route)
res.push(route) }
} } else if (route.roles) {
} else if (route.roles) { if (auth.hasRoleOr(route.roles)) {
if (auth.hasRoleOr(route.roles)) { res.push(route)
res.push(route) }
} }
} })
}) return res
return res }
}
export const loadView = (view) => {
export const loadView = (view) => { if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development') { return (resolve) => require([`@/views/${view}`], resolve)
return (resolve) => require([`@/views/${view}`], resolve) } else {
} else { // 使用 import 实现生产环境的路由懒加载
// 使用 import 实现生产环境的路由懒加载 return () => import(`@/views/${view}`)
return () => import(`@/views/${view}`) }
} }
}
export default permission
export default permission