修复菜单折叠问题
This commit is contained in:
1 parent
85aa7f97a7
commit
19ad2a5d99
2 files changed
+88
-38
No files matched your search
@@ -12,7 +12,6 @@
|
||||
<script setup lang="ts" name="layoutAside">
|
||||
import {computed, defineAsyncComponent, onBeforeMount, reactive, ref, watch} from 'vue';
|
||||
import {storeToRefs} from 'pinia';
|
||||
import pinia from '/@/stores/index';
|
||||
import {useRoutesList} from '/@/stores/routesList';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import {useTagsViewRoutes} from '/@/stores/tagsViewRoutes';
|
||||
@@ -141,8 +140,8 @@ onBeforeMount(() => {
|
||||
mittBus.on('setSendClassicChildren', (res: MittMenu) => {
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = [];
|
||||
state.menuList = res.children;
|
||||
// 不要先清空再赋值,避免菜单闪断导致父级折叠
|
||||
state.menuList = res.children || [];
|
||||
}
|
||||
});
|
||||
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
@@ -159,11 +158,11 @@ watch(themeConfig.value, (val) => {
|
||||
if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
|
||||
}
|
||||
});
|
||||
// 监听 pinia 值的变化,动态赋值给菜单中
|
||||
// 仅在路由表变化时重建侧栏,避免任意 pinia 写入都重置展开状态
|
||||
watch(
|
||||
pinia.state,
|
||||
(val) => {
|
||||
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
|
||||
routesList,
|
||||
() => {
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) return false;
|
||||
setFilterRoutes();
|
||||
},
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<template>
|
||||
<el-menu
|
||||
router
|
||||
:key="state.menuRenderKey"
|
||||
:default-active="state.defaultActive"
|
||||
:default-openeds="state.defaultOpeneds"
|
||||
background-color="transparent"
|
||||
:collapse="state.isCollapse"
|
||||
:unique-opened="getThemeConfig.isUniqueOpened"
|
||||
:collapse-transition="false"
|
||||
>
|
||||
<template v-for="val in menuLists">
|
||||
<!-- 三级菜单 -->
|
||||
<!-- 三级菜单 -->
|
||||
<el-sub-menu :index="val.path" v-if="val.children && val.children.length > 0" :key="val.path">
|
||||
<template #title>
|
||||
<SvgIcon :name="val.meta.icon" />
|
||||
@@ -17,7 +19,7 @@
|
||||
<SubItem :chil="val.children" />
|
||||
</el-sub-menu>
|
||||
<template v-else>
|
||||
<!-- 二级菜单 -->
|
||||
<!-- 二级菜单 -->
|
||||
<el-menu-item :index="val.path" :key="val.path">
|
||||
<SvgIcon :name="val.meta.icon" />
|
||||
<template #title v-if="!val.meta.isLink || (val.meta.isLink && val.meta.isIframe)">
|
||||
@@ -33,12 +35,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="navMenuVertical">
|
||||
import {RouteRecordRaw} from 'vue-router';
|
||||
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||
import other from '/@/utils/other';
|
||||
import { RouteRecordRaw } from "vue-router"
|
||||
import { useThemeConfig } from "/@/stores/themeConfig"
|
||||
import other from "/@/utils/other"
|
||||
|
||||
// 引入组件
|
||||
const SubItem = defineAsyncComponent(() => import('/@/layout/navMenu/subItem.vue'));
|
||||
const SubItem = defineAsyncComponent(() => import("/@/layout/navMenu/subItem.vue"))
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
@@ -47,54 +49,103 @@ const props = defineProps({
|
||||
type: Array<RouteRecordRaw>,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
// 定义变量内容
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const route = useRoute();
|
||||
const storesThemeConfig = useThemeConfig()
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig)
|
||||
const route = useRoute()
|
||||
const state = reactive({
|
||||
defaultActive: route.meta.isDynamic ? route.meta.isDynamicPath : route.path,
|
||||
defaultOpeneds: [] as string[],
|
||||
menuRenderKey: 0,
|
||||
isCollapse: false,
|
||||
});
|
||||
})
|
||||
|
||||
// 获取父级菜单数据
|
||||
const menuLists = computed(() => {
|
||||
return <RouteItems>props.menuList;
|
||||
});
|
||||
return <RouteItems>props.menuList
|
||||
})
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return themeConfig.value;
|
||||
});
|
||||
return themeConfig.value
|
||||
})
|
||||
// 菜单高亮(详情时,父级高亮)
|
||||
const setParentHighlight = (currentRoute: RouteToFrom) => {
|
||||
const { path, meta } = currentRoute;
|
||||
const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split('/') : path!.split('/');
|
||||
if (pathSplit.length >= 4 && meta?.isHide) return pathSplit.splice(0, 3).join('/');
|
||||
else return path;
|
||||
};
|
||||
const { path, meta } = currentRoute
|
||||
const pathSplit = meta?.isDynamic ? meta.isDynamicPath!.split("/") : path!.split("/")
|
||||
if (pathSplit.length >= 4 && meta?.isHide) return pathSplit.splice(0, 3).join("/")
|
||||
else return path
|
||||
}
|
||||
|
||||
/** 找到当前激活项的所有父级 path,保持左侧分组展开 */
|
||||
const findParentPaths = (menus: RouteItems, active: string, trail: string[] = []): string[] | null => {
|
||||
for (const m of menus || []) {
|
||||
if (!m) continue
|
||||
if (m.path === active) {
|
||||
return trail
|
||||
}
|
||||
if (m.children && m.children.length > 0) {
|
||||
const found = findParentPaths(m.children as RouteItems, active, [...trail, m.path])
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
// 隐藏子路由 / 详情页:按前缀打开最近父级
|
||||
for (const m of menus || []) {
|
||||
if (!m?.children?.length) continue
|
||||
if (active.startsWith(`${m.path}/`)) {
|
||||
const found = findParentPaths(m.children as RouteItems, active, [...trail, m.path])
|
||||
if (found) return found
|
||||
return [...trail, m.path]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const refreshMenuState = (currentRoute: RouteToFrom = route) => {
|
||||
const active = setParentHighlight(currentRoute) as string
|
||||
const openeds = findParentPaths(menuLists.value, active) || []
|
||||
const sameActive = state.defaultActive === active
|
||||
const sameOpeneds =
|
||||
state.defaultOpeneds.length === openeds.length && state.defaultOpeneds.every((p, i) => p === openeds[i])
|
||||
state.defaultActive = active
|
||||
state.defaultOpeneds = openeds
|
||||
// 仅在激活项或父级展开变化时重渲染,避免无意义闪烁
|
||||
if (!sameActive || !sameOpeneds) {
|
||||
state.menuRenderKey += 1
|
||||
}
|
||||
}
|
||||
|
||||
// 打开外部链接
|
||||
const onALinkClick = (val: RouteItem) => {
|
||||
other.handleOpenLink(val);
|
||||
};
|
||||
other.handleOpenLink(val)
|
||||
}
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
state.defaultActive = setParentHighlight(route);
|
||||
});
|
||||
refreshMenuState(route)
|
||||
})
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
state.defaultActive = setParentHighlight(to);
|
||||
const clientWidth = document.body.clientWidth;
|
||||
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
|
||||
});
|
||||
refreshMenuState(to)
|
||||
const clientWidth = document.body.clientWidth
|
||||
if (clientWidth < 1000) themeConfig.value.isCollapse = false
|
||||
})
|
||||
// 侧栏菜单数据被经典分栏/路由过滤重建后,重新带上父级展开
|
||||
watch(
|
||||
menuLists,
|
||||
() => {
|
||||
refreshMenuState(route)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
// 设置菜单的收起/展开
|
||||
watch(
|
||||
themeConfig.value,
|
||||
() => {
|
||||
document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse);
|
||||
document.body.clientWidth <= 1000 ? (state.isCollapse = false) : (state.isCollapse = themeConfig.value.isCollapse)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
},
|
||||
)
|
||||
</script>
|
||||
Reference in new issue
Block a user