mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-17 17:26:38 +08:00
feat(projects): 面包屑
This commit is contained in:
parent
e25afe2fad
commit
09c7658c21
@ -1,7 +1,18 @@
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import type { MenuOption, DropdownOption } from 'naive-ui';
|
||||
|
||||
/** 菜单项配置 */
|
||||
export type GlobalMenuOption = MenuOption & {
|
||||
routeName: string;
|
||||
routePath: string;
|
||||
};
|
||||
|
||||
/** 面包屑 */
|
||||
export type GlobalBreadcrumb = DropdownOption & {
|
||||
key: string;
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
routeName: string;
|
||||
hasChildren: boolean;
|
||||
iconName?: string;
|
||||
children?: GlobalBreadcrumb[];
|
||||
};
|
||||
|
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<n-breadcrumb class="px-12px">
|
||||
<template v-for="breadcrumb in breadcrumbs" :key="breadcrumb.key">
|
||||
<n-breadcrumb-item>
|
||||
<n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.children" @select="dropdownSelect">
|
||||
<span>
|
||||
<component :is="breadcrumb.icon" v-if="theme.header.crumb.showIcon" class="inline-block mr-4px text-16px" />
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
</span>
|
||||
</n-dropdown>
|
||||
<template v-else>
|
||||
<component :is="breadcrumb.icon" v-if="theme.header.crumb.showIcon" class="inline-block mr-4px text-16px" />
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
</template>
|
||||
</n-breadcrumb-item>
|
||||
</template>
|
||||
</n-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { NBreadcrumb, NBreadcrumbItem, NDropdown } from 'naive-ui';
|
||||
import { routePath } from '@/router';
|
||||
import { useThemeStore, useRouteStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { getBreadcrumbByRouteKey } from '@/utils';
|
||||
import type { GlobalMenuOption } from '@/interface';
|
||||
|
||||
const route = useRoute();
|
||||
const theme = useThemeStore();
|
||||
const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const breadcrumbs = computed(() =>
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as GlobalMenuOption[], routePath('root'))
|
||||
);
|
||||
|
||||
function dropdownSelect(key: string) {
|
||||
routerPush({ name: key });
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
26
src/layouts/common/GlobalHeader/components/HeaderMenu.vue
Normal file
26
src/layouts/common/GlobalHeader/components/HeaderMenu.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<n-menu :value="activeKey" mode="horizontal" :options="menus" @update:value="handleUpdateMenu" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { NMenu } from 'naive-ui';
|
||||
import type { MenuOption } from 'naive-ui';
|
||||
import { useRouteStore } from '@/store';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import type { GlobalMenuOption } from '@/interface';
|
||||
|
||||
const route = useRoute();
|
||||
const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const menus = computed(() => routeStore.menus as GlobalMenuOption[]);
|
||||
const activeKey = computed(() => route.name as string);
|
||||
|
||||
function handleUpdateMenu(_key: string, item: MenuOption) {
|
||||
const menuItem = item as GlobalMenuOption;
|
||||
routerPush(menuItem.routePath);
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
@ -1,7 +1,9 @@
|
||||
import MenuCollapse from './MenuCollapse.vue';
|
||||
import GlobalBreadcrumb from './GlobalBreadcrumb.vue';
|
||||
import HeaderMenu from './HeaderMenu.vue';
|
||||
import GithubSite from './GithubSite.vue';
|
||||
import FullScreen from './FullScreen.vue';
|
||||
import ThemeMode from './ThemeMode.vue';
|
||||
import UserAvatar from './UserAvatar.vue';
|
||||
|
||||
export { MenuCollapse, GithubSite, FullScreen, ThemeMode, UserAvatar };
|
||||
export { MenuCollapse, GlobalBreadcrumb, HeaderMenu, GithubSite, FullScreen, ThemeMode, UserAvatar };
|
||||
|
@ -3,13 +3,11 @@
|
||||
<global-logo v-if="showLogo" :show-title="true" class="h-full" :style="{ width: theme.sider.width + 'px' }" />
|
||||
<div v-if="!showHeaderMenu" class="flex-1-hidden flex-y-center h-full">
|
||||
<menu-collapse v-if="showMenuCollape" />
|
||||
<!-- <global-breadcrumb v-if="theme.header.crumb.visible" /> -->
|
||||
<global-breadcrumb v-if="theme.header.crumb.visible" />
|
||||
</div>
|
||||
<div v-else class="flex-1-hidden flex-y-center h-full" :style="{ justifyContent: theme.menu.horizontalPosition }">
|
||||
<header-menu />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="flex-1-hidden flex-y-center h-full"
|
||||
:style="{ justifyContent: theme.menu.horizontalPosition }"
|
||||
></div>
|
||||
<div class="flex justify-end h-full">
|
||||
<github-site />
|
||||
<full-screen />
|
||||
@ -24,7 +22,15 @@ import { DarkModeContainer } from '@/components';
|
||||
import { useThemeStore } from '@/store';
|
||||
import type { GlobalHeaderProps } from '@/interface';
|
||||
import GlobalLogo from '../GlobalLogo/index.vue';
|
||||
import { MenuCollapse, GithubSite, FullScreen, ThemeMode, UserAvatar } from './components';
|
||||
import {
|
||||
MenuCollapse,
|
||||
GlobalBreadcrumb,
|
||||
HeaderMenu,
|
||||
GithubSite,
|
||||
FullScreen,
|
||||
ThemeMode,
|
||||
UserAvatar
|
||||
} from './components';
|
||||
|
||||
interface Props {
|
||||
/** 显示logo */
|
||||
|
@ -54,7 +54,7 @@ export const constantRoutes: AuthRoute.Route[] = [
|
||||
singleLayout: 'blank'
|
||||
}
|
||||
},
|
||||
// 匹配无效的路径重定向not-found的页面
|
||||
// 匹配无效路径的路由
|
||||
{
|
||||
name: 'not-found-page',
|
||||
path: '/:pathMatch(.*)*',
|
||||
|
35
src/utils/router/breadcrumb.ts
Normal file
35
src/utils/router/breadcrumb.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import type { GlobalMenuOption, GlobalBreadcrumb } from '@/interface';
|
||||
|
||||
/**
|
||||
* 获取面包屑数据
|
||||
* @param activeKey - 当前页面路由的key
|
||||
* @param menus - 菜单数据
|
||||
* @param rootPath - 根路由路径
|
||||
*/
|
||||
export function getBreadcrumbByRouteKey(activeKey: string, menus: GlobalMenuOption[], rootPath: string) {
|
||||
return menus.map(menu => getBreadcrumbItem(activeKey, menu, rootPath)).flat(1);
|
||||
}
|
||||
|
||||
function getBreadcrumbItem(activeKey: string, menu: GlobalMenuOption, rootPath: string) {
|
||||
const list: GlobalBreadcrumb[] = [];
|
||||
if (activeKey.includes(menu.routeName)) {
|
||||
const breadcrumb: GlobalBreadcrumb = {
|
||||
key: menu.routeName,
|
||||
label: menu.label as string,
|
||||
routeName: menu.routeName,
|
||||
disabled: menu.routePath === rootPath,
|
||||
hasChildren: false
|
||||
};
|
||||
if (menu.icon) {
|
||||
breadcrumb.icon = menu.icon;
|
||||
}
|
||||
if (menu.children && menu.children.length) {
|
||||
breadcrumb.hasChildren = true;
|
||||
breadcrumb.children = menu.children
|
||||
.map(item => getBreadcrumbItem(activeKey, item as GlobalMenuOption, rootPath))
|
||||
.flat(1);
|
||||
}
|
||||
list.push(breadcrumb);
|
||||
}
|
||||
return list;
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export * from './helpers';
|
||||
export * from './menu';
|
||||
export * from './breadcrumb';
|
||||
export * from './regexp';
|
||||
|
Loading…
Reference in New Issue
Block a user