feat(projects): 添加多级菜单页面

This commit is contained in:
Soybean
2021-09-29 17:59:32 +08:00
parent 2a7709ec04
commit 3f49d6db30
9 changed files with 88 additions and 83 deletions

View File

@ -1,10 +1,5 @@
<template>
<div
class="mb-6px px-4px cursor-pointer"
@click="handleRouter"
@mouseenter="handleMouseEvent('enter')"
@mouseleave="handleMouseEvent('leave')"
>
<div class="mb-6px px-4px cursor-pointer" @mouseenter="setTrue" @mouseleave="setFalse">
<div
class="flex-center flex-col py-12px rounded-2px"
:class="{ 'text-primary bg-primary bg-opacity-10': isActive, 'text-primary': isHover }"
@ -23,10 +18,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import type { PropType, VNodeChild } from 'vue';
import { useRouter } from 'vue-router';
import { useAppStore } from '@/store';
import { useBoolean } from '@/hooks';
import { useVerticalMixSiderContext } from '@/context';
const props = defineProps({
routeName: {
@ -51,40 +43,8 @@ const props = defineProps({
}
});
const app = useAppStore();
const router = useRouter();
const { useVerticalMixSiderInject } = useVerticalMixSiderContext();
const { bool: isHover, setTrue, setFalse } = useBoolean();
const { bool: isMouseEnterMenu, setTrue: setMouseEnterMenu, setFalse: setMouseLeaveMenu } = useBoolean();
const { setHoverRouteName, showChildMenu, hideChildMenu, isMouseEnterChildMenu } = useVerticalMixSiderInject();
const isActive = computed(() => props.routeName === props.activeRouteName);
function handleRouter() {
router.push({ name: props.routeName });
}
async function setActiveHoverRouteName() {
setTimeout(() => {
if (app.menu.fixedMix && !isMouseEnterChildMenu.value && !isMouseEnterMenu.value) {
setHoverRouteName(props.activeRouteName);
}
setMouseLeaveMenu();
}, 100);
}
function handleMouseEvent(type: 'enter' | 'leave') {
if (type === 'enter') {
setMouseEnterMenu();
setTrue();
setHoverRouteName(props.routeName);
showChildMenu();
} else {
setFalse();
hideChildMenu();
setActiveHoverRouteName();
}
}
</script>
<style scoped></style>

View File

@ -13,13 +13,10 @@
dark:bg-[#18181c]
"
:style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }"
@mouseenter="handleMouseEvent('enter')"
@mouseleave="handleMouseEvent('leave')"
>
<header class="header-height flex-y-center justify-between">
<h2 class="pl-8px text-16px text-primary font-bold">{{ title }}</h2>
<div class="px-8px text-16px cursor-pointer" @click="toggleFixedMixMenu">
<div class="px-8px text-16px text-gray-600 cursor-pointer" @click="toggleFixedMixMenu">
<icon-mdi:pin-off v-if="app.menu.fixedMix" />
<icon-mdi:pin v-else />
</div>
@ -39,11 +36,14 @@ import { NScrollbar, NMenu } from 'naive-ui';
import type { MenuOption } from 'naive-ui';
import { useThemeStore, useAppStore } from '@/store';
import { useAppTitle } from '@/hooks';
import { useVerticalMixSiderContext } from '@/context';
import { menus } from '@/router';
import type { GlobalMenuOption } from '@/interface';
const props = defineProps({
visible: {
type: Boolean,
default: false
},
activeRouteName: {
type: String,
required: true
@ -55,23 +55,12 @@ const route = useRoute();
const theme = useThemeStore();
const app = useAppStore();
const { toggleFixedMixMenu } = useAppStore();
const { useVerticalMixSiderInject } = useVerticalMixSiderContext();
const title = useAppTitle();
const {
childMenuVisible,
hoverRouteName,
setHoverRouteName,
showChildMenu,
hideChildMenu,
setMouseEnterChildMenu,
setMouseLeaveChildMenu
} = useVerticalMixSiderInject();
const childMenus = computed(() => {
const children: MenuOption[] = [];
menus.some(item => {
const flag = item.routeName === hoverRouteName.value && Boolean(item.children?.length);
const flag = item.routeName === props.activeRouteName && Boolean(item.children?.length);
if (flag) {
children.push(...item.children!);
}
@ -80,7 +69,7 @@ const childMenus = computed(() => {
return children;
});
const showDrawer = computed(() => (childMenuVisible.value && childMenus.value.length) || app.menu.fixedMix);
const showDrawer = computed(() => (props.visible && childMenus.value.length) || app.menu.fixedMix);
const activeKey = computed(() => route.name as string);
@ -93,17 +82,6 @@ const headerHeight = computed(() => {
const { height } = theme.headerStyle;
return `${height}px`;
});
function handleMouseEvent(type: 'enter' | 'leave') {
if (type === 'enter') {
showChildMenu();
setMouseEnterChildMenu();
} else {
hideChildMenu();
setMouseLeaveChildMenu();
setHoverRouteName(props.activeRouteName);
}
}
</script>
<style scoped>
.drawer-shadow {

View File

@ -1,5 +1,5 @@
<template>
<div class="flex h-full bg-white dark:bg-[#18181c]">
<div class="flex h-full bg-white dark:bg-[#18181c]" @mouseleave="handleMouseLeaveMenu">
<div
class="flex-col-stretch flex-1 h-full transition-width duration-300 ease-in-out"
:class="[app.menu.collapsed ? 'mix-menu-collapsed-width' : 'mix-menu-width']"
@ -15,6 +15,7 @@
:icon="item.icon"
:active-route-name="activeParentRouteName"
:is-mini="app.menu.collapsed"
@click="handleMixMenu(item.routeName)"
/>
</n-scrollbar>
</div>
@ -24,26 +25,26 @@
class="relative h-full transition-width duration-300 ease-in-out"
:style="{ width: app.menu.fixedMix ? theme.menuStyle.width + 'px' : '0px' }"
>
<mix-menu-drawer :active-route-name="activeParentRouteName" />
<mix-menu-drawer :visible="drawerVisible" :active-route-name="activeParentRouteName" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { ref, computed } from 'vue';
import type { VNodeChild } from 'vue';
import { NScrollbar } from 'naive-ui';
import { useRoute } from 'vue-router';
import { useAppStore, useThemeStore } from '@/store';
import { useVerticalMixSiderContext } from '@/context';
import { menus } from '@/router';
import { MixMenu, MixMenuCollapse, MixMenuDrawer } from './components';
import { GlobalLogo } from '../../../common';
import { useBoolean } from '@/hooks';
const theme = useThemeStore();
const app = useAppStore();
const route = useRoute();
const { useVerticalMixSiderProvide } = useVerticalMixSiderContext();
const { bool: drawerVisible, setTrue: openDrawer, setFalse: hideDrawer } = useBoolean();
const mixMenuWidth = computed(() => `${theme.menuStyle.mixWidth}px`);
const mixMenuCollapsedWidth = computed(() => `${theme.menuStyle.mixCollapsedWidth}px`);
@ -52,7 +53,6 @@ const firstDegreeMenus = menus.map(item => {
const { routeName } = item;
const label = item.label as string;
const icon = item.icon! as () => VNodeChild;
return {
routeName,
label,
@ -60,16 +60,26 @@ const firstDegreeMenus = menus.map(item => {
};
});
const activeParentRouteName = computed(() => {
const activeParentRouteName = ref(getActiveRouteName());
function getActiveRouteName() {
let name = '';
const { matched } = route;
if (matched.length) {
name = matched[0].name as string;
}
return name;
});
}
useVerticalMixSiderProvide();
function handleMixMenu(routeName: string) {
activeParentRouteName.value = routeName;
openDrawer();
}
function handleMouseLeaveMenu() {
activeParentRouteName.value = getActiveRouteName();
hideDrawer();
}
</script>
<style scoped>
.mix-menu-width {

View File

@ -0,0 +1,6 @@
<template>
<router-view />
</template>
<script setup lang="ts"></script>
<style scoped></style>

View File

@ -1,4 +1,5 @@
import BasicLayout from './BasicLayout/index.vue';
import BlankLayout from './BlankLayout/index.vue';
import MultiMenuLayout from './MultiMenuLayout/index.vue';
export { BasicLayout, BlankLayout };
export { BasicLayout, BlankLayout, MultiMenuLayout };