Commit 0b042bcf authored by 水落(YangLei)'s avatar 水落(YangLei)

feat: 菜单权限处理

parent 445c4639
......@@ -7,7 +7,6 @@
<script>
import { enquireScreen } from './utils/commonUtil';
import { mapState, mapMutations } from 'vuex';
import { getI18nKey } from '@/utils/routerUtil';
import { changeThemeColor } from '@/utils/themeUtil';
export default {
......@@ -22,7 +21,7 @@ export default {
this.setLanguage(this.lang);
enquireScreen(isMobile => this.setDevice(isMobile));
},
mounted() {},
watch: {
lang(val) {
this.setLanguage(val);
......@@ -31,15 +30,15 @@ export default {
$route() {
this.setHtmlTitle();
},
'theme.mode': function(val) {
'theme.mode'(val) {
let closeMessage = this.$message.loading(`您选择了主题模式 ${val}, 正在切换...`);
changeThemeColor(this.theme.color, val).then(closeMessage);
},
'theme.color': function(val) {
'theme.color'(val) {
let closeMessage = this.$message.loading(`您选择了主题色 ${val}, 正在切换...`);
changeThemeColor(val, this.theme.mode).then(closeMessage);
},
layout: function() {
layout() {
window.dispatchEvent(new Event('resize'));
},
},
......
......@@ -8,12 +8,12 @@
:trigger="null"
>
<div :class="['logo', theme]">
<router-link to="/dashboard/workplace">
<router-link to="/">
<img src="@/assets/img/logo.png" />
<h1>{{ systemName }}</h1>
</router-link>
</div>
<i-menu :theme="theme" :collapsed="collapsed" :options="menuData" @select="onSelect" class="menu" />
<i-menu :theme="theme" :collapsed="collapsed" :options="menuData" class="menu" />
</a-layout-sider>
</template>
......@@ -51,11 +51,6 @@ export default {
},
...mapState('settingModule', ['isMobile', 'systemName']),
},
methods: {
onSelect(obj) {
this.$emit('menuSelect', obj);
},
},
};
</script>
......
......@@ -34,6 +34,7 @@
import Menu from 'ant-design-vue/es/menu';
import Icon from 'ant-design-vue/es/icon';
import { getUserInfo } from '@/utils';
import { mapState } from 'vuex';
const { Item, SubMenu } = Menu;
......@@ -67,24 +68,21 @@ export default {
sOpenKeys: [],
cachedOpenKeys: [],
menuData: [],
allMenuList: [],
};
},
computed: {
...mapState('settingModule', ['layout']),
menuTheme() {
return this.theme == 'light' ? this.theme : 'dark';
},
},
created() {
const { menuList } = getUserInfo();
// 增加查询速度
this.menuData = menuList.filter(i => i.menuType === 'MENU');
this.allMenuList = menuList;
this.updateMenu();
// // 自定义国际化配置
// if (this.i18n && this.i18n.messages) {
// const messages = this.i18n.messages;
// Object.keys(messages).forEach(key => {
// this.$i18n.mergeLocaleMessage(key, messages[key]);
// });
// }
},
watch: {
i18n(val) {
......@@ -103,9 +101,6 @@ export default {
this.sOpenKeys = this.cachedOpenKeys;
}
},
$route: function() {
this.updateMenu();
},
sOpenKeys(val) {
this.$emit('openChange', val);
this.$emit('update:openKeys', val);
......@@ -123,17 +118,37 @@ export default {
}
return !icon || icon == 'none' ? null : h(Icon, { props: { type: icon } });
},
renderMenuItem(h, menu) {
const tag = 'router-link';
const config = {
props: { to: menu.path },
props: { to: menu.menuUrl },
attrs: { style: 'overflow:hidden;white-space:normal;text-overflow:clip;' },
};
// 菜单
if (menu.menuType === 'MENU') {
return h(Item, { key: menu.menuId }, [
h(tag, config, [this.renderIcon(h, menu.menuIcon, menu.menuId), menu.menuName]),
]);
}
return h(Item, { key: menu.menuId }, [
h(tag, config, [this.renderIcon(h, menu.menuIcon, menu.menuId), menu.menuName]),
h(
'a',
{
attrs: { style: 'overflow:hidden;white-space:normal;text-overflow:clip;' },
on: {
click(e) {
e.preventDefault();
},
},
},
[this.renderIcon(h, menu.menuIcon, menu.menuId), menu.menuName],
),
]);
},
renderSubMenu(h, menu) {
const subItem = [
h(
......@@ -146,52 +161,63 @@ export default {
),
];
const itemArr = [];
menu.children.forEach(item => {
itemArr.push(this.renderItem(h, item));
});
return h(SubMenu, { key: menu.menuId }, subItem.concat(itemArr));
},
renderItem(h, menu) {
return menu.children && menu.children.length
? this.renderSubMenu(h, menu)
: this.renderMenuItem(h, menu);
},
renderMenu(h, menuTree) {
let menuArr = [];
menuTree.forEach((menu, i) => {
const menuArr = [];
menuTree.forEach(menu => {
menuArr.push(this.renderItem(h, menu));
});
return menuArr;
},
updateMenu() {
this.selectedKeys = this.getSelectedKey(this.$route);
this.selectedKeys = this.getSelectedKey();
// 混合模式 不管
if (this.layout !== 'mix') this.sOpenKeys = this.getOpenKeysByPath(this.$route.path);
},
// let openKeys = matchedRoutes.map(item => item.path);
getSelectedKey() {
const { path } = this.$route;
let routeToMenu = this.menuData.find(m => m.menuUrl === path);
// openKeys = openKeys.slice(0, openKeys.length - 1);
if (this.layout !== 'mix') return [routeToMenu.menuId];
// if (!fastEqual(openKeys, this.sOpenKeys)) {
// this.collapsed || this.mode === 'horizontal'
// ? (this.cachedOpenKeys = openKeys)
// : (this.sOpenKeys = openKeys);
// }
this.sOpenKeys = this.getOpenKeysByPath(this.$route.path);
},
getSelectedKey(route) {
const { path } = route;
return [this.menuData.find(m => m.menuUrl === path).menuId];
// 说明这是头部菜单
if (this.mode === 'horizontal') {
let parentMenuId = routeToMenu.parentMenuId;
while (parentMenuId !== 0) {
routeToMenu = this.allMenuList.find(m => m.menuId === parentMenuId);
parentMenuId = routeToMenu.parentMenuId;
}
return [routeToMenu.menuId];
}
return [];
},
getOpenKeysByPath(path) {
const { menuList } = getUserInfo();
const { parentMenuId } = this.menuData.find(m => m.menuUrl === path);
if (parentMenuId === 0) return [];
const parentMenus = [parentMenuId];
const res = [parentMenuId];
while (parentMenus.length) {
const menuId = parentMenus.pop();
const parentMenu = menuList.find(m => m.menuId === menuId);
const parentMenu = this.allMenuList.find(m => m.menuId === menuId);
if (parentMenu.parentMenuId !== 0) {
parentMenus.push(parentMenu.parentMenuId);
res.push(parentMenu.parentMenuId);
......@@ -215,7 +241,7 @@ export default {
this.sOpenKeys = val;
},
click: obj => {
obj.selectedKeys = [obj.key];
this.selectedKeys = [obj.key];
this.$emit('select', obj);
},
},
......
<template>
<div :class="['page-header', layout, pageWidth]">
<a-breadcrumb>
<a-breadcrumb-item :key="index" v-for="(item, index) in breadcrumb">
<span>{{ item }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
<a-breadcrumb style="margin: 10px 0">
<a-breadcrumb-item :key="index" v-for="(item, index) in breadcrumb">
<span>{{ item }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'PageHeader',
props: {
title: {
type: [String, Boolean],
required: false,
},
breadcrumb: {
type: Array,
required: false,
},
logo: {
type: String,
required: false,
},
avatar: {
type: String,
required: false,
},
},
computed: {
...mapState('settingModule', ['layout', 'showPageTitle', 'pageWidth']),
},
};
</script>
<style lang="less" scoped>
.page-header {
padding: 16px 24px;
&.head.fixed {
margin: auto;
max-width: 1400px;
}
}
</style>
......@@ -8,7 +8,7 @@ import VueI18n from 'vue-i18n';
import { accountModule, settingModule } from './pages/frame/store';
import App from './App.vue';
import Plugins from './plugins';
import { loadRoutes, loadGuards, setAppOptions } from './utils/routerUtil';
import { loadGuards, setAppOptions } from './utils/routerUtil';
import guards from './router/guards';
import { loadResponseInterceptor } from './utils/requestUtil';
import langUtils from '@/utils/langUtils';
......@@ -56,8 +56,6 @@ Vue.use(Plugins);
//设置应用配置
setAppOptions({ router, store, i18n });
// 加载路由
loadRoutes();
// 加载路由守卫
loadGuards(guards, { router, store, i18n, message: Vue.prototype.$message });
......
<template>
<a-layout-header :class="[headerTheme, 'admin-header']">
<div :class="['admin-header-wide', layout, pageWidth]">
<router-link v-if="isMobile || layout === 'head'" to="/" :class="['logo', isMobile ? null : 'pc', headerTheme]">
<router-link
v-if="isMobile || layout === 'head'"
to="/"
:class="['logo', isMobile ? null : 'pc', headerTheme]"
>
<img width="32" src="@/assets/img/logo.png" />
<h1 v-if="!isMobile">{{systemName}}</h1>
<h1 v-if="!isMobile">{{ systemName }}</h1>
</router-link>
<a-icon v-if="showToggleCollapse" class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggleCollapse" />
<div v-if="layout !== 'side' && !isMobile" class="admin-header-menu" :style="`width: ${menuWidth};`">
<i-menu class="head-menu" :theme="headerTheme" mode="horizontal" :options="menuData" @select="onSelect" />
<a-icon
v-if="showToggleCollapse"
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click="toggleCollapse"
/>
<div
v-if="layout !== 'side' && !isMobile"
class="admin-header-menu"
:style="`width: ${menuWidth};`"
>
<i-menu
class="head-menu"
:theme="headerTheme"
mode="horizontal"
:options="menuData"
@select="onSelect"
/>
</div>
<div :class="['admin-header-right', headerTheme]">
<a-tooltip class="header-item" title="帮助文档" placement="bottom">
<a href="https://iczer.gitee.io/vue-antd-admin-docs/" target="_blank">
<a-icon type="question-circle-o" />
</a>
</a-tooltip>
<layout-top-header-notice class="header-item" />
<layout-top-header-avatar class="header-item" />
<layout-top-header-lang class="header-item" />
</div>
</div>
</a-layout-header>
......@@ -37,8 +50,7 @@ export default {
components: { IMenu, LayoutTopHeaderAvatar, LayoutTopHeaderNotice, LayoutTopHeaderLang },
props: ['collapsed', 'menuData'],
data() {
return {
};
return {};
},
computed: {
...mapState('settingModule', ['theme', 'isMobile', 'layout', 'systemName', 'pageWidth']),
......@@ -54,14 +66,13 @@ export default {
const extraWidth = searchActive ? '600px' : '400px';
return `calc(${headWidth} - ${extraWidth})`;
},
showToggleCollapse(){
if (this.layout !== 'head' && this.isMobile===false){
showToggleCollapse() {
if (this.layout !== 'head' && this.isMobile === false) {
return true;
}
else{
} else {
return false;
}
}
},
},
methods: {
toggleCollapse() {
......
......@@ -10,6 +10,7 @@
</template>
<script>
import { setUserInfoByRequest } from '@/utils';
import langUtils from '@/utils/langUtils';
export default {
......@@ -27,8 +28,9 @@ export default {
},
},
methods: {
change({ key }) {
async change({ key }) {
langUtils.set(key);
await setUserInfoByRequest();
window.location.reload();
},
},
......
<template>
<div class="side-setting">
<div :class="$style['side-setting']">
<setting-item>
<a-button @click="saveSetting" type="primary" icon="save">{{ $t('save') }}</a-button>
<a-button @click="resetSetting" type="dashed" icon="redo" style="float: right">
......@@ -306,7 +306,7 @@ export default {
};
</script>
<style lang="less" scoped>
<style lang="less" module>
.side-setting {
min-height: 100%;
background-color: @base-bg-color;
......@@ -315,9 +315,7 @@ export default {
line-height: 1.5;
word-wrap: break-word;
position: relative;
.flex {
display: flex;
}
.select-item {
width: 80px;
}
......
<template>
<div class="setting-item">
<h3 v-if="title" class="title">{{title}}</h3>
<slot></slot>
<div :class="$style['setting-item']">
<h3 v-if="title" :class="$style.title">{{ title }}</h3>
<slot />
</div>
</template>
......@@ -12,14 +12,14 @@ export default {
};
</script>
<style lang="less" scoped>
<style lang="less" module>
.setting-item {
margin-bottom: 24px;
.title {
font-size: 14px;
color: @title-color;
line-height: 22px;
margin-bottom: 12px;
}
}
.title {
font-size: 14px;
color: @title-color;
line-height: 22px;
margin-bottom: 12px;
}
</style>
<template>
<div :class="['tabs-head', layout, pageWidth]">
<a-tabs
type="editable-card"
:class="[
'tabs-container',
layout,
pageWidth,
{ affixed: affixed, 'fixed-header': fixedHeader, collapsed: adminLayout.collapsed },
]"
:active-key="active"
:hide-add="true"
>
<a-tooltip placement="left" :title="lockTitle" slot="tabBarExtraContent">
<a-tabs type="editable-card" :active-key="active" :hide-add="true">
<a-tooltip placement="left" :title="lockTitle" slot="tabBarExtraContent">
<a-icon
theme="filled"
@click="onLockClick"
class="header-lock"
:type="fixedTabs ? 'lock' : 'unlock'"
/>
</a-tooltip>
<a-tab-pane v-for="page in pageList" :key="page.fullPath">
<div slot="tab" class="tab" @contextmenu="e => onContextmenu(page.fullPath, e)">
<a-icon
theme="filled"
@click="onLockClick"
class="header-lock"
:type="fixedTabs ? 'lock' : 'unlock'"
@click="onRefresh(page)"
:class="['icon-sync', { hide: page.fullPath !== active && !page.loading }]"
:type="page.loading ? 'loading' : 'sync'"
/>
</a-tooltip>
<a-tab-pane v-for="page in pageList" :key="page.fullPath">
<div slot="tab" class="tab" @contextmenu="e => onContextmenu(page.fullPath, e)">
<a-icon
@click="onRefresh(page)"
:class="['icon-sync', { hide: page.fullPath !== active && !page.loading }]"
:type="page.loading ? 'loading' : 'sync'"
/>
<div class="title" @click="onTabClick(page.fullPath)">{{ pageName(page) }}</div>
<a-icon
v-if="!page.unclose"
@click="onClose(page.fullPath)"
class="icon-close"
type="close"
/>
</div>
</a-tab-pane>
</a-tabs>
<div v-if="affixed" class="virtual-tabs"></div>
</div>
<div class="title" @click="onTabClick(page.fullPath)">{{ pageName(page) }}</div>
<a-icon
v-if="!page.unclose"
@click="onClose(page.fullPath)"
class="icon-close"
type="close"
/>
</div>
</a-tab-pane>
</a-tabs>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
import { getI18nKey } from '@/utils/routerUtil';
import layoutTabHeaderI18n from './i18n';
import { getUserInfo } from '@/utils';
export default {
name: 'LayoutTabsHeader',
......@@ -56,18 +43,24 @@ export default {
data() {
return {
affixed: false,
menuList: [],
};
},
inject: ['adminLayout'],
created() {
this.affixed = this.fixedTabs;
const { menuList } = getUserInfo();
this.menuList = menuList.filter(i => i.menuType === 'MENU');
},
computed: {
...mapState('settingModule', ['layout', 'pageWidth', 'fixedHeader', 'fixedTabs', 'customTitles']),
lockTitle() {
return this.$t(this.fixedTabs ? 'unlock' : 'lock');
},
},
methods: {
...mapMutations('settingModule', ['setFixedTabs']),
onLockClick() {
......@@ -96,8 +89,7 @@ export default {
},
pageName(page) {
const pagePath = page.fullPath.split('?')[0];
const custom = this.customTitles.find(item => item.path === pagePath);
return (custom && custom.title) || page.title || this.$t(getI18nKey(page.keyPath));
return this.menuList.find(m => m.menuUrl === pagePath)?.menuName;
},
},
};
......@@ -113,6 +105,8 @@ export default {
.title {
display: inline-block;
height: 100%;
user-select: none;
-webkit-user-select: none;
}
.icon-close {
font-size: 12px;
......@@ -136,54 +130,4 @@ export default {
}
}
}
.tabs-head {
margin: 0 auto;
&.head.fixed {
width: 1400px;
}
}
.tabs-container {
margin: -16px auto 8px;
transition: top, left 0.2s;
.header-lock {
font-size: 18px;
cursor: pointer;
color: @primary-3;
&:hover {
color: @primary-color;
}
}
&.affixed {
margin: 0 auto;
top: 0px;
padding: 8px 24px 0;
position: fixed;
height: 48px;
z-index: 1;
background-color: @layout-body-background;
&.side,
&.mix {
right: 0;
left: 256px;
&.collapsed {
left: 80px;
}
}
&.head {
width: inherit;
padding: 8px 0 0;
&.fluid {
left: 0;
right: 0;
padding: 8px 24px 0;
}
}
&.fixed-header {
top: 64px;
}
}
}
.virtual-tabs {
height: 48px;
}
</style>
......@@ -39,6 +39,7 @@
:menuData="headMenuData"
:collapsed="collapsed"
@toggleCollapse="toggleCollapse"
@menuSelect="menuSelect"
/>
<a-layout-header
:class="[
......@@ -46,18 +47,16 @@
{ 'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage },
]"
v-show="fixedHeader"
></a-layout-header>
<a-layout-content class="admin-layout-content" :style="`min-height: ${minHeight}px;`">
<div style="position: relative">
<slot></slot>
</div>
/>
<a-layout-content class="admin-layout-content">
<slot />
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script>
import LayoutTopHeader from '../components/header/LayoutTopHeader';
import LayoutTopHeader from '../components/header/LayoutTopHeader.vue';
import Drawer from '@/components/tool/Drawer';
import SideMenu from '@/components/menu/SideMenu.vue';
import Setting from '../components/setting/Setting';
......@@ -76,6 +75,9 @@ export default {
showSetting: false,
drawerOpen: false,
menuData: [],
firstMenu: [],
subMenu: [],
menuList: [],
};
},
provide() {
......@@ -84,17 +86,14 @@ export default {
};
},
watch: {
$route(val) {
this.setActivated(val);
},
layout() {
this.setActivated(this.$route);
},
isMobile(val) {
if (!val) {
this.drawerOpen = false;
}
},
layout(layout) {
this.updateSildeMenu(layout);
},
},
computed: {
...mapState('settingModule', [
......@@ -110,8 +109,6 @@ export default {
'multiPage',
]),
...mapGetters('settingModule', ['firstMenu', 'subMenu']),
hideSettingExtend() {
if (this.hideSetting === false && process.env.NODE_ENV === 'development') {
return false;
......@@ -131,41 +128,63 @@ export default {
},
headMenuData() {
const { layout, menuData, firstMenu } = this;
console.log('first', firstMenu);
return layout === 'mix' ? firstMenu : menuData;
},
sideMenuData() {
const { layout, menuData, subMenu } = this;
console.log('sub', subMenu);
return layout === 'mix' ? subMenu : menuData;
},
},
methods: {
...mapMutations('settingModule', ['correctPageMinHeight', 'setActivatedFirst']),
...mapMutations('settingModule', ['correctPageMinHeight']),
toggleCollapse() {
this.collapsed = !this.collapsed;
},
onMenuSelect() {
this.toggleCollapse();
},
setActivated(route) {
if (this.layout === 'mix') {
let matched = route.matched;
matched = matched.slice(0, matched.length - 1);
const { firstMenu } = this;
for (let menu of firstMenu) {
if (matched.findIndex(item => item.path === menu.fullPath) !== -1) {
this.setActivatedFirst(menu.fullPath);
break;
}
updateSildeMenu(layout) {
if (layout === 'mix') {
const { path } = this.$route;
let currentMenu = this.menuList.find(i => i.menuUrl === path);
let parentMenuId = currentMenu.parentMenuId;
while (parentMenuId !== 0) {
currentMenu = this.menuList.find(i => i.menuId === parentMenuId);
parentMenuId = currentMenu.parentMenuId;
}
this.subMenu = currentMenu.children || [];
}
},
menuSelect(obj) {
console.log('切换', obj);
// 拿到 菜单id
const menuId = obj.key;
const currentMenu = this.menuList.find(i => i.menuId === menuId);
if (currentMenu.menuType === 'MENU') {
this.subMenu = [];
return;
}
this.subMenu = currentMenu.children || [];
},
},
created() {
this.correctPageMinHeight(this.minHeight - 24);
this.setActivated(this.$route);
const userInfo = getUserInfo();
const menuData = convertListToTree(userInfo?.menuList || [], false, true);
const { menuList } = getUserInfo();
console.log(menuList);
const menuData = convertListToTree(menuList || [], false, true);
this.menuData = menuData;
this.menuList = menuList;
this.firstMenu = JSON.parse(JSON.stringify(menuData)).map(i => {
delete i.children;
return i;
});
},
beforeDestroy() {
this.correctPageMinHeight(-this.minHeight + 24);
......@@ -195,6 +214,9 @@ export default {
}
}
.admin-layout-main {
display: flow-root;
position: initial;
.admin-header {
top: 0;
right: 0;
......
<template>
<div class="common-layout">
<div class="content">
<slot></slot>
<slot />
</div>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'CommonLayout',
computed: {
...mapState('settingModule', ['footerLinks', 'copyright']),
},
};
</script>
<style scoped lang="less">
.common-layout {
display: flex;
......
......@@ -4,12 +4,11 @@
ref="pageHeader"
:style="`margin-top: ${multiPage ? 0 : -24}px`"
:breadcrumb="breadcrumb"
:title="pageTitle"
:logo="logo"
:avatar="avatar"
>
<slot name="action" slot="action"></slot>
<slot slot="content" name="headerContent"></slot>
<slot name="action" slot="action" />
<slot slot="content" name="headerContent" />
<div slot="content" v-if="!this.$slots.headerContent && desc">
<p>{{ desc }}</p>
<div v-if="this.linkList" class="link">
......@@ -20,16 +19,15 @@
</div>
<slot v-if="this.$slots.extra" slot="extra" name="extra"></slot>
</page-header>
<div ref="page" :class="['page-content', layout, pageWidth]">
<slot></slot>
</div>
<slot />
</div>
</template>
<script>
import PageHeader from '@/components/page/PageHeader';
import PageHeader from '@/components/page/PageHeader.vue';
import { mapState, mapMutations } from 'vuex';
import { getI18nKey } from '@/utils/routerUtil';
import { getUserInfo } from '@/utils';
export default {
name: 'PageLayout',
......@@ -61,33 +59,17 @@ export default {
this.updatePageHeight();
},
created() {
const { menuList } = getUserInfo();
this.menuList = menuList;
this.page = this.$route.meta.page;
},
beforeDestroy() {
this.updatePageHeight(0);
},
computed: {
...mapState('settingModule', ['layout', 'multiPage', 'pageMinHeight', 'pageWidth', 'customTitles']),
pageTitle() {
let pageTitle = this.page && this.page.title;
return this.customTitle || (pageTitle && this.$t(pageTitle)) || this.title || this.routeName;
},
routeName() {
const route = this.$route;
return this.$t(getI18nKey(route.matched[route.matched.length - 1].path));
},
...mapState('settingModule', ['layout', 'multiPage', 'pageMinHeight', 'pageWidth']),
breadcrumb() {
let page = this.page;
let breadcrumb = page && page.breadcrumb;
if (breadcrumb) {
let i18nBreadcrumb = [];
breadcrumb.forEach(item => {
i18nBreadcrumb.push(this.$t(item));
});
return i18nBreadcrumb;
} else {
return this.getRouteBreadcrumb();
}
return this.getRouteBreadcrumb();
},
marginCorrect() {
return this.multiPage ? 24 : 0;
......@@ -96,18 +78,14 @@ export default {
methods: {
...mapMutations('settingModule', ['correctPageMinHeight']),
getRouteBreadcrumb() {
let routes = this.$route.matched;
const path = this.$route.path;
let breadcrumb = [];
routes
.filter(item => path.includes(item.path))
.forEach(route => {
const path = route.path.length === 0 ? '/home' : route.path;
breadcrumb.push(this.$t(getI18nKey(path)));
});
let pageTitle = this.page && this.page.title;
if (this.customTitle || pageTitle) {
breadcrumb[breadcrumb.length - 1] = this.customTitle || pageTitle;
const currentMenu = this.menuList.find(m => m.menuUrl === path);
let parentMenuId = currentMenu.parentMenuId;
const breadcrumb = [currentMenu.menuName];
while (parentMenuId !== 0) {
const parentMenu = this.menuList.find(m => m.menuId === parentMenuId);
breadcrumb.unshift(parentMenu.menuName);
parentMenuId = parentMenu.parentMenuId;
}
return breadcrumb;
},
......@@ -135,12 +113,4 @@ export default {
}
}
}
.page-content {
position: relative;
&.head.fixed {
margin: 0 auto;
max-width: 1400px;
}
}
</style>
......@@ -42,14 +42,6 @@ export default {
return menuItem;
});
},
subMenu(state) {
const { menuData, activatedFirst } = state;
if (menuData.length > 0 && !menuData[0].fullPath) {
formatFullPath(menuData);
}
const current = menuData.find(menu => menu.fullPath === activatedFirst);
return (current && current.children) || [];
},
},
mutations: {
setDevice(state, isMobile) {
......@@ -59,6 +51,7 @@ export default {
state.theme = theme;
},
setLayout(state, layout) {
console.log(layout);
state.layout = layout;
},
setMultiPage(state, multiPage) {
......
......@@ -10,15 +10,10 @@
@refresh="refresh"
@contextmenu="onContextmenu"
/>
<div
:class="['tabs-view-content', layout, pageWidth]"
:style="`margin-top: ${multiPage ? -20 : 0}px`"
>
<a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
<router-view v-if="!refreshing" ref="tabContent" :key="$route.fullPath" />
</a-keep-alive>
<router-view ref="tabContent" v-else-if="!refreshing" />
</div>
<a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
<router-view v-if="!refreshing" ref="tabContent" :key="$route.fullPath" />
</a-keep-alive>
<router-view ref="tabContent" v-else-if="!refreshing" />
</admin-layout>
</template>
......@@ -28,7 +23,7 @@ import Contextmenu from '@/components/menu/Contextmenu.vue';
import { mapState, mapMutations } from 'vuex';
import { getI18nKey } from '@/utils/routerUtil';
import AKeepAlive from '@/components/cache/AKeepAlive';
import LayoutTabsHeader from '../../components/tab/LayoutTabsHeader';
import LayoutTabsHeader from '../../components/tab/LayoutTabsHeader.vue';
import templateI18n from './i18n';
export default {
......
......@@ -24,7 +24,6 @@ const hasAuthorityRoutes = [
path: '/',
component: TabsTemplateView,
redirect: '/home',
name: '首页',
children: [
{
path: 'home',
......@@ -59,12 +58,6 @@ const hasAuthorityRoutes = [
{
path: 'system',
name: '系统管理',
meta: {
icon: 'setting',
page: {
cacheAble: true,
},
},
component: PageTemplateView,
children: [
{
......
......@@ -57,43 +57,11 @@ const authorityGuard = (to, from, next, options) => {
if (!hasAuthority(to)) {
message.warning(`对不起,您无权访问页面: ${to.fullPath},请联系管理员`);
next({ path: '/403' });
// NProgress.done()
} else {
next();
}
};
/**
* 混合导航模式下一级菜单跳转重定向
* @param to
* @param from
* @param next
* @param options
* @returns {*}
*/
const redirectGuard = (to, from, next, options) => {
const { store } = options;
const getFirstChild = routes => {
const route = routes[0];
if (!route.children || route.children.length === 0) {
return route;
}
return getFirstChild(route.children);
};
if (store.state.settingModule.layout === 'mix') {
const firstMenu = store.getters['settingModule/firstMenu'];
if (firstMenu.find(item => item.fullPath === to.fullPath)) {
store.commit('settingModule/setActivatedFirst', to.fullPath);
const subMenu = store.getters['settingModule/subMenu'];
if (subMenu.length > 0) {
const redirect = getFirstChild(subMenu);
return next({ path: redirect.fullPath });
}
}
}
next();
};
/**
* 进度条结束
* @param to
......@@ -106,6 +74,6 @@ const progressDone = () => {
};
export default {
beforeEach: [progressStart, loginGuard, authorityGuard, redirectGuard],
beforeEach: [progressStart, loginGuard, authorityGuard],
afterEach: [progressDone],
};
import syncConfig from './config';
import { formatRoutes } from '../utils/routerUtil';
// 不需要登录拦截的路由配置
const loginIgnore = {
......@@ -22,7 +21,6 @@ const loginIgnore = {
*/
function initRouter() {
const options = syncConfig;
formatRoutes(options.routes);
return options;
}
......
......@@ -70,29 +70,6 @@ function parseRoutes(routesConfig, routerMap) {
return routes;
}
/**
* 加载路由
* @param routesConfig {RouteConfig[]} 路由配置
*/
function loadRoutes(routesConfig) {
// 应用配置
const { router, store, i18n } = appOptions;
routesConfig = store.getters['accountModule/routesConfig'];
// 提取路由国际化数据
mergeI18nFromRoutes(i18n, router.options.routes);
// 初始化Admin后台菜单数据
const rootRoute = router.options.routes.find(item => item.path === '/');
const menuRoutes = rootRoute && rootRoute.children;
if (menuRoutes) {
store.commit('settingModule/setMenuData', menuRoutes);
}
}
/**
* 深度合并路由
* @param target {Route[]}
......@@ -131,57 +108,6 @@ function deepMergeRoutes(target, source) {
return parseRoutesMap(merge);
}
/**
* 格式化路由
* @param routes 路由配置
*/
function formatRoutes(routes) {
routes.forEach(route => {
const { path } = route;
if (!path.startsWith('/') && path !== '*') {
route.path = '/' + path;
}
});
formatAuthority(routes);
}
/**
* 格式化路由的权限配置
* @param routes 路由
* @param pAuthorities 父级路由权限配置集合
*/
function formatAuthority(routes, pAuthorities = []) {
routes.forEach(route => {
const meta = route.meta;
const defaultAuthority = pAuthorities[pAuthorities.length - 1] || { permission: '*' };
if (meta) {
let authority = {};
if (!meta.authority) {
authority = defaultAuthority;
} else if (typeof meta.authority === 'string') {
authority.permission = meta.authority;
} else if (typeof meta.authority === 'object') {
authority = meta.authority;
const { role } = authority;
if (typeof role === 'string') {
authority.role = [role];
}
if (!authority.permission && !authority.role) {
authority = defaultAuthority;
}
}
meta.authority = authority;
} else {
const authority = defaultAuthority;
route.meta = { authority };
}
route.meta.pAuthorities = pAuthorities;
if (route.children) {
formatAuthority(route.children, [...pAuthorities, route.meta.authority]);
}
});
}
/**
* 从路由 path 解析 i18n key
* @param path
......@@ -213,13 +139,4 @@ function loadGuards(guards, options) {
});
}
export {
parseRoutes,
loadRoutes,
formatAuthority,
getI18nKey,
loadGuards,
deepMergeRoutes,
formatRoutes,
setAppOptions,
};
export { parseRoutes, getI18nKey, loadGuards, deepMergeRoutes, setAppOptions };
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment