| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- <template>
- <el-menu
- :default-openeds="openKeys"
- :default-active="getSelectedKeys"
- :theme="getTheme"
- :collapse="getCollapsed"
- :collapse-transition="false"
- :unique-opened="getUniqueOpened"
- :mode="props.mode"
- style="width: 100%"
- @select="menuItemClick"
- @open="subMenuItemClick"
- >
- <template v-for="item in menus" :key="item.key">
- <el-menu-item v-if="!item.children" :key="getMenuKey(item.key)" :index="item.key">
- <el-icon v-if="item.icon"><component :is="item.icon" /></el-icon>
- <template #title>
- <span>{{ item.title }}</span>
- </template>
- </el-menu-item>
- <template v-else><ReSubMenu :parent="item" /></template>
- </template>
- </el-menu>
- </template>
- <script lang="ts" setup>
- import { ref, computed, watch, onMounted, unref, inject } from 'vue';
- import { useRoute, useRouter } from 'vue-router';
- import { generatorMenu, generatorMenuMix } from '@/utils';
- import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
- import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
- import { useProjectSettingStore } from '@/store/modules/projectSetting';
- import ReSubMenu from './ReSubMenu.vue';
- const collapsed = inject('collapsed');
- const { getMenuSetting, getNavMode, getNavTheme } = useProjectSetting();
- const navMode = getNavMode;
- defineEmits(['update:collapsed']);
- const props = defineProps({
- mode: {
- type: String,
- default: 'vertical',
- },
- theme: {
- type: String,
- default: 'dark',
- },
- //位置
- location: {
- type: String,
- default: 'left',
- },
- });
- // 当前路由
- const currentRoute = useRoute();
- const router = useRouter();
- const settingStore = useProjectSettingStore();
- const menus = ref<any>([]);
- const headerMenuSelectKey = ref<string>('');
- // 获取当前打开的子菜单
- const matched = currentRoute.matched;
- const activeMenu = currentRoute.meta?.activeMenu || null; // activeMenu undefined,null 或 空字符串,统一变为 null。
- const selectedKeys = ref<string>((activeMenu ?? currentRoute.name) as string);
- const openKeys = ref(matched && matched.length ? matched.map((item) => item.name) : []);
- const asyncRouteStore = useAsyncRouteStore();
- const getCollapsed = computed(() => {
- let location = props.location;
- return location === 'left' ? unref(collapsed) : false;
- });
- const getUniqueOpened = computed(() => {
- return getMenuSetting.value.uniqueOpened;
- });
- const getSelectedKeys = computed(() => {
- let location = props.location;
- console.log('location', location);
- console.log('selectedKeys', selectedKeys.value);
- return location === 'left' || (location === 'header' && unref(navMode) === 'horizontal')
- ? unref(selectedKeys)
- : unref(headerMenuSelectKey);
- });
- // 监听分割菜单
- watch(
- () => settingStore.menuSetting.mixMenu,
- () => {
- updateMenu();
- },
- );
- // 跟随页面路由变化,切换菜单选中状态
- watch(
- () => currentRoute.fullPath,
- () => {
- updateMenu();
- const matched = currentRoute.matched;
- openKeys.value = matched.map((item) => item.name);
- const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
- selectedKeys.value = activeMenu ? activeMenu : (currentRoute.name as string);
- },
- );
- function getMenuKey(key) {
- return `${props.location}_${key}`;
- }
- function updateMenu() {
- if (!settingStore.menuSetting.mixMenu) {
- menus.value = generatorMenu(asyncRouteStore.getMenus);
- } else {
- //混合菜单
- const firstRouteName: string = (currentRoute.matched[0].name as string) || '';
- menus.value = generatorMenuMix(asyncRouteStore.getMenus, firstRouteName, props.location);
- const activeMenu: string = currentRoute?.matched[0].meta?.activeMenu as string;
- headerMenuSelectKey.value = (activeMenu ? activeMenu : firstRouteName) || '';
- }
- }
- // const getMenuMode = computed(() => {
- // if (props.mode === 'vertical') {
- // return getMenuIsPop.value ? 'pop' : 'vertical';
- // } else {
- // return props.mode;
- // }
- // });
- const getTheme = computed(() => {
- return getNavTheme.value === 'light' ? 'light' : props.theme;
- });
- //点击菜单项时触发
- function menuItemClick(key) {
- if (/http(s)?:/.test(key)) {
- window.open(key);
- } else {
- router.push({ name: key });
- }
- }
- function subMenuItemClick(_, keys) {
- if (!keys) return;
- const latestOpenKey = keys.find((key) => openKeys.value.indexOf(key) === -1);
- const isExistChildren = findChildrenLen(latestOpenKey as string);
- openKeys.value = isExistChildren ? (latestOpenKey ? [latestOpenKey] : []) : keys;
- }
- //查找是否存在子路由
- function findChildrenLen(key: string) {
- if (!key) return false;
- const subRouteChildren: string[] = [];
- for (const { children, key } of unref(menus)) {
- if (children && children.length) {
- subRouteChildren.push(key as string);
- }
- }
- return subRouteChildren.includes(key);
- }
- onMounted(() => {
- updateMenu();
- });
- </script>
- <style scoped lang="scss">
- .my-icon {
- position: relative;
- margin-right: 5px;
- width: 0.75em;
- height: 0.75em;
- filter: drop-shadow(var(--el-menu-text-color) 80px 0);
- transform: translate(-80px);
- }
- :deep(.el-menu-item.is-active) {
- .my-icon {
- filter: drop-shadow(var(--el-menu-hover-text-color) 80px 0);
- transform: translate(-80px);
- }
- }
- :deep(.el-menu-item:hover) {
- .my-icon {
- filter: drop-shadow(var(--el-menu-hover-text-color) 80px 0);
- transform: translate(-80px);
- }
- }
- </style>
|