Files
workspace/code/fms/fms-vue/src/layouts/components/AppToolbar.vue
T
2026-07-16 21:55:51 +08:00

181 lines
4.8 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.
<script setup>
import { computed, h, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { Bell, ChevronDown, Languages, LogOut, Settings, UserRound } from "@lucide/vue";
import { useAppStore } from "../../stores/app";
import { useAuthStore } from "../../stores/auth";
const route = useRoute();
const router = useRouter();
const appStore = useAppStore();
const authStore = useAuthStore();
const profileOpen = ref(false);
const logoutOpen = ref(false);
const menuIcon = (icon) => () => h(icon, { size: 16, strokeWidth: 1.8 });
const breadcrumbItems = computed(() => {
const items = [];
if (route.path === "/dashboard") return [{ label: "工作台" }];
if (route.meta.section) items.push({ label: route.meta.section });
if (route.meta.title) items.push({ label: route.meta.title });
return items;
});
const languageMenu = computed(() => ({
items: [
{ key: "zh-CN", label: "简体中文" },
{ key: "en", label: "English" },
],
onClick: ({ key }) => appStore.setLocale(key),
selectable: true,
selectedKeys: [appStore.locale],
}));
const userMenu = {
items: [
{ key: "profile", icon: menuIcon(UserRound), label: "个人资料" },
{ key: "settings", icon: menuIcon(Settings), label: "账户设置" },
{ type: "divider" },
{ key: "logout", icon: menuIcon(LogOut), label: "退出登录", danger: true },
],
onClick: handleUserMenu,
};
const displayName = computed(() => authStore.user?.name || authStore.user?.id || "当前用户");
watch(
() => appStore.locale,
(locale) => document.documentElement.setAttribute("lang", locale),
{ immediate: true },
);
function handleUserMenu({ key }) {
if (key === "profile" || key === "settings") profileOpen.value = true;
if (key === "logout") logoutOpen.value = true;
}
function confirmLogout() {
authStore.clearSession();
logoutOpen.value = false;
router.replace("/login");
}
</script>
<template>
<header class="top-toolbar">
<a-breadcrumb class="toolbar-breadcrumb" separator=">">
<a-breadcrumb-item v-for="item in breadcrumbItems" :key="item.label">
{{ item.label }}
</a-breadcrumb-item>
</a-breadcrumb>
<div class="toolbar-actions">
<a-tooltip title="切换语言" placement="bottom">
<a-dropdown :menu="languageMenu" :trigger="['click']" placement="bottomRight">
<a-button type="text" class="toolbar-action" aria-label="切换语言">
<template #icon><Languages /></template>
</a-button>
</a-dropdown>
</a-tooltip>
<a-tooltip title="通知提醒" placement="bottom">
<a-button type="text" class="toolbar-action" aria-label="通知提醒">
<a-badge dot><Bell /></a-badge>
</a-button>
</a-tooltip>
<a-dropdown :menu="userMenu" :trigger="['click']" placement="bottomRight">
<button type="button" class="toolbar-user" aria-label="用户菜单">
<a-avatar :size="24"><UserRound /></a-avatar>
<span>{{ displayName }}</span>
<ChevronDown class="toolbar-user__arrow" />
</button>
</a-dropdown>
</div>
</header>
<a-modal v-model:open="profileOpen" title="个人资料" :footer="null" width="420px">
<a-descriptions :column="1" bordered size="small">
<a-descriptions-item label="用户ID">{{ authStore.user?.id || "--" }}</a-descriptions-item>
<a-descriptions-item label="姓名">{{ authStore.user?.name || "--" }}</a-descriptions-item>
<a-descriptions-item label="机构码">{{ authStore.orgId || "--" }}</a-descriptions-item>
</a-descriptions>
</a-modal>
<a-modal
v-model:open="logoutOpen"
title="退出登录"
ok-text="确认退出"
cancel-text="取消"
@ok="confirmLogout"
>
确认退出当前账号吗?
</a-modal>
</template>
<style lang="scss" scoped>
.top-toolbar {
z-index: 16;
display: flex;
flex: none;
height: 44px;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 0 8px 0 12px;
border-bottom: 1px solid var(--fms-border);
background: var(--fms-surface);
}
.toolbar-breadcrumb {
min-width: 0;
overflow: hidden;
font-size: 13px;
text-overflow: ellipsis;
white-space: nowrap;
}
.toolbar-actions {
display: flex;
flex: none;
height: 100%;
align-items: center;
gap: 2px;
}
.toolbar-action {
width: 32px;
height: 32px;
padding-inline: 0;
border-radius: 4px;
color: var(--fms-text-secondary);
font-size: 17px;
}
.toolbar-user {
display: flex;
height: 32px;
align-items: center;
gap: 6px;
margin-left: 4px;
padding: 0 6px 0 4px;
border: 0;
border-radius: 4px;
color: var(--fms-text);
background: transparent;
cursor: pointer;
font-size: 13px;
white-space: nowrap;
&:hover {
background: var(--fms-hover);
}
&__arrow {
width: 12px;
height: 12px;
color: var(--fms-text-secondary);
}
}
</style>