224 lines
6.0 KiB
Vue
224 lines
6.0 KiB
Vue
<template>
|
|
<!--
|
|
Replacement for legacy TheNavbarVertical.vue / TheNavbarHorizontal.vue.
|
|
Legacy carried a Vuexy/Vuesax toolbar with a theme customizer, search overlay,
|
|
notification dropdown, bookmarks bar and i18n switcher. Of those, only the
|
|
pieces with real backing survive: the sidebar toggle, the page title, and the
|
|
user dropdown with logout. The rest were template demo features.
|
|
-->
|
|
<a-layout-header class="the-navbar">
|
|
<div class="nav-left">
|
|
<a-button type="text" @click="$emit('toggle-sidebar')">
|
|
<MenuUnfoldOutlined v-if="collapsed" />
|
|
<MenuFoldOutlined v-else />
|
|
</a-button>
|
|
<span class="page-title">{{ pageTitle }}</span>
|
|
<a-breadcrumb v-if="breadcrumb" class="breadcrumb">
|
|
<a-breadcrumb-item v-for="(crumb, i) in breadcrumb" :key="i">
|
|
<router-link v-if="crumb.url && !crumb.active" :to="crumb.url">
|
|
{{ crumb.title }}
|
|
</router-link>
|
|
<span v-else>{{ crumb.title }}</span>
|
|
</a-breadcrumb-item>
|
|
</a-breadcrumb>
|
|
</div>
|
|
|
|
<div class="nav-right">
|
|
<a-dropdown trigger="click" placement="bottomRight" :overlay-class-name="'user-dropdown-overlay'">
|
|
<span class="user-chip">
|
|
<a-avatar size="small" style="background-color: #1c2d56">
|
|
{{ initial }}
|
|
</a-avatar>
|
|
<span class="user-name">{{ displayName }}</span>
|
|
</span>
|
|
<template #overlay>
|
|
<div class="user-card">
|
|
<div class="user-card__head">
|
|
<a-avatar size="large" style="background-color: #1c2d56">
|
|
{{ initial }}
|
|
</a-avatar>
|
|
<div class="user-card__id">
|
|
<span class="user-card__name">{{ displayName }}</span>
|
|
<span class="user-card__mobile">{{ auth.user?.mobile }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<a-divider class="user-card__divider" />
|
|
|
|
<a-menu class="user-card__menu">
|
|
<a-menu-item key="my-data" @click="router.push('/my-data')">
|
|
<UserOutlined />
|
|
اطلاعات من
|
|
</a-menu-item>
|
|
<a-menu-item v-if="canSeePermissions" key="permission" @click="router.push('/permission')">
|
|
<SafetyCertificateOutlined />
|
|
دسترسی ها
|
|
</a-menu-item>
|
|
</a-menu>
|
|
|
|
<a-divider class="user-card__divider" />
|
|
|
|
<a-menu class="user-card__menu">
|
|
<a-menu-item key="logout" @click="onLogout">
|
|
<LogoutOutlined />
|
|
خروج
|
|
</a-menu-item>
|
|
</a-menu>
|
|
</div>
|
|
</template>
|
|
</a-dropdown>
|
|
</div>
|
|
</a-layout-header>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import {
|
|
LogoutOutlined,
|
|
MenuFoldOutlined,
|
|
MenuUnfoldOutlined,
|
|
SafetyCertificateOutlined,
|
|
UserOutlined,
|
|
} from '@ant-design/icons-vue';
|
|
import { useAuthStore } from '../../stores/auth';
|
|
import { useUsersStore } from '../../stores/users';
|
|
|
|
interface Crumb {
|
|
title: string;
|
|
url?: string;
|
|
active?: boolean;
|
|
}
|
|
|
|
defineProps<{ collapsed?: boolean }>();
|
|
defineEmits<{ (e: 'toggle-sidebar'): void }>();
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const auth = useAuthStore();
|
|
const users = useUsersStore();
|
|
|
|
const pageTitle = computed(() => (route.meta?.pageTitle as string) ?? '');
|
|
const breadcrumb = computed(() => route.meta?.breadcrumb as Crumb[] | undefined);
|
|
const displayName = computed(() => auth.user?.name ?? auth.user?.fullName ?? 'کاربر');
|
|
const initial = computed(() => displayName.value.trim().charAt(0) || 'ک');
|
|
const canSeePermissions = computed(() => Boolean(users.hasPermission('user.permission')));
|
|
|
|
async function onLogout() {
|
|
const left = await auth.logout();
|
|
if (!left) router.push('/auth/login');
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.the-navbar {
|
|
height: 61px; /* legacy ara/ant.less @layout-header-height */
|
|
background: #fff;
|
|
padding: 0 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
.nav-left,
|
|
.nav-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
.page-title {
|
|
font-size: 16px;
|
|
color: #1c2d56;
|
|
}
|
|
.breadcrumb {
|
|
font-size: 13px;
|
|
}
|
|
.user-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
cursor: pointer;
|
|
}
|
|
</style>
|
|
|
|
<!--
|
|
Unscoped: ant-design-vue's <a-dropdown> teleports its #overlay to <body>, so
|
|
a `scoped` block (which relies on a component-instance data attribute) never
|
|
matches it. Reach is contained instead by :overlay-class-name="'user-dropdown-overlay'"
|
|
above, so every selector below is namespaced under that one class.
|
|
-->
|
|
<style>
|
|
.user-dropdown-overlay .ant-dropdown-menu {
|
|
padding: 0;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card {
|
|
width: 220px;
|
|
padding: 12px;
|
|
background: #fff;
|
|
border-radius: 10px;
|
|
box-shadow: 0 8px 28px -8px rgba(28, 45, 86, 0.28);
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 2px 4px 8px;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__id {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: #1c2d56;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__mobile {
|
|
font-size: 12px;
|
|
color: #8a909c;
|
|
direction: ltr;
|
|
text-align: right;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__divider {
|
|
margin: 4px 0;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__menu.ant-menu {
|
|
border-inline-end: none;
|
|
background: transparent;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__menu .ant-menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
height: 34px;
|
|
line-height: 34px;
|
|
margin: 0;
|
|
padding-inline: 6px !important;
|
|
border-radius: 6px;
|
|
width: 100%;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__menu .ant-menu-item:hover {
|
|
background: #f4f5f6;
|
|
color: #1c2d56;
|
|
}
|
|
|
|
.user-dropdown-overlay .user-card__menu .anticon {
|
|
font-size: 15px;
|
|
color: #8a909c;
|
|
}
|
|
</style>
|