244 lines
7.3 KiB
Vue
Executable File
244 lines
7.3 KiB
Vue
Executable File
<template>
|
|
<header
|
|
class="bg-gray-100 h-16 dark:bg-dark-primary border-gray-200 dark:border-dark-primary-800 px-6 grid grid-cols-12 items-center sticky top-0 z-40 gap-4"
|
|
>
|
|
<!-- سمت چپ هدر -->
|
|
<div class="col-span-3 lg:col-span-2 xl:col-span-3 hidden lg:block">
|
|
<template v-if="headerSchema.breadcrumb">
|
|
<Breadcrumb
|
|
:breadcrumbData="[]"
|
|
:tabs="tabs"
|
|
:activeTabId="activeTabModel"
|
|
/>
|
|
</template>
|
|
<template v-if="headerSchema.logo">
|
|
<nuxt-link :to="{ name: 'DashboardBasePage' }">
|
|
<div class="flex items-center gap-3">
|
|
<img :src="useSystemTheme.logo.value" alt="" class="h-9 w-9" />
|
|
<div v-if="useSystemTheme.currentTheme.value" class="flex flex-col">
|
|
<span class="font-bold text-gray-900 dark:text-light-primary">
|
|
{{ useSystemTheme.currentTheme.value.title || "" }}
|
|
</span>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ useSystemTheme.currentTheme.value.subTitle || "" }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</nuxt-link>
|
|
</template>
|
|
</div>
|
|
<div class="col-span-3 lg:col-span-2 xl:col-span-3 lg:hidden">
|
|
<button
|
|
@click="toggleSidebarMenu"
|
|
class="flex items-center justify-center w-8 h-8 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-dark-primary-800 transition-colors duration-200"
|
|
aria-label="باز کردن منو"
|
|
>
|
|
<UIcon name="i-heroicons-bars-3" class="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- وسط هدر -->
|
|
<div
|
|
class="col-span-6 lg:col-span-8 xl:col-span-6 flex justify-center pt-4"
|
|
>
|
|
<TabBar
|
|
:tabs="tabs"
|
|
v-model:active-tab="activeTabModel"
|
|
mode="soft"
|
|
:width="tabBarWidth"
|
|
@tab-change="emit('tab-change', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<!-- راست هدر -->
|
|
<div
|
|
class="col-span-3 lg:col-span-2 xl:col-span-3 flex items-center justify-end gap-3"
|
|
>
|
|
<!-- Dark mode -->
|
|
<UButton
|
|
variant="ghost"
|
|
size="sm"
|
|
color="gray"
|
|
:icon="
|
|
useSystemTheme.isDark.value ? 'i-heroicons-sun' : 'i-heroicons-moon'
|
|
"
|
|
@click="useSystemTheme.toggleDarkMode"
|
|
/>
|
|
|
|
<!-- Language -->
|
|
<UDropdownMenu :items="headerItems.languages" dir="rtl">
|
|
<UButton
|
|
variant="ghost"
|
|
size="sm"
|
|
icon="i-heroicons-language"
|
|
label="فارسی"
|
|
/>
|
|
</UDropdownMenu>
|
|
|
|
<!-- Notifications -->
|
|
<UButton
|
|
variant="ghost"
|
|
size="sm"
|
|
icon="i-heroicons-bell"
|
|
:badge="unreadNotifications"
|
|
/>
|
|
|
|
<!-- User menu -->
|
|
<UDropdownMenu
|
|
v-if="isClient && userAvatar"
|
|
:items="userMenuItems"
|
|
dir="rtl"
|
|
>
|
|
<!-- <UAvatar
|
|
v-if="userInitial"
|
|
:label="userInitial"
|
|
size="sm"
|
|
class="cursor-pointer"
|
|
/> -->
|
|
<!-- <span v-if="userInitial" class="text-white text-lg font-bold">{{
|
|
userInitial
|
|
}}</span>
|
|
<UAvatar v-else :src="userAvatar" size="sm" class="cursor-pointer" /> -->
|
|
|
|
<!-- اگر avatar داشت -->
|
|
<UAvatar :src="userAvatar" size="sm" class="cursor-pointer" />
|
|
|
|
<!-- اگر avatar نداشت -->
|
|
<!-- <UAvatar
|
|
v-else
|
|
:label="userInitial"
|
|
size="sm"
|
|
class="cursor-pointer bg-primary text-white font-bold"
|
|
/> -->
|
|
</UDropdownMenu>
|
|
|
|
<UDropdownMenu v-else-if="isClient" :items="userMenuItems" dir="rtl">
|
|
<span class="text-white text-lg font-bold">{{ userInitial }}</span>
|
|
</UDropdownMenu>
|
|
</div>
|
|
</header>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from "vue";
|
|
import headerItems from "@/json/header/header.json";
|
|
import { composSystemTheme } from "@/composables/composSystemTheme";
|
|
import { useCommonStore } from "@/stores/commonStore";
|
|
import { useAuthStore } from "@/stores/authStore";
|
|
import { useRouter } from "vue-router";
|
|
|
|
const commonStore = useCommonStore();
|
|
const authStore = useAuthStore();
|
|
const router = useRouter();
|
|
/* ---------------- PROPS ---------------- */
|
|
const props = defineProps({
|
|
tabs: { type: Array, required: true, default: [] },
|
|
activeTab: { type: String, default: "" },
|
|
unreadNotifications: { type: Number, default: 0 },
|
|
tabBarWidth: { type: String, default: "40em" },
|
|
headerSchema: { type: Object, default: {} },
|
|
});
|
|
|
|
const emit = defineEmits([
|
|
"update:activeTab",
|
|
"tab-change",
|
|
"user-menu-select",
|
|
]);
|
|
|
|
/* ---------------- ACTIVE TAB ---------------- */
|
|
const activeTabModel = computed({
|
|
get: () => props.activeTab,
|
|
set: (val) => emit("update:activeTab", val),
|
|
});
|
|
|
|
/* ---------------- THEME ---------------- */
|
|
const useSystemTheme = composSystemTheme();
|
|
|
|
/* ---------------- USER MENU HANDLER ---------------- */
|
|
const handleUserMenu = async (action) => {
|
|
if (action.key === "logout") {
|
|
localStorage.clear();
|
|
sessionStorage.clear();
|
|
|
|
const cookies = document.cookie.split(";");
|
|
for (let cookie of cookies) {
|
|
const eqPos = cookie.indexOf("=");
|
|
const name = eqPos > -1 ? cookie.substr(0, eqPos).trim() : cookie.trim();
|
|
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/";
|
|
}
|
|
authStore.userReset();
|
|
navigateTo("/login");
|
|
|
|
await router.push("/login");
|
|
} else if (action.key === "profile") {
|
|
await router.push("/profile");
|
|
} else if (action.key === "settings") {
|
|
await router.push("/settings");
|
|
} else if (action.key === "developer") {
|
|
await router.push("/developer");
|
|
} else if (action.key === "admin") {
|
|
await router.push("/admin");
|
|
} else {
|
|
emit("user-menu-select", action);
|
|
}
|
|
};
|
|
|
|
/* ---------------- USER MENU ITEMS ---------------- */
|
|
const userMenuItems = computed(() =>
|
|
headerItems.userMenu.map((group) =>
|
|
group.map((item) => ({
|
|
...item,
|
|
onSelect: () => handleUserMenu(item),
|
|
})),
|
|
),
|
|
);
|
|
|
|
/* ---------------- AVATAR ---------------- */
|
|
|
|
// const userAvatar = "https://api.dicebear.com/7.x/avataaars/svg?seed=admin";
|
|
const isClient = ref(false);
|
|
onMounted(() => {
|
|
isClient.value = true;
|
|
});
|
|
|
|
const userAvatar = computed(() => {
|
|
if (!process.client) return null;
|
|
|
|
try {
|
|
const user = JSON.parse(localStorage.getItem("user") || "{}");
|
|
const avatar = user?.user_data?.avatar;
|
|
|
|
if (!avatar) return null;
|
|
const BASE_URL = "https://hamfahmi.ir/";
|
|
const url = BASE_URL + `api/media${avatar}`;
|
|
|
|
// console.log("avatar ==> ", url);
|
|
|
|
return url;
|
|
} catch (e) {
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
});
|
|
|
|
const userInitial = computed(() => {
|
|
if (!process.client) return "؟";
|
|
|
|
try {
|
|
const user = JSON.parse(localStorage.getItem("user") || {});
|
|
const firstName = user?.user_data?.first_name?.trim() || "";
|
|
const lastName = user?.user_data?.last_name?.trim() || "";
|
|
|
|
const first = firstName[0] || "";
|
|
const second = lastName[0] || firstName[1] || "";
|
|
|
|
return first + second || "؟";
|
|
} catch (e) {
|
|
return "؟";
|
|
}
|
|
});
|
|
function toggleSidebarMenu() {
|
|
commonStore.isSidebarOpen();
|
|
}
|
|
</script>
|