1.把目录“首页”放在第一个菜单位置

2.有二级菜单,例如点击系统管理时 默认选中第一个子菜单--用户管理
3.没有二级菜单,左侧二级不显示,比如首页或以后新增目录
4.新建一个目录,下面没有子菜单,出不来对应的页面
This commit is contained in:
LILU
2022-01-11 22:38:52 +08:00
parent 869dcf73f8
commit a3b59a2b9f
6 changed files with 799 additions and 785 deletions

View File

@ -142,12 +142,12 @@ 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) => {
@ -157,7 +157,13 @@ export default {
}); });
} }
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.$store.commit("SET_SIDEBAR_ACTIVE_MENU", routes[0].path);
let that = this;
that.$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,326 +1,329 @@
<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.commit("SET_SIDEBAR_ACTIVE_MENU", this.$route.path);
} this.$store.dispatch('tagsView/addView', this.$route)
return false }
}, return false
moveToCurrentTag() { },
const tags = this.$refs.tag moveToCurrentTag() {
this.$nextTick(() => { const tags = this.$refs.tag
for (const tag of tags) { this.$nextTick(() => {
if (tag.to.path === this.$route.path) { for (const tag of tags) {
this.$refs.scrollPane.moveToTarget(tag) if (tag.to.path === this.$route.path) {
// when query is different then update this.$refs.scrollPane.moveToTarget(tag)
if (tag.to.fullPath !== this.$route.fullPath) { // when query is different then update
this.$store.dispatch('tagsView/updateVisitedView', this.$route) if (tag.to.fullPath !== this.$route.fullPath) {
} this.$store.dispatch('tagsView/updateVisitedView', this.$route)
break }
} break
} }
}) }
}, })
refreshSelectedTag(view) { },
this.$tab.refreshPage(view); refreshSelectedTag(view) {
}, this.$tab.refreshPage(view);
closeSelectedTag(view) { },
this.$tab.closePage(view).then(({ visitedViews }) => { closeSelectedTag(view) {
if (this.isActive(view)) { this.$tab.closePage(view).then(({ visitedViews }) => {
this.toLastView(visitedViews, view) if (this.isActive(view)) {
} this.toLastView(visitedViews, view)
}) }
}, })
closeRightTags() { },
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => { closeRightTags() {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
this.toLastView(visitedViews) if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
} this.toLastView(visitedViews)
}) }
}, })
closeLeftTags() { },
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => { closeLeftTags() {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) { this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
this.toLastView(visitedViews) if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
} this.toLastView(visitedViews)
}) }
}, })
closeOthersTags() { },
this.$router.push(this.selectedTag).catch(()=>{}); closeOthersTags() {
this.$tab.closeOtherPage(this.selectedTag).then(() => { this.$router.push(this.selectedTag).catch(()=>{});
this.moveToCurrentTag() this.$tab.closeOtherPage(this.selectedTag).then(() => {
}) this.moveToCurrentTag()
}, })
closeAllTags(view) { },
this.$tab.closeAllPage().then(({ visitedViews }) => { closeAllTags(view) {
if (this.affixTags.some(tag => tag.path === this.$route.path)) { this.$tab.closeAllPage().then(({ visitedViews }) => {
return if (this.affixTags.some(tag => tag.path === this.$route.path)) {
} return
this.toLastView(visitedViews, view) }
}) this.toLastView(visitedViews, view)
}, })
toLastView(visitedViews, view) { },
const latestView = visitedViews.slice(-1)[0] toLastView(visitedViews, view) {
if (latestView) { console.log("打开了2")
this.$router.push(latestView.fullPath) const latestView = visitedViews.slice(-1)[0]
} else { if (latestView) {
// now the default is to redirect to the home page if there is no tags-view, this.$router.push(latestView.fullPath)
// you can adjust it according to your needs. } else {
if (view.name === 'Dashboard') { // now the default is to redirect to the home page if there is no tags-view,
// to reload home page // you can adjust it according to your needs.
this.$router.replace({ path: '/redirect' + view.fullPath }) if (view.name === 'Dashboard') {
} else { // to reload home page
this.$router.push('/') this.$router.replace({ path: '/redirect' + view.fullPath })
} } else {
} this.$router.push('/')
}, }
openMenu(tag, e) { }
const menuMinWidth = 105 },
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left openMenu(tag, e) {
const offsetWidth = this.$el.offsetWidth // container width console.log("打开了3",tag,e)
const maxLeft = offsetWidth - menuMinWidth // left boundary const menuMinWidth = 105
const left = e.clientX - offsetLeft + 15 // 15: margin right const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
const offsetWidth = this.$el.offsetWidth // container width
if (left > maxLeft) { const maxLeft = offsetWidth - menuMinWidth // left boundary
this.left = maxLeft const left = e.clientX - offsetLeft + 15 // 15: margin right
} else {
this.left = left if (left > maxLeft) {
} this.left = maxLeft
} else {
this.top = e.clientY this.left = left
this.visible = true }
this.selectedTag = tag
}, this.top = e.clientY
closeMenu() { this.visible = true
this.visible = false this.selectedTag = tag
}, },
handleScroll() { closeMenu() {
this.closeMenu() this.visible = false
} },
} handleScroll() {
} this.closeMenu()
</script> }
}
<style lang="scss" scoped> }
.tags-view-container { </script>
height: 34px;
width: 100%; <style lang="scss" scoped>
background: #fff; .tags-view-container {
border-bottom: 1px solid #d8dce5; height: 34px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04); width: 100%;
.tags-view-wrapper { background: #fff;
.tags-view-item { border-bottom: 1px solid #d8dce5;
display: inline-block; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
position: relative; .tags-view-wrapper {
cursor: pointer; .tags-view-item {
height: 26px; display: inline-block;
line-height: 26px; position: relative;
border: 1px solid #d8dce5; cursor: pointer;
color: #495060; height: 26px;
background: #fff; line-height: 26px;
padding: 0 8px; border: 1px solid #d8dce5;
font-size: 12px; color: #495060;
margin-left: 5px; background: #fff;
margin-top: 4px; padding: 0 8px;
&:first-of-type { font-size: 12px;
margin-left: 15px; margin-left: 5px;
} margin-top: 4px;
&:last-of-type { &:first-of-type {
margin-right: 15px; margin-left: 15px;
} }
&.active { &:last-of-type {
background-color: #42b983; margin-right: 15px;
color: #fff; }
border-color: #42b983; &.active {
&::before { background-color: #42b983;
content: ''; color: #fff;
background: #fff; border-color: #42b983;
display: inline-block; &::before {
width: 8px; content: '';
height: 8px; background: #fff;
border-radius: 50%; display: inline-block;
position: relative; width: 8px;
margin-right: 2px; height: 8px;
} border-radius: 50%;
} position: relative;
} margin-right: 2px;
} }
.contextmenu { }
margin: 0; }
background: #fff; }
z-index: 3000; .contextmenu {
position: absolute; margin: 0;
list-style-type: none; background: #fff;
padding: 5px 0; z-index: 3000;
border-radius: 4px; position: absolute;
font-size: 12px; list-style-type: none;
font-weight: 400; padding: 5px 0;
color: #333; border-radius: 4px;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); font-size: 12px;
li { font-weight: 400;
margin: 0; color: #333;
padding: 7px 16px; box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
cursor: pointer; li {
&:hover { margin: 0;
background: #eee; padding: 7px 16px;
} cursor: pointer;
} &:hover {
} background: #eee;
} }
</style> }
}
<style lang="scss"> }
//reset element css of el-icon-close </style>
.tags-view-wrapper {
.tags-view-item { <style lang="scss">
.el-icon-close { //reset element css of el-icon-close
width: 16px; .tags-view-wrapper {
height: 16px; .tags-view-item {
vertical-align: 2px; .el-icon-close {
border-radius: 50%; width: 16px;
text-align: center; height: 16px;
transition: all .3s cubic-bezier(.645, .045, .355, 1); vertical-align: 2px;
transform-origin: 100% 50%; border-radius: 50%;
&:before { text-align: center;
transform: scale(.6); transition: all .3s cubic-bezier(.645, .045, .355, 1);
display: inline-block; transform-origin: 100% 50%;
vertical-align: -3px; &:before {
} transform: scale(.6);
&:hover { display: inline-block;
background-color: #b4bccc; vertical-align: -3px;
color: #fff; }
} &:hover {
} background-color: #b4bccc;
} 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,
} sidebarActiveMenu:state => state.permission.sidebarActiveMenu,
export default getters }
export default getters

View File

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