feat(projects): 新增顶部菜单
This commit is contained in:
parent
e9db67ee12
commit
221d2cc02d
@ -1,5 +1,5 @@
|
|||||||
export { ContentType, EnumDataType, EnumLoginModule } from './common';
|
export { ContentType, EnumDataType, EnumLoginModule } from './common';
|
||||||
export { EnumAnimate } from './animate';
|
export { EnumAnimate } from './animate';
|
||||||
export { EnumNavMode, EnumNavTheme, EnumMultiTabMode } from './theme';
|
export { EnumNavMode, EnumNavTheme, EnumMultiTabMode, EnumHorizontalMenuPosition } from './theme';
|
||||||
export { EnumRoutePath, EnumRouteTitle } from './route';
|
export { EnumRoutePath, EnumRouteTitle } from './route';
|
||||||
export { EnumStorageKey } from './storage';
|
export { EnumStorageKey } from './storage';
|
||||||
|
@ -13,7 +13,15 @@ export enum EnumNavTheme {
|
|||||||
'header-dark' = '暗色的侧边栏和顶栏'
|
'header-dark' = '暗色的侧边栏和顶栏'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 多页签风格 */
|
||||||
export enum EnumMultiTabMode {
|
export enum EnumMultiTabMode {
|
||||||
'button' = '按钮风格',
|
'button' = '按钮风格',
|
||||||
'browser' = '浏览器风格'
|
'browser' = '浏览器风格'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 水平模式的菜单位置 */
|
||||||
|
export enum EnumHorizontalMenuPosition {
|
||||||
|
'flex-start' = '居左',
|
||||||
|
'center' = '居中',
|
||||||
|
'flex-end' = '居右'
|
||||||
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export { UserInfo } from './business';
|
export { UserInfo } from './business';
|
||||||
export { ThemeSettings, NavMode, MultiTabMode, AnimateType } from './theme';
|
export { ThemeSettings, NavMode, MultiTabMode, AnimateType, HorizontalMenuPosition } from './theme';
|
||||||
export { CustomRoute, RoutePathKey, GlobalMenuOption, LoginModuleType } from './common';
|
export { CustomRoute, RoutePathKey, GlobalMenuOption, LoginModuleType } from './common';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EnumAnimate, EnumNavMode, EnumNavTheme, EnumMultiTabMode } from '@/enum';
|
import { EnumAnimate, EnumNavMode, EnumNavTheme, EnumMultiTabMode, EnumHorizontalMenuPosition } from '@/enum';
|
||||||
|
|
||||||
export interface ThemeSettings {
|
export interface ThemeSettings {
|
||||||
/** 深色模式 */
|
/** 深色模式 */
|
||||||
@ -56,6 +56,13 @@ interface HeaderStyle {
|
|||||||
bgColor: string;
|
bgColor: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type HorizontalMenuPosition = keyof typeof EnumHorizontalMenuPosition;
|
||||||
|
|
||||||
|
interface HorizontalMenuPositionList {
|
||||||
|
value: HorizontalMenuPosition;
|
||||||
|
label: EnumHorizontalMenuPosition;
|
||||||
|
}
|
||||||
|
|
||||||
interface MenuStyle {
|
interface MenuStyle {
|
||||||
/** 菜单宽度 */
|
/** 菜单宽度 */
|
||||||
width: number;
|
width: number;
|
||||||
@ -67,6 +74,10 @@ interface MenuStyle {
|
|||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
/** 分割菜单 */
|
/** 分割菜单 */
|
||||||
splitMenu: boolean;
|
splitMenu: boolean;
|
||||||
|
/** 水平模式的菜单的位置 */
|
||||||
|
horizontalPosition: HorizontalMenuPosition;
|
||||||
|
/** 水平模式的菜单的位置列表 */
|
||||||
|
horizontalPositionList: HorizontalMenuPositionList[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MultiTabMode = keyof typeof EnumMultiTabMode;
|
export type MultiTabMode = keyof typeof EnumMultiTabMode;
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<n-menu :value="activeKey" mode="horizontal" :options="menus" @update:value="handleUpdateMenu" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import type { MenuOption } from 'naive-ui';
|
||||||
|
import { NMenu } from 'naive-ui';
|
||||||
|
import { menus } from '@/router';
|
||||||
|
import { GlobalMenuOption } from '@/interface';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const activeKey = computed(() => getActiveKey());
|
||||||
|
|
||||||
|
function getActiveKey() {
|
||||||
|
return route.name as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateMenu(key: string, item: MenuOption) {
|
||||||
|
const menuItem = item as GlobalMenuOption;
|
||||||
|
router.push(menuItem.routePath);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
@ -5,11 +5,12 @@
|
|||||||
<div v-if="!theme.isVerticalNav" class="menu-width h-full">
|
<div v-if="!theme.isVerticalNav" class="menu-width h-full">
|
||||||
<global-logo />
|
<global-logo />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-y-center h-full">
|
<div class="flex-1-hidden flex-y-center h-full" :style="{ justifyContent: theme.menuStyle.horizontalPosition }">
|
||||||
<menu-collapse v-if="theme.navStyle.mode !== 'horizontal'" />
|
<menu-collapse v-if="theme.navStyle.mode !== 'horizontal'" />
|
||||||
<global-breadcrumb v-if="theme.crumbsStyle.visible" />
|
<global-breadcrumb v-if="theme.crumbsStyle.visible && theme.navStyle.mode !== 'horizontal'" />
|
||||||
|
<header-menu v-if="theme.navStyle.mode === 'horizontal'" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 flex justify-end h-full">
|
<div class="flex justify-end h-full">
|
||||||
<gihub-site />
|
<gihub-site />
|
||||||
<full-screen />
|
<full-screen />
|
||||||
<user-avatar />
|
<user-avatar />
|
||||||
@ -25,6 +26,7 @@ import { NLayoutHeader } from 'naive-ui';
|
|||||||
import { useThemeStore } from '@/store';
|
import { useThemeStore } from '@/store';
|
||||||
import { GlobalBreadcrumb, UserAvatar, MenuCollapse, FullScreen, GihubSite, SettingDrawerButton } from './components';
|
import { GlobalBreadcrumb, UserAvatar, MenuCollapse, FullScreen, GihubSite, SettingDrawerButton } from './components';
|
||||||
import { GlobalLogo } from '../common';
|
import { GlobalLogo } from '../common';
|
||||||
|
import HeaderMenu from './components/HeaderMenu.vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
zIndex: {
|
zIndex: {
|
||||||
|
@ -4,25 +4,13 @@
|
|||||||
<setting-menu-item label="分割菜单">
|
<setting-menu-item label="分割菜单">
|
||||||
<n-switch :value="theme.menuStyle.splitMenu" @update:value="handleSplitMenu" />
|
<n-switch :value="theme.menuStyle.splitMenu" @update:value="handleSplitMenu" />
|
||||||
</setting-menu-item>
|
</setting-menu-item>
|
||||||
<setting-menu-item label="固定头部和多页签">
|
<setting-menu-item label="顶部菜单位置">
|
||||||
<n-switch :value="splitMenu" :disabled="disabledSplitMenu" @update:value="handleFixedHeaderAndTab" />
|
<n-select
|
||||||
</setting-menu-item>
|
|
||||||
<setting-menu-item label="头部高度">
|
|
||||||
<n-input-number
|
|
||||||
class="w-120px"
|
class="w-120px"
|
||||||
size="small"
|
size="small"
|
||||||
:value="theme.headerStyle.height"
|
:value="theme.menuStyle.horizontalPosition"
|
||||||
:step="1"
|
:options="theme.menuStyle.horizontalPositionList"
|
||||||
@update:value="handleHeaderHeight"
|
@update:value="handleHorizontalMenuPosition"
|
||||||
/>
|
|
||||||
</setting-menu-item>
|
|
||||||
<setting-menu-item label="多页签高度">
|
|
||||||
<n-input-number
|
|
||||||
class="w-120px"
|
|
||||||
size="small"
|
|
||||||
:value="theme.multiTabStyle.height"
|
|
||||||
:step="1"
|
|
||||||
@update:value="handleMultiTabHeight"
|
|
||||||
/>
|
/>
|
||||||
</setting-menu-item>
|
</setting-menu-item>
|
||||||
<setting-menu-item label="菜单展开宽度">
|
<setting-menu-item label="菜单展开宽度">
|
||||||
@ -45,18 +33,40 @@
|
|||||||
@update:value="handleMixMenuWidth"
|
@update:value="handleMixMenuWidth"
|
||||||
/>
|
/>
|
||||||
</setting-menu-item>
|
</setting-menu-item>
|
||||||
|
<setting-menu-item label="固定头部和多页签">
|
||||||
|
<n-switch :value="splitMenu" :disabled="disabledSplitMenu" @update:value="handleFixedHeaderAndTab" />
|
||||||
|
</setting-menu-item>
|
||||||
|
<setting-menu-item label="头部高度">
|
||||||
|
<n-input-number
|
||||||
|
class="w-120px"
|
||||||
|
size="small"
|
||||||
|
:value="theme.headerStyle.height"
|
||||||
|
:step="1"
|
||||||
|
@update:value="handleHeaderHeight"
|
||||||
|
/>
|
||||||
|
</setting-menu-item>
|
||||||
|
<setting-menu-item label="多页签高度">
|
||||||
|
<n-input-number
|
||||||
|
class="w-120px"
|
||||||
|
size="small"
|
||||||
|
:value="theme.multiTabStyle.height"
|
||||||
|
:step="1"
|
||||||
|
@update:value="handleMultiTabHeight"
|
||||||
|
/>
|
||||||
|
</setting-menu-item>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { NDivider, NSpace, NSwitch, NInputNumber } from 'naive-ui';
|
import { NDivider, NSpace, NSwitch, NSelect, NInputNumber } from 'naive-ui';
|
||||||
import { useThemeStore } from '@/store';
|
import { useThemeStore } from '@/store';
|
||||||
import { SettingMenuItem } from '../common';
|
import { SettingMenuItem } from '../common';
|
||||||
|
|
||||||
const theme = useThemeStore();
|
const theme = useThemeStore();
|
||||||
const {
|
const {
|
||||||
handleSplitMenu,
|
handleSplitMenu,
|
||||||
|
handleHorizontalMenuPosition,
|
||||||
handleFixedHeaderAndTab,
|
handleFixedHeaderAndTab,
|
||||||
handleHeaderHeight,
|
handleHeaderHeight,
|
||||||
handleMultiTabHeight,
|
handleMultiTabHeight,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-layout class="h-full" has-sider>
|
<n-layout class="h-full" has-sider>
|
||||||
<global-sider v-if="theme.isVerticalNav" :z-index="3" />
|
<global-sider v-if="theme.isVerticalNav" :z-index="3" />
|
||||||
<global-header v-if="isHorizontalMix" :z-index="2" />
|
<global-header v-if="isHorizontalMix" :z-index="4" />
|
||||||
<div class="flex-1-hidden flex h-full">
|
<div class="flex-1-hidden flex h-full">
|
||||||
<global-sider v-if="isHorizontalMix" class="sider-margin" :z-index="3" />
|
<global-sider v-if="isHorizontalMix" class="sider-margin" :z-index="3" />
|
||||||
<n-scrollbar
|
<n-scrollbar
|
||||||
@ -39,6 +39,11 @@ const { scrollbar, resetScrollBehavior } = useScrollBehavior();
|
|||||||
const routeProps = useRouteProps();
|
const routeProps = useRouteProps();
|
||||||
|
|
||||||
const isHorizontalMix = computed(() => theme.navStyle.mode === 'horizontal-mix');
|
const isHorizontalMix = computed(() => theme.navStyle.mode === 'horizontal-mix');
|
||||||
|
|
||||||
|
const headerHeight = computed(() => {
|
||||||
|
const { height } = theme.headerStyle;
|
||||||
|
return `${height}px`;
|
||||||
|
});
|
||||||
const headerAndMultiTabHeight = computed(() => {
|
const headerAndMultiTabHeight = computed(() => {
|
||||||
const {
|
const {
|
||||||
headerStyle: { height: hHeight },
|
headerStyle: { height: hHeight },
|
||||||
@ -59,7 +64,7 @@ watch(
|
|||||||
z-index: 11;
|
z-index: 11;
|
||||||
}
|
}
|
||||||
.sider-margin {
|
.sider-margin {
|
||||||
margin-top: v-bind(headerAndMultiTabHeight);
|
margin-top: v-bind(headerHeight);
|
||||||
}
|
}
|
||||||
.content-padding {
|
.content-padding {
|
||||||
padding-top: v-bind(headerAndMultiTabHeight);
|
padding-top: v-bind(headerAndMultiTabHeight);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ThemeSettings } from '../interface';
|
import type { ThemeSettings } from '../interface';
|
||||||
import { EnumAnimate, EnumMultiTabMode } from '../enum';
|
import { EnumAnimate, EnumMultiTabMode, EnumHorizontalMenuPosition } from '../enum';
|
||||||
|
|
||||||
const themeColorList = [
|
const themeColorList = [
|
||||||
'#409EFF',
|
'#409EFF',
|
||||||
@ -41,7 +41,13 @@ const themeSettings: ThemeSettings = {
|
|||||||
mixWidth: 80,
|
mixWidth: 80,
|
||||||
collapsedWidth: 64,
|
collapsedWidth: 64,
|
||||||
fixed: true,
|
fixed: true,
|
||||||
splitMenu: false
|
splitMenu: false,
|
||||||
|
horizontalPosition: 'flex-start',
|
||||||
|
horizontalPositionList: [
|
||||||
|
{ value: 'flex-start', label: EnumHorizontalMenuPosition['flex-start'] },
|
||||||
|
{ value: 'center', label: EnumHorizontalMenuPosition.center },
|
||||||
|
{ value: 'flex-end', label: EnumHorizontalMenuPosition['flex-end'] }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
headerStyle: {
|
headerStyle: {
|
||||||
height: 56,
|
height: 56,
|
||||||
|
@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
|
|||||||
import type { GlobalThemeOverrides } from 'naive-ui';
|
import type { GlobalThemeOverrides } from 'naive-ui';
|
||||||
import { themeSettings } from '@/settings';
|
import { themeSettings } from '@/settings';
|
||||||
import { store } from '@/store';
|
import { store } from '@/store';
|
||||||
import type { ThemeSettings, NavMode, MultiTabMode, AnimateType } from '@/interface';
|
import type { ThemeSettings, NavMode, MultiTabMode, AnimateType, HorizontalMenuPosition } from '@/interface';
|
||||||
import { getHoverAndPressedColor } from './helpers';
|
import { getHoverAndPressedColor } from './helpers';
|
||||||
|
|
||||||
type ThemeState = ThemeSettings;
|
type ThemeState = ThemeSettings;
|
||||||
@ -85,6 +85,10 @@ const themeStore = defineStore({
|
|||||||
this.menuStyle.mixWidth = width;
|
this.menuStyle.mixWidth = width;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/** 更改顶部水平菜单的位置 */
|
||||||
|
handleHorizontalMenuPosition(position: HorizontalMenuPosition) {
|
||||||
|
this.menuStyle.horizontalPosition = position;
|
||||||
|
},
|
||||||
/** 更改头部的高度 */
|
/** 更改头部的高度 */
|
||||||
handleHeaderHeight(height: number | null) {
|
handleHeaderHeight(height: number | null) {
|
||||||
if (height !== null) {
|
if (height !== null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user