Files
vue3-element-admin/src/layouts/components/LayoutToolbar.vue

348 lines
8.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div :class="['navbar-actions', navbarActionsClass]">
<!-- 桌面端工具项 -->
<template v-if="isDesktop">
<!-- 搜索 -->
<div class="navbar-actions__item">
<CommandPalette />
</div>
<!-- 全屏 -->
<div class="navbar-actions__item">
<Fullscreen />
</div>
<!-- 布局大小 -->
<div class="navbar-actions__item">
<SizeSelect />
</div>
<!-- 语言选择 -->
<div class="navbar-actions__item">
<LangSelect />
</div>
<!-- 通知 -->
<div class="navbar-actions__item">
<NoticeDropdown />
</div>
<!-- 租户选择如果启用多租户-->
<div v-if="showTenantSelect" class="navbar-actions__item">
<TenantSwitcher @change="handleTenantChange" />
</div>
</template>
<!-- 用户菜单 -->
<div class="navbar-actions__item">
<el-dropdown trigger="click">
<div class="user-profile">
<div style="width: 28px; height: 28px; overflow: hidden; border-radius: 50%">
<img
:src="userStore.userInfo.avatar"
class="user-profile__avatar"
style="width: 100%; height: 100%; object-fit: cover; object-position: center"
/>
</div>
<span class="user-profile__name">{{ userStore.userInfo.username }}</span>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handleProfileClick">
{{ t("navbar.profile") }}
</el-dropdown-item>
<el-dropdown-item divided @click="logout">
{{ t("navbar.logout") }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- 系统设置 -->
<div v-if="defaults.showSettings" class="navbar-actions__item" @click="handleSettingsClick">
<div class="i-svg:setting" />
</div>
</div>
</template>
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useRoute, useRouter } from "vue-router";
import { defaults } from "@/settings";
import { DeviceEnum, SidebarColor, ThemeMode, LayoutMode } from "@/enums/settings";
import { useAppStore, useSettingsStore, useUserStore } from "@/store";
// 导入子组件
import CommandPalette from "@/components/CommandPalette/index.vue";
import Fullscreen from "@/components/Fullscreen/index.vue";
import SizeSelect from "@/components/SizeSelect/index.vue";
import LangSelect from "@/components/LangSelect/index.vue";
import NoticeDropdown from "@/components/NoticeDropdown/index.vue";
import TenantSwitcher from "@/components/TenantSwitcher/index.vue";
import { useTenantStoreHook } from "@/store/modules/tenant";
const { t } = useI18n();
const appStore = useAppStore();
const settingStore = useSettingsStore();
const userStore = useUserStore();
const tenantStore = useTenantStoreHook();
const route = useRoute();
const router = useRouter();
// 是否为桌面设备
const isDesktop = computed(() => appStore.device === DeviceEnum.DESKTOP);
const isPlatformUser = computed(() => {
return (userStore.userInfo?.tenantScope || "").toUpperCase() === "PLATFORM";
});
// 是否显示租户选择(仅平台用户可显式切换租户)
const showTenantSelect = computed(() => {
if (!isPlatformUser.value) {
return false;
}
if (tenantStore.tenantList.length <= 1) {
return false;
}
return true;
});
function handleTenantChange(tenantId: number) {
tenantStore
.switchTenant(tenantId)
.then(() => {
ElMessage.success("切换租户成功");
window.location.reload();
})
.catch((error: any) => {
ElMessage.error(error?.message || "切换租户失败");
});
}
/**
* 打开个人中心页面
*/
function handleProfileClick() {
router.push({ name: "Profile" });
}
// 根据主题和侧边栏配色方案选择样式类
const navbarActionsClass = computed(() => {
const { theme, sidebarColorScheme, layout } = settingStore;
// 暗黑主题下,所有布局都使用白色文字
if (theme === ThemeMode.DARK) {
return "navbar-actions--white-text";
}
// 明亮主题下
if (theme === ThemeMode.LIGHT) {
// 顶部布局和混合布局的顶部区域:
// - 如果侧边栏是经典蓝色,使用白色文字
// - 如果侧边栏是极简白色,使用深色文字
if (layout === LayoutMode.TOP || layout === LayoutMode.MIX) {
if (sidebarColorScheme === SidebarColor.CLASSIC_BLUE) {
return "navbar-actions--white-text";
} else {
return "navbar-actions--dark-text";
}
}
}
return "navbar-actions--dark-text";
});
/**
* 退出登录
*/
function logout() {
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
lockScroll: false,
}).then(() => {
userStore.logout().then(() => {
router.push(`/login?redirect=${route.fullPath}`);
});
});
}
/**
* 打开系统设置页面
*/
function handleSettingsClick() {
settingStore.settingsVisible = true;
}
</script>
<style lang="scss" scoped>
.navbar-actions {
display: flex;
align-items: center;
min-height: 44px;
&__item {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-width: 44px; /* 增加最小点击区域到44px符合人机交互标准 */
height: 44px;
padding: 0 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
// 确保子元素居中
> * {
display: flex;
align-items: center;
justify-content: center;
}
// 确保 Element Plus 组件可以正常工作
:deep(.el-dropdown),
:deep(.el-tooltip) {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 44px;
}
:deep(.i-svg\:language) {
flex-shrink: 0;
width: 18px;
height: 18px;
font-size: 18px;
line-height: 18px;
background-size: 18px 18px;
}
// 图标样式
:deep([class^="i-svg:"]) {
font-size: 18px;
line-height: 1;
color: var(--el-text-color-regular);
transition: color 0.3s;
}
&:hover {
background: rgba(0, 0, 0, 0.04);
:deep([class^="i-svg:"]) {
color: var(--el-color-primary);
}
}
}
.user-profile {
display: flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 8px;
&__avatar {
flex-shrink: 0;
width: 28px;
height: 28px;
border-radius: 50%;
}
&__name {
margin-left: 8px;
color: var(--el-text-color-regular);
white-space: nowrap;
transition: color 0.3s;
}
}
}
// 白色文字样式(用于深色背景:暗黑主题、顶部布局、混合布局等)
.navbar-actions--white-text {
.navbar-actions__item {
:deep([class^="i-svg:"]) {
color: rgba(255, 255, 255, 0.85);
}
&:hover {
background: rgba(255, 255, 255, 0.1);
:deep([class^="i-svg:"]) {
color: #fff;
}
}
}
.user-profile__name {
color: rgba(255, 255, 255, 0.85);
}
// 租户选择器在白色文字模式下的样式
::v-deep(.tenant-switcher__trigger) {
color: rgba(255, 255, 255, 0.85);
}
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
color: rgba(255, 255, 255, 0.85);
}
::v-deep(.tenant-switcher__trigger:hover) {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
color: #fff;
}
}
// 深色文字样式(用于浅色背景:明亮主题下的左侧布局等)
.navbar-actions--dark-text {
.navbar-actions__item {
:deep([class^="i-svg:"]) {
color: var(--el-text-color-regular) !important;
}
&:hover {
background: rgba(0, 0, 0, 0.04);
:deep([class^="i-svg:"]) {
color: var(--el-color-primary) !important;
}
}
}
.user-profile__name {
color: var(--el-text-color-regular) !important;
}
// 租户选择器在深色文字模式下的样式
::v-deep(.tenant-switcher__trigger) {
color: var(--el-text-color-regular) !important;
}
::v-deep(.tenant-switcher__trigger .tenant-switcher__icon) {
color: var(--el-text-color-regular) !important;
}
::v-deep(.tenant-switcher__trigger:hover) {
color: var(--el-color-primary) !important;
background: rgba(0, 0, 0, 0.04);
}
::v-deep(.tenant-switcher__trigger:hover .tenant-switcher__icon) {
color: var(--el-color-primary) !important;
}
}
// 确保下拉菜单中的图标不受影响
::v-deep(.el-dropdown-menu) {
[class^="i-svg:"] {
color: var(--el-text-color-regular) !important;
&:hover {
color: var(--el-color-primary) !important;
}
}
}
</style>