refactor(components): blankLayout引入GlobalContent
This commit is contained in:
44
src/layouts/common/VerticalMixSider/components/MixMenu.vue
Normal file
44
src/layouts/common/VerticalMixSider/components/MixMenu.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<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-active': isActive, 'text-primary': isHover }"
|
||||
>
|
||||
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
|
||||
<p
|
||||
class="pt-8px text-12px overflow-hidden transition-height duration-200 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-20px pt-8px']"
|
||||
>
|
||||
{{ label }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import type { VNodeChild } from 'vue';
|
||||
import { useBoolean } from '@/hooks';
|
||||
|
||||
interface Props {
|
||||
/** 路由名称 */
|
||||
routeName: string;
|
||||
/** 路由名称文本 */
|
||||
label: string;
|
||||
/** 路由图标 */
|
||||
icon: VNodeChild;
|
||||
/** 当前激活状态的理由名称 */
|
||||
activeRouteName: string;
|
||||
/** mini尺寸的路由 */
|
||||
isMini?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isMini: false
|
||||
});
|
||||
|
||||
const { bool: isHover, setTrue, setFalse } = useBoolean();
|
||||
|
||||
const isActive = computed(() => props.routeName === props.activeRouteName);
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div class="flex-center h-36px text-[#333639] dark:text-[#ffffffd1] cursor-pointer" @click="toggleMenu">
|
||||
<icon-ph-caret-double-right-bold v-if="app.menu.collapsed" class="text-16px" />
|
||||
<icon-ph-caret-double-left-bold v-else class="text-16px" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
const app = useAppStore();
|
||||
const { toggleMenu } = useAppStore();
|
||||
</script>
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
drawer-shadow
|
||||
absolute-lt
|
||||
flex-col-stretch
|
||||
h-full
|
||||
overflow-hidden
|
||||
bg-white
|
||||
dark:bg-dark
|
||||
transition-width
|
||||
duration-300
|
||||
ease-in-out
|
||||
"
|
||||
:style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }"
|
||||
>
|
||||
<header class="header-height flex-y-center justify-between">
|
||||
<h2 class="text-primary pl-8px text-16px font-bold">{{ title }}</h2>
|
||||
<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>
|
||||
</header>
|
||||
<div class="flex-1-hidden">
|
||||
<n-scrollbar>
|
||||
<n-menu :value="activeKey" :options="childMenus" :indent="18" @update:value="handleUpdateMenu" />
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { NScrollbar, NMenu } from 'naive-ui';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useThemeStore, useAppStore } from '@/store';
|
||||
import { useAppTitle } from '@/composables';
|
||||
import { menus } from '@/router';
|
||||
import type { GlobalMenuOption } from '@/interface';
|
||||
|
||||
interface Props {
|
||||
/** 菜单抽屉可见性 */
|
||||
visible?: boolean;
|
||||
/** 激活状态的路由名称 */
|
||||
activeRouteName: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const theme = useThemeStore();
|
||||
const app = useAppStore();
|
||||
const { toggleFixedMixMenu } = useAppStore();
|
||||
const title = useAppTitle();
|
||||
|
||||
const childMenus = computed(() => {
|
||||
const children: MenuOption[] = [];
|
||||
menus.some(item => {
|
||||
const flag = item.routeName === props.activeRouteName && Boolean(item.children?.length);
|
||||
if (flag) {
|
||||
children.push(...item.children!);
|
||||
}
|
||||
return flag;
|
||||
});
|
||||
return children;
|
||||
});
|
||||
|
||||
const showDrawer = computed(() => (props.visible && childMenus.value.length) || app.menu.fixedMix);
|
||||
|
||||
const activeKey = computed(() => route.name as string);
|
||||
|
||||
function handleUpdateMenu(key: string, item: MenuOption) {
|
||||
const menuItem = item as GlobalMenuOption;
|
||||
router.push(menuItem.routePath);
|
||||
}
|
||||
|
||||
const headerHeight = computed(() => {
|
||||
const { height } = theme.headerStyle;
|
||||
return `${height}px`;
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.drawer-shadow {
|
||||
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
|
||||
}
|
||||
.header-height {
|
||||
height: v-bind(headerHeight);
|
||||
}
|
||||
</style>
|
||||
5
src/layouts/common/VerticalMixSider/components/index.ts
Normal file
5
src/layouts/common/VerticalMixSider/components/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import MixMenu from './MixMenu.vue';
|
||||
import MixMenuCollapse from './MixMenuCollapse.vue';
|
||||
import MixMenuDrawer from './MixMenuDrawer.vue';
|
||||
|
||||
export { MixMenu, MixMenuCollapse, MixMenuDrawer };
|
||||
104
src/layouts/common/VerticalMixSider/index.vue
Normal file
104
src/layouts/common/VerticalMixSider/index.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<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']"
|
||||
>
|
||||
<global-logo :show-title="false" />
|
||||
<div class="flex-1-hidden">
|
||||
<n-scrollbar>
|
||||
<mix-menu
|
||||
v-for="item in firstDegreeMenus"
|
||||
:key="item.routeName"
|
||||
:route-name="item.routeName"
|
||||
:label="item.label"
|
||||
:icon="item.icon"
|
||||
:active-route-name="activeParentRouteName"
|
||||
:is-mini="app.menu.collapsed"
|
||||
@click="handleMixMenu(item.routeName, item.isSingle)"
|
||||
/>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
<mix-menu-collapse />
|
||||
</div>
|
||||
<div
|
||||
class="relative h-full transition-width duration-300 ease-in-out"
|
||||
:style="{ width: app.menu.fixedMix ? theme.menuStyle.width + 'px' : '0px' }"
|
||||
>
|
||||
<mix-menu-drawer :visible="drawerVisible" :active-route-name="activeParentRouteName" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import type { VNodeChild } from 'vue';
|
||||
import { NScrollbar } from 'naive-ui';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
import { menus } from '@/router';
|
||||
import { routeNameWatcher } from '@/composables';
|
||||
import { useBoolean } from '@/hooks';
|
||||
import { MixMenu, MixMenuCollapse, MixMenuDrawer } from './components';
|
||||
import GlobalLogo from '../GlobalLogo/index.vue';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const app = useAppStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { bool: drawerVisible, setTrue: openDrawer, setFalse: hideDrawer } = useBoolean();
|
||||
|
||||
const mixMenuWidth = computed(() => `${theme.menuStyle.mixWidth}px`);
|
||||
const mixMenuCollapsedWidth = computed(() => `${theme.menuStyle.mixCollapsedWidth}px`);
|
||||
|
||||
const firstDegreeMenus = menus.map(item => {
|
||||
const { routeName } = item;
|
||||
const label = item.label as string;
|
||||
const icon = item.icon! as () => VNodeChild;
|
||||
const isSingle = !(item.children && item.children.length);
|
||||
|
||||
return {
|
||||
routeName,
|
||||
label,
|
||||
icon,
|
||||
isSingle
|
||||
};
|
||||
});
|
||||
|
||||
const activeParentRouteName = ref(getActiveRouteName());
|
||||
|
||||
function getActiveRouteName() {
|
||||
let name = '';
|
||||
const menuMatched = route.matched.filter(item => !item.meta.isNotMenu);
|
||||
if (menuMatched.length) {
|
||||
name = menuMatched[0].name as string;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function handleMixMenu(routeName: string, isSingle: boolean) {
|
||||
activeParentRouteName.value = routeName;
|
||||
if (isSingle) {
|
||||
router.push({ name: routeName });
|
||||
} else {
|
||||
openDrawer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMouseLeaveMenu() {
|
||||
activeParentRouteName.value = getActiveRouteName();
|
||||
hideDrawer();
|
||||
}
|
||||
|
||||
routeNameWatcher(() => {
|
||||
activeParentRouteName.value = getActiveRouteName();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.mix-menu_width {
|
||||
width: v-bind(mixMenuWidth);
|
||||
}
|
||||
.mix-menu_collapsed-width {
|
||||
width: v-bind(mixMenuCollapsedWidth);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user