mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
refactor(components): blankLayout引入GlobalContent
This commit is contained in:
14
src/layouts/common/GlobalHeader/components/FullScreen.vue
Normal file
14
src/layouts/common/GlobalHeader/components/FullScreen.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full" tooltip-content="全屏" @click="toggle">
|
||||
<icon-gridicons-fullscreen-exit v-if="isFullscreen" class="text-16px" />
|
||||
<icon-gridicons-fullscreen v-else class="text-16px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFullscreen } from '@vueuse/core';
|
||||
import { HoverContainer } from '@/components';
|
||||
|
||||
const { isFullscreen, toggle } = useFullscreen();
|
||||
</script>
|
||||
<style scoped></style>
|
12
src/layouts/common/GlobalHeader/components/GithubSite.vue
Normal file
12
src/layouts/common/GlobalHeader/components/GithubSite.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<hover-container tooltip-content="github" class="w-40px h-full">
|
||||
<a href="https://github.com/honghuangdc/soybean-admin" target="_blank" class="flex-center">
|
||||
<icon-mdi-github class="text-20px text-[#666]" />
|
||||
</a>
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { HoverContainer } from '@/components';
|
||||
</script>
|
||||
<style scoped></style>
|
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<n-breadcrumb class="px-12px">
|
||||
<template v-for="breadcrumb in breadcrumbList" :key="breadcrumb.key">
|
||||
<n-breadcrumb-item>
|
||||
<n-dropdown v-if="breadcrumb.hasChildren" :options="breadcrumb.children" @select="dropdownSelect">
|
||||
<span>
|
||||
<Icon
|
||||
v-if="theme.crumbsStyle.showIcon && breadcrumb.iconName"
|
||||
:icon="breadcrumb.iconName"
|
||||
class="inline-block mr-4px text-16px"
|
||||
/>
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
</span>
|
||||
</n-dropdown>
|
||||
<template v-else>
|
||||
<Icon
|
||||
v-if="theme.crumbsStyle.showIcon && breadcrumb.iconName"
|
||||
:icon="breadcrumb.iconName"
|
||||
class="inline-block mr-4px text-16px"
|
||||
/>
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
</template>
|
||||
</n-breadcrumb-item>
|
||||
</template>
|
||||
</n-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import type { RouteLocationMatched } from 'vue-router';
|
||||
import { NBreadcrumb, NBreadcrumbItem, NDropdown } from 'naive-ui';
|
||||
import type { DropdownOption } from 'naive-ui';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { routePath } from '@/router';
|
||||
import { useThemeStore } from '@/store';
|
||||
import type { RouteKey } from '@/interface';
|
||||
|
||||
type Breadcrumb = DropdownOption & {
|
||||
key: string;
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
routeName: RouteKey;
|
||||
hasChildren: boolean;
|
||||
iconName?: string;
|
||||
children?: Breadcrumb[];
|
||||
};
|
||||
|
||||
const theme = useThemeStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const breadcrumbList = computed<Breadcrumb[]>(() => generateBreadcrumb());
|
||||
|
||||
function generateBreadcrumb() {
|
||||
const { matched } = route;
|
||||
return recursionBreadcrumb(matched);
|
||||
}
|
||||
|
||||
/** 递归匹配路由获取面包屑数据 */
|
||||
function recursionBreadcrumb(routeMatched: RouteLocationMatched[]) {
|
||||
const list: Breadcrumb[] = [];
|
||||
routeMatched.forEach(item => {
|
||||
if (!item.meta?.isNotMenu) {
|
||||
const routeName = item.name as RouteKey;
|
||||
const breadcrumItem: Breadcrumb = {
|
||||
key: routeName,
|
||||
label: (item.meta?.title as string) || '',
|
||||
disabled: item.path === routePath('root'),
|
||||
routeName,
|
||||
hasChildren: false
|
||||
};
|
||||
if (item.meta?.icon) {
|
||||
breadcrumItem.iconName = item.meta.icon as string;
|
||||
}
|
||||
if (item.children && item.children.length) {
|
||||
breadcrumItem.hasChildren = true;
|
||||
breadcrumItem.children = recursionBreadcrumb(item.children as RouteLocationMatched[]);
|
||||
}
|
||||
list.push(breadcrumItem);
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
function dropdownSelect(optionKey: string) {
|
||||
const key = optionKey as RouteKey;
|
||||
router.push({ name: key });
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
27
src/layouts/common/GlobalHeader/components/HeaderMenu.vue
Normal file
27
src/layouts/common/GlobalHeader/components/HeaderMenu.vue
Normal file
@ -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>
|
15
src/layouts/common/GlobalHeader/components/MenuCollapse.vue
Normal file
15
src/layouts/common/GlobalHeader/components/MenuCollapse.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full" @click="toggleMenu">
|
||||
<icon-line-md-menu-unfold-left v-if="app.menu.collapsed" class="text-16px" />
|
||||
<icon-line-md-menu-fold-left v-else class="text-16px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store';
|
||||
import { HoverContainer } from '@/components';
|
||||
|
||||
const app = useAppStore();
|
||||
const { toggleMenu } = useAppStore();
|
||||
</script>
|
||||
<style scoped></style>
|
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full" tooltip-content="项目配置" placement="bottom-end" @click="openSettingDrawer">
|
||||
<icon-mdi-light-cog class="text-16px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useAppStore } from '@/store';
|
||||
import { HoverContainer } from '@/components';
|
||||
|
||||
const { openSettingDrawer } = useAppStore();
|
||||
</script>
|
||||
<style scoped></style>
|
15
src/layouts/common/GlobalHeader/components/ThemeMode.vue
Normal file
15
src/layouts/common/GlobalHeader/components/ThemeMode.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<hover-container class="w-40px" content-class="hover:text-primary" tooltip-content="主题模式" @click="toggleDarkMode">
|
||||
<icon-mdi-moon-waning-crescent v-if="theme.darkMode" class="text-14px" />
|
||||
<icon-mdi-white-balance-sunny v-else class="text-14px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { HoverContainer } from '@/components';
|
||||
import { useThemeStore } from '@/store';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { toggleDarkMode } = useThemeStore();
|
||||
</script>
|
||||
<style scoped></style>
|
55
src/layouts/common/GlobalHeader/components/UserAvatar.vue
Normal file
55
src/layouts/common/GlobalHeader/components/UserAvatar.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<n-dropdown :options="options" @select="handleDropdown">
|
||||
<hover-container class="px-12px">
|
||||
<img :src="avatar" class="w-32px h-32px" />
|
||||
<span class="pl-8px text-16px font-medium">Soybean</span>
|
||||
</hover-container>
|
||||
</n-dropdown>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { NDropdown, useDialog } from 'naive-ui';
|
||||
import { HoverContainer } from '@/components';
|
||||
import { useRouterPush } from '@/composables';
|
||||
import { iconifyRender, resetAuthStorage } from '@/utils';
|
||||
import avatar from '@/assets/svg/avatar/avatar01.svg';
|
||||
|
||||
type DropdownKey = 'user-center' | 'logout';
|
||||
|
||||
const { toLogin } = useRouterPush();
|
||||
const dialog = useDialog();
|
||||
|
||||
const options = [
|
||||
{
|
||||
label: '用户中心',
|
||||
key: 'user-center',
|
||||
icon: iconifyRender('carbon:user-avatar')
|
||||
},
|
||||
{
|
||||
type: 'divider',
|
||||
key: 'divider'
|
||||
},
|
||||
{
|
||||
label: '退出登录',
|
||||
key: 'logout',
|
||||
icon: iconifyRender('carbon:logout')
|
||||
}
|
||||
];
|
||||
|
||||
function handleDropdown(optionKey: string) {
|
||||
const key = optionKey as DropdownKey;
|
||||
if (key === 'logout') {
|
||||
dialog.info({
|
||||
title: '提示',
|
||||
content: '您确定要退出登录吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
resetAuthStorage();
|
||||
toLogin('pwd-login', 'current');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
19
src/layouts/common/GlobalHeader/components/index.ts
Normal file
19
src/layouts/common/GlobalHeader/components/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import HeaderMenu from './HeaderMenu.vue';
|
||||
import GlobalBreadcrumb from './GlobalBreadcrumb.vue';
|
||||
import UserAvatar from './UserAvatar.vue';
|
||||
import MenuCollapse from './MenuCollapse.vue';
|
||||
import ThemeMode from './ThemeMode.vue';
|
||||
import FullScreen from './FullScreen.vue';
|
||||
import SettingDrawerButton from './SettingDrawerButton.vue';
|
||||
import GithubSite from './GithubSite.vue';
|
||||
|
||||
export {
|
||||
HeaderMenu,
|
||||
GlobalBreadcrumb,
|
||||
UserAvatar,
|
||||
MenuCollapse,
|
||||
ThemeMode,
|
||||
FullScreen,
|
||||
SettingDrawerButton,
|
||||
GithubSite
|
||||
};
|
Reference in New Issue
Block a user