This commit is contained in:
kk
2026-07-08 18:00:33 +08:00
commit e66b4a3680
312 changed files with 54718 additions and 0 deletions

View 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>

View 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>