refactor: ♻️ 导航栏组件代码重构优化
This commit is contained in:
11
src/components/Fullscreen/index.vue
Normal file
11
src/components/Fullscreen/index.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<div @click="toggle">
|
||||||
|
<svg-icon :icon-class="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { isFullscreen, toggle } = useFullscreen();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
|
<!-- 汉堡按钮组件:展开/收缩菜单 -->
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="px-[15px] flex items-center justify-center color-[var(--el-text-color-regular)]"
|
class="px-[15px] flex items-center justify-center color-[var(--el-text-color-regular)]"
|
||||||
@click="toggleClick"
|
@click="toggleClick"
|
||||||
>
|
>
|
||||||
<svg-icon
|
<svg-icon
|
||||||
class="hamburger"
|
|
||||||
:class="{ 'is-active': isActive }"
|
|
||||||
icon-class="collapse"
|
icon-class="collapse"
|
||||||
|
:class="{ hamburger: true, 'is-active': isActive }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
defineProps({
|
||||||
isActive: {
|
isActive: {
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dropdown trigger="click" @command="handleSizeChange">
|
<!-- 布局大小 -->
|
||||||
<div>
|
<el-tooltip
|
||||||
<svg-icon icon-class="size" />
|
:content="$t('sizeSelect.tooltip')"
|
||||||
</div>
|
effect="dark"
|
||||||
<template #dropdown>
|
placement="bottom"
|
||||||
<el-dropdown-menu>
|
>
|
||||||
<el-dropdown-item
|
<el-dropdown trigger="click" @command="handleSizeChange">
|
||||||
v-for="item of sizeOptions"
|
<div>
|
||||||
:key="item.value"
|
<svg-icon icon-class="size" />
|
||||||
:disabled="appStore.size == item.value"
|
</div>
|
||||||
:command="item.value"
|
<template #dropdown>
|
||||||
>
|
<el-dropdown-menu>
|
||||||
{{ item.label }}
|
<el-dropdown-item
|
||||||
</el-dropdown-item>
|
v-for="item of sizeOptions"
|
||||||
</el-dropdown-menu>
|
:key="item.value"
|
||||||
</template>
|
:disabled="appStore.size == item.value"
|
||||||
</el-dropdown>
|
:command="item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex">
|
|
||||||
<template v-if="!isMobile">
|
|
||||||
<!--搜索 -->
|
|
||||||
<menu-search />
|
|
||||||
<!--全屏 -->
|
|
||||||
<div class="nav-action-item" @click="toggle">
|
|
||||||
<svg-icon
|
|
||||||
:icon-class="isFullscreen ? 'fullscreen-exit' : 'fullscreen'"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 布局大小 -->
|
|
||||||
<el-tooltip
|
|
||||||
:content="$t('sizeSelect.tooltip')"
|
|
||||||
effect="dark"
|
|
||||||
placement="bottom"
|
|
||||||
>
|
|
||||||
<size-select class="nav-action-item" />
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<!-- 语言选择 -->
|
|
||||||
<lang-select class="nav-action-item" />
|
|
||||||
|
|
||||||
<!-- 消息通知 -->
|
|
||||||
<notice class="nav-action-item" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 用户头像 -->
|
|
||||||
<el-dropdown class="nav-action-item" trigger="click">
|
|
||||||
<div class="flex-center h100% p10px">
|
|
||||||
<img
|
|
||||||
:src="userStore.userInfo.avatar"
|
|
||||||
class="rounded-full mr-10px w24px h24px"
|
|
||||||
/>
|
|
||||||
<span>{{ userStore.userInfo.username }}</span>
|
|
||||||
</div>
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item @click="handleOpenUserProfile">
|
|
||||||
{{ $t("navbar.profile") }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
href="https://gitee.com/youlaiorg/vue3-element-admin"
|
|
||||||
>
|
|
||||||
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
|
|
||||||
</a>
|
|
||||||
<a target="_blank" href="https://juejin.cn/post/7228990409909108793">
|
|
||||||
<el-dropdown-item>{{ $t("navbar.document") }}</el-dropdown-item>
|
|
||||||
</a>
|
|
||||||
<el-dropdown-item divided @click="logout">
|
|
||||||
{{ $t("navbar.logout") }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
|
|
||||||
<!-- 设置 -->
|
|
||||||
<template v-if="defaultSettings.showSettings">
|
|
||||||
<div class="nav-action-item" @click="settingStore.settingsVisible = true">
|
|
||||||
<svg-icon icon-class="setting" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import defaultSettings from "@/settings";
|
|
||||||
import { DeviceEnum } from "@/enums/DeviceEnum";
|
|
||||||
|
|
||||||
import {
|
|
||||||
useAppStore,
|
|
||||||
useTagsViewStore,
|
|
||||||
useUserStore,
|
|
||||||
useSettingsStore,
|
|
||||||
} from "@/store";
|
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const tagsViewStore = useTagsViewStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
const settingStore = useSettingsStore();
|
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
const router = useRouter();
|
|
||||||
const { isFullscreen, toggle } = useFullscreen();
|
|
||||||
const isMobile = computed(() => appStore.device === DeviceEnum.MOBILE);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打开个人中心页面
|
|
||||||
*/
|
|
||||||
function handleOpenUserProfile() {
|
|
||||||
router.push({ name: "Profile" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注销登出
|
|
||||||
*/
|
|
||||||
function logout() {
|
|
||||||
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
|
|
||||||
confirmButtonText: "确定",
|
|
||||||
cancelButtonText: "取消",
|
|
||||||
type: "warning",
|
|
||||||
lockScroll: false,
|
|
||||||
}).then(() => {
|
|
||||||
userStore
|
|
||||||
.logout()
|
|
||||||
.then(() => {
|
|
||||||
tagsViewStore.delAllViews();
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
router.push(`/login?redirect=${route.fullPath}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.nav-action-item {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 40px;
|
|
||||||
height: $navbar-height;
|
|
||||||
line-height: $navbar-height;
|
|
||||||
color: var(--el-text-color);
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgb(0 0 0 / 10%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-divider--horizontal) {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .nav-action-item:hover {
|
|
||||||
background: rgb(255 255 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-top .nav-action-item,
|
|
||||||
.layout-mix .nav-action-item {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
81
src/layout/components/NavBar/components/NavbarRight.vue
Normal file
81
src/layout/components/NavBar/components/NavbarRight.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div class="navbar__right">
|
||||||
|
<!-- 手机设备(窄屏)不显示 -->
|
||||||
|
<template v-if="!isMobile">
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<MenuSearch />
|
||||||
|
|
||||||
|
<!-- 全屏 -->
|
||||||
|
<Fullscreen />
|
||||||
|
|
||||||
|
<!-- 布局大小 -->
|
||||||
|
<SizeSelect />
|
||||||
|
|
||||||
|
<!-- 语言选择 -->
|
||||||
|
<LangSelect />
|
||||||
|
|
||||||
|
<!-- 消息通知 -->
|
||||||
|
<Notification />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 用户头像(个人中心、注销登录等) -->
|
||||||
|
<UserProfile />
|
||||||
|
|
||||||
|
<!-- 设置面板 -->
|
||||||
|
<div
|
||||||
|
v-if="defaultSettings.showSettings"
|
||||||
|
@click="settingStore.settingsVisible = true"
|
||||||
|
>
|
||||||
|
<SvgIcon icon-class="setting" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import defaultSettings from "@/settings";
|
||||||
|
import { DeviceEnum } from "@/enums/DeviceEnum";
|
||||||
|
|
||||||
|
import { useAppStore, useSettingsStore } from "@/store";
|
||||||
|
|
||||||
|
import UserProfile from "./UserProfile.vue";
|
||||||
|
import Notification from "./Notification.vue";
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const settingStore = useSettingsStore();
|
||||||
|
|
||||||
|
const isMobile = computed(() => appStore.device === DeviceEnum.MOBILE);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.navbar__right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 40px;
|
||||||
|
height: $navbar-height;
|
||||||
|
line-height: $navbar-height;
|
||||||
|
color: var(--el-text-color);
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgb(0 0 0 / 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-divider--horizontal) {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .navbar__right > *:hover {
|
||||||
|
background: rgb(255 255 255 / 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-top .navbar__right > *,
|
||||||
|
.layout-mix .navbar__right > * {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
74
src/layout/components/NavBar/components/UserProfile.vue
Normal file
74
src/layout/components/NavBar/components/UserProfile.vue
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<el-dropdown trigger="click">
|
||||||
|
<div class="flex-center h100% p13px">
|
||||||
|
<img
|
||||||
|
:src="userStore.userInfo.avatar"
|
||||||
|
class="rounded-full mr-10px w24px h24px"
|
||||||
|
/>
|
||||||
|
<span>{{ userStore.userInfo.username }}</span>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="handleOpenUserProfile">
|
||||||
|
{{ $t("navbar.profile") }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://gitee.com/youlaiorg/vue3-element-admin"
|
||||||
|
>
|
||||||
|
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
|
||||||
|
</a>
|
||||||
|
<a target="_blank" href="https://juejin.cn/post/7228990409909108793">
|
||||||
|
<el-dropdown-item>{{ $t("navbar.document") }}</el-dropdown-item>
|
||||||
|
</a>
|
||||||
|
<el-dropdown-item divided @click="logout">
|
||||||
|
{{ $t("navbar.logout") }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
name: "UserProfile",
|
||||||
|
});
|
||||||
|
|
||||||
|
import { useTagsViewStore, useUserStore } from "@/store";
|
||||||
|
|
||||||
|
const tagsViewStore = useTagsViewStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开个人中心页面
|
||||||
|
*/
|
||||||
|
function handleOpenUserProfile() {
|
||||||
|
router.push({ name: "Profile" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销登出
|
||||||
|
*/
|
||||||
|
function logout() {
|
||||||
|
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
lockScroll: false,
|
||||||
|
}).then(() => {
|
||||||
|
userStore
|
||||||
|
.logout()
|
||||||
|
.then(() => {
|
||||||
|
tagsViewStore.delAllViews();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
router.push(`/login?redirect=${route.fullPath}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar-container">
|
<div class="navbar">
|
||||||
<!-- 导航栏面包屑 -->
|
<div class="navbar__left">
|
||||||
<div class="flex">
|
<!-- 展开/收缩菜单 -->
|
||||||
<hamburger
|
<Hamburger :is-active="isSidebarOpened" @toggle-click="toggleSideBar" />
|
||||||
:is-active="appStore.sidebar.opened"
|
<!-- 面包屑 -->
|
||||||
@toggle-click="toggleSideBar"
|
|
||||||
/>
|
|
||||||
<breadcrumb />
|
<breadcrumb />
|
||||||
</div>
|
</div>
|
||||||
<!-- 导航栏右侧 -->
|
<!-- 导航栏右侧 -->
|
||||||
<NavbarAction />
|
<NavbarRight />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -18,16 +16,25 @@ import { useAppStore } from "@/store";
|
|||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
// 侧边栏是否打开
|
||||||
|
const isSidebarOpened = computed(() => appStore.sidebar.opened);
|
||||||
|
|
||||||
|
// 展开/收缩菜单
|
||||||
function toggleSideBar() {
|
function toggleSideBar() {
|
||||||
appStore.toggleSidebar();
|
appStore.toggleSidebar();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.navbar-container {
|
.navbar {
|
||||||
@apply flex-x-between;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
height: $navbar-height;
|
height: $navbar-height;
|
||||||
background: var(--el-bg-color);
|
background: var(--el-bg-color);
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ 'has-logo': sidebarLogo }">
|
<div :class="{ 'has-logo': sidebarLogo }">
|
||||||
<!-- 混合布局的顶部的Sidebar -->
|
<!-- 混合布局顶部 -->
|
||||||
<div v-if="layout == LayoutEnum.MIX" class="flex w-full">
|
<div v-if="isMixLayout" class="flex w-full">
|
||||||
<SidebarLogo v-if="sidebarLogo" :collapse="!appStore.sidebar.opened" />
|
<SidebarLogo v-if="sidebarLogo" :collapse="isSidebarCollapsed" />
|
||||||
<SidebarMixTopMenu class="flex-1" />
|
<SidebarMixTopMenu class="flex-1" />
|
||||||
<NavbarAction />
|
<NavbarRight />
|
||||||
</div>
|
</div>
|
||||||
<!-- 左侧和顶部布局的Sidebar -->
|
|
||||||
|
<!-- 顶部布局顶部 || 左侧布局左侧 -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<SidebarLogo v-if="sidebarLogo" :collapse="!appStore.sidebar.opened" />
|
<SidebarLogo v-if="sidebarLogo" :collapse="isSidebarCollapsed" />
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<SidebarMenu :menu-list="permissionStore.routes" base-path="" />
|
<SidebarMenu :menu-list="permissionStore.routes" base-path="" />
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<NavbarAction v-if="layout === LayoutEnum.TOP" />
|
|
||||||
|
<!-- 顶部布局导航 -->
|
||||||
|
<NavbarRight v-if="isTopLayout" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useSettingsStore, usePermissionStore, useAppStore } from "@/store";
|
|
||||||
import { LayoutEnum } from "@/enums/LayoutEnum";
|
import { LayoutEnum } from "@/enums/LayoutEnum";
|
||||||
|
import { useSettingsStore, usePermissionStore, useAppStore } from "@/store";
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
@@ -27,6 +30,10 @@ const permissionStore = usePermissionStore();
|
|||||||
|
|
||||||
const sidebarLogo = computed(() => settingsStore.sidebarLogo);
|
const sidebarLogo = computed(() => settingsStore.sidebarLogo);
|
||||||
const layout = computed(() => settingsStore.layout);
|
const layout = computed(() => settingsStore.layout);
|
||||||
|
|
||||||
|
const isMixLayout = computed(() => layout.value === LayoutEnum.MIX);
|
||||||
|
const isTopLayout = computed(() => layout.value === LayoutEnum.TOP);
|
||||||
|
const isSidebarCollapsed = computed(() => !appStore.sidebar.opened);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
<!-- 左侧和顶部布局 -->
|
<!-- 左侧和顶部布局 -->
|
||||||
<div v-else :class="{ hasTagsView: showTagsView }" class="main-container">
|
<div v-else :class="{ hasTagsView: showTagsView }" class="main-container">
|
||||||
<div :class="{ 'fixed-header': fixedHeader }">
|
<div :class="{ 'fixed-header': fixedHeader }">
|
||||||
<NavBar v-if="layout === LayoutEnum.LEFT" />
|
<Navbar v-if="layout === LayoutEnum.LEFT" />
|
||||||
<TagsView v-if="showTagsView" />
|
<TagsView v-if="showTagsView" />
|
||||||
</div>
|
</div>
|
||||||
<AppMain />
|
<AppMain />
|
||||||
|
|||||||
Reference in New Issue
Block a user