init
This commit is contained in:
63
src/page/index/sidebar/index.vue
Normal file
63
src/page/index/sidebar/index.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<el-scrollbar class="avue-menu">
|
||||
<div v-if="menu && menu.length == 0 && !isHorizontal" class="avue-sidebar--tip">
|
||||
{{ $t('menuTip') }}
|
||||
</div>
|
||||
<el-menu
|
||||
unique-opened
|
||||
:default-active="activeMenu"
|
||||
:mode="setting.sidebar"
|
||||
:collapse="getScreen(isCollapse)"
|
||||
>
|
||||
<sidebar-item :menu="menu"></sidebar-item>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import website from '@/config/website';
|
||||
import sidebarItem from './sidebarItem.vue';
|
||||
|
||||
export default {
|
||||
name: 'sidebar',
|
||||
components: { sidebarItem },
|
||||
inject: ['index'],
|
||||
created() {
|
||||
this.index.openMenu();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isHorizontal', 'setting', 'menu', 'tag', 'isCollapse', 'menuId']),
|
||||
// 带参菜单的索引集合,用于激活态的精确匹配
|
||||
menuIndexes() {
|
||||
const props = website.menu;
|
||||
const indexes = new Set();
|
||||
const collect = list => {
|
||||
(list || []).forEach(item => {
|
||||
const query = item[props.query];
|
||||
if (query && Object.keys(query).length) {
|
||||
indexes.add(this.$router.resolve({ path: item[props.path], query }).fullPath);
|
||||
}
|
||||
collect(item[props.children]);
|
||||
});
|
||||
};
|
||||
collect(this.menu);
|
||||
return indexes;
|
||||
},
|
||||
activeMenu() {
|
||||
const route = this.$route;
|
||||
const { meta, path } = route;
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
// 完整地址命中带参菜单索引时精确高亮,其余场景按路径高亮
|
||||
const fullPath = route.fullPath;
|
||||
if (fullPath !== path && this.menuIndexes.has(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
return path;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
105
src/page/index/sidebar/sidebarItem.vue
Normal file
105
src/page/index/sidebar/sidebarItem.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<template v-for="item in menu">
|
||||
<el-menu-item
|
||||
v-if="validatenull(item[childrenKey]) && validRoles(item)"
|
||||
:index="getIndex(item)"
|
||||
@click="open(item)"
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<template #title>
|
||||
<span :alt="item[pathKey]">{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<el-sub-menu
|
||||
v-else-if="!validatenull(item[childrenKey]) && validRoles(item)"
|
||||
:index="getPath(item)"
|
||||
:key="item[labelKey]"
|
||||
>
|
||||
<template #title>
|
||||
<i :class="item[iconKey]"></i>
|
||||
<span>{{ getTitle(item) }}</span>
|
||||
</template>
|
||||
<template v-for="(child, cindex) in item[childrenKey]" :key="child[labelKey]">
|
||||
<el-menu-item
|
||||
:index="getIndex(child)"
|
||||
@click="open(child)"
|
||||
v-if="validatenull(child[childrenKey])"
|
||||
>
|
||||
<i :class="child[iconKey]"></i>
|
||||
<template #title>
|
||||
<span>{{ getTitle(child) }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
<sidebar-item v-else :menu="[child]" :key="cindex"></sidebar-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { validatenull } from 'utils/validate';
|
||||
import website from '@/config/website';
|
||||
import { generateIframePath, generateIframeQuery, isIframeRoute } from '@/router/router';
|
||||
|
||||
export default {
|
||||
name: 'sidebarItem',
|
||||
data() {
|
||||
return {
|
||||
props: website.menu,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
menu: Array,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['roles']),
|
||||
labelKey() {
|
||||
return this.props.label;
|
||||
},
|
||||
pathKey() {
|
||||
return this.props.path;
|
||||
},
|
||||
queryKey() {
|
||||
return this.props.query;
|
||||
},
|
||||
iconKey() {
|
||||
return this.props.icon;
|
||||
},
|
||||
childrenKey() {
|
||||
return this.props.children;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
validatenull,
|
||||
getPath(item) {
|
||||
return generateIframePath(item, this.pathKey);
|
||||
},
|
||||
getIndex(item) {
|
||||
// 带参菜单以完整地址为索引,保证同路径多菜单的激活态互不干扰
|
||||
const { path, query } = this.menuRoute(item);
|
||||
if (!query || !Object.keys(query).length) return path;
|
||||
return this.$router.resolve({ path, query }).fullPath;
|
||||
},
|
||||
menuRoute(item) {
|
||||
const path = this.getPath(item);
|
||||
const originalPath = item[this.pathKey];
|
||||
const isIframe = isIframeRoute(path, originalPath);
|
||||
const query = isIframe
|
||||
? generateIframeQuery(item, this.pathKey, this.queryKey)
|
||||
: item[this.queryKey];
|
||||
return { path, query };
|
||||
},
|
||||
getTitle(item) {
|
||||
return this.$router.$avueRouter.generateTitle(item, this.props);
|
||||
},
|
||||
validRoles(item) {
|
||||
item.meta = item.meta || {};
|
||||
return item.meta.roles ? item.meta.roles.includes(this.roles) : true;
|
||||
},
|
||||
open(item) {
|
||||
this.$router.push(this.menuRoute(item));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user