284 lines
8.2 KiB
Vue
Executable File
284 lines
8.2 KiB
Vue
Executable File
<!-- app/components/Sidebar.vue -->
|
|
<template>
|
|
<UDashboardSidebar
|
|
side="left"
|
|
:collapsed="collapsed"
|
|
collapsible
|
|
:ui="sidebarUI"
|
|
class="sidebar-gradient"
|
|
:style="{ width: collapsed ? '2%' : '8%' }"
|
|
dir="rtl"
|
|
@update:collapsed="onCollapse"
|
|
>
|
|
<template #header>
|
|
<nuxt-link
|
|
:to="{ name: 'DashboardBasePage' }"
|
|
class="w-full flex justify-center items-center"
|
|
>
|
|
<div class="flex items-center gap-3">
|
|
<img :src="logoSrc" alt="" class="h-9 w-9" />
|
|
</div>
|
|
</nuxt-link>
|
|
</template>
|
|
|
|
<!-- <div
|
|
v-if="useSystemTheme?.currentTheme?.value && !collapsed"
|
|
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> -->
|
|
<template #default>
|
|
<div class="">
|
|
|
|
<UNavigationMenu
|
|
dir="rtl"
|
|
orientation="vertical"
|
|
:items="getSideBarSchema()?.topMenu || []"
|
|
:collapsed="collapsed"
|
|
:ui="navigationUI"
|
|
class="w-full"
|
|
/>
|
|
</div>
|
|
|
|
<div class="mt-auto pb-2 flex flex-col gap-2">
|
|
<UNavigationMenu
|
|
dir="rtl"
|
|
:collapsed="collapsed"
|
|
:items="getSideBarSchema()?.bottomMenu || []"
|
|
orientation="vertical"
|
|
:ui="navigationUI"
|
|
/>
|
|
<UTooltip
|
|
arrow
|
|
dir="rtl"
|
|
:text="user?.first_name + ' ' + user?.last_name"
|
|
>
|
|
<UButton
|
|
:avatar="
|
|
user?.avatar
|
|
? { src: user?.avatar }
|
|
: {
|
|
label:
|
|
(user?.first_name?.[0] || '') +
|
|
(user?.last_name?.[0] || ''),
|
|
}
|
|
"
|
|
:label="
|
|
collapsed ? '' : user?.first_name + ' ' + user?.last_name || ''
|
|
"
|
|
color="neutral"
|
|
variant="ghost"
|
|
class="w-full"
|
|
:block="collapsed"
|
|
/></UTooltip>
|
|
</div>
|
|
</template>
|
|
|
|
<template #footer>
|
|
<div>
|
|
<!-- دکمههای باز/بسته کردن سایدبار -->
|
|
<div class="flex justify-center items-center gap-2 mt-4">
|
|
<!-- دکمه باز کردن -->
|
|
<UButton
|
|
v-if="collapsed"
|
|
icon="stash-chevron-double-left-solid"
|
|
color="neutral"
|
|
variant="ghost"
|
|
size="sm"
|
|
class="transition-all duration-300"
|
|
@click="toggleSidebar"
|
|
/>
|
|
<!-- دکمه بستن -->
|
|
<UButton
|
|
v-else
|
|
icon="stash-chevron-double-right-solid"
|
|
color="neutral"
|
|
variant="ghost"
|
|
size="sm"
|
|
class="transition-all duration-300 absolute left-0 -translate-y-1/2"
|
|
@click="toggleSidebar"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</UDashboardSidebar>
|
|
<USlideover
|
|
v-model:open="sidebarOpenComputed"
|
|
side="right"
|
|
overlay
|
|
class="lg:hidden"
|
|
:dismissible="true"
|
|
dir="rtl"
|
|
:title="
|
|
useSystemTheme?.currentTheme?.value?.title +
|
|
' - ' +
|
|
useSystemTheme?.currentTheme?.value?.subTitle || ''
|
|
"
|
|
>
|
|
<template #body>
|
|
<div class="p-4 flex flex-col h-full">
|
|
<UNavigationMenu
|
|
orientation="vertical"
|
|
:items="filteredSidebar.topMenu"
|
|
@click="onMenuSelect"
|
|
dir="rtl"
|
|
/>
|
|
|
|
<div class="mt-auto">
|
|
<UNavigationMenu
|
|
orientation="vertical"
|
|
:items="filteredSidebar.bottomMenu"
|
|
@click="onMenuSelect"
|
|
dir="rtl"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #footer="{ collapsed }">
|
|
<div>
|
|
<UButton
|
|
:avatar="
|
|
user?.avatar
|
|
? { src: user?.avatar }
|
|
: {
|
|
label:
|
|
(user?.first_name?.[0] || '') +
|
|
(user?.last_name?.[0] || ''),
|
|
}
|
|
"
|
|
:label="
|
|
collapsed ? '' : user?.first_name + ' ' + user?.last_name || ''
|
|
"
|
|
color="neutral"
|
|
variant="ghost"
|
|
class="w-full"
|
|
:block="collapsed"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</USlideover>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted } from "vue";
|
|
import { composSystemTheme } from "@/composables/composSystemTheme";
|
|
import { storeToRefs } from "pinia";
|
|
import { useCommonStore } from "@/stores/commonStore";
|
|
|
|
const commonStore = useCommonStore();
|
|
const { sidebarOpen } = storeToRefs(commonStore);
|
|
|
|
// proxy امن برای v-model:open
|
|
const sidebarOpenComputed = computed({
|
|
get: () => sidebarOpen.value,
|
|
set: (v) => (commonStore.sidebarOpen = v),
|
|
});
|
|
const filteredSidebar = computed(
|
|
() => props.sidebarItems || { topMenu: [], bottomMenu: [] },
|
|
);
|
|
|
|
function onMenuSelect() {
|
|
// وقتی کاربر آیتم منو رو زد، اسلایداور رو ببند
|
|
commonStore.closeSidebar();
|
|
}
|
|
const useSystemTheme = composSystemTheme();
|
|
|
|
const props = defineProps({
|
|
sidebarItems: {
|
|
type: Object,
|
|
required: true,
|
|
default: () => ({
|
|
topMenu: [],
|
|
bottomMenu: [],
|
|
}),
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["update:collapsed"]);
|
|
|
|
const logoSrc = ref("");
|
|
const collapsed = ref(false);
|
|
const user = ref({ first_name: "", last_name: "", avatar: "" });
|
|
const onCollapse = (value) => {
|
|
collapsed.value = value;
|
|
emit("update:collapsed", value);
|
|
};
|
|
function getSideBarSchema() {
|
|
const config = useRuntimeConfig();
|
|
const NUXT_PUBLIC_IS_DEVLOP_MODE = Number(
|
|
config.public?.NUXT_PUBLIC_IS_DEVLOP_MODE ?? 1,
|
|
);
|
|
|
|
const filterMenu = (menu) => {
|
|
if (NUXT_PUBLIC_IS_DEVLOP_MODE === 1) return menu || [];
|
|
return (
|
|
menu?.filter(
|
|
(el) => el.develop === undefined || Number(el.develop) === 0,
|
|
) || []
|
|
);
|
|
};
|
|
|
|
const topMenuFiltered = filterMenu(props.sidebarItems?.topMenu);
|
|
const bottomMenuFiltered = filterMenu(props.sidebarItems?.bottomMenu);
|
|
|
|
return {
|
|
topMenu: topMenuFiltered,
|
|
bottomMenu: bottomMenuFiltered,
|
|
};
|
|
}
|
|
|
|
// تابع برای باز/بسته کردن نرم سایدبار
|
|
const toggleSidebar = () => {
|
|
collapsed.value = !collapsed?.value;
|
|
emit("update:collapsed", collapsed?.value);
|
|
};
|
|
|
|
const sidebarUI = {
|
|
wrapper:
|
|
"z-30 h-[calc(100vh-64px)] top-16 rounded-r-2xl border-r border-gray-200/50 dark:border-dark-primary-800/50 backdrop-blur-sm transition-all duration-300",
|
|
base: "backdrop-blur-sm transition-all duration-300 ease-out",
|
|
header: "border-b border-gray-200/30 dark:border-dark-primary-800/30",
|
|
footer: "border-t border-gray-200/30 dark:border-dark-primary-800/30",
|
|
};
|
|
|
|
const navigationUI = {
|
|
base: "space-y-1",
|
|
wrapper: (c) => (c ? "items-center" : ""),
|
|
inactive:
|
|
"text-dark-primary-700 dark:text-gray-300 hover:bg-gradient-to-r hover:from-blue-50/50 hover:to-transparent dark:hover:from-blue-900/20 hover:text-blue-600 dark:hover:text-blue-400",
|
|
active:
|
|
"bg-gradient-to-r from-blue-100 to-blue-50/30 dark:from-blue-900/30 dark:to-blue-900/10 text-blue-600 dark:text-blue-400 font-semibold shadow-sm",
|
|
icon: { base: "transition-transform duration-300", active: "scale-110" },
|
|
};
|
|
|
|
onMounted(() => {
|
|
useSystemTheme.applyTheme();
|
|
setTimeout(() => {
|
|
logoSrc.value = useSystemTheme.logo.value;
|
|
}, 300);
|
|
const stored = localStorage.getItem("user");
|
|
if (stored) {
|
|
try {
|
|
user.value = JSON.parse(stored);
|
|
} catch (e) {
|
|
console.error("خطا در خواندن کاربر از localStorage:", e);
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.sidebar-gradient {
|
|
background: linear-gradient(to bottom, #ffffff, #f9fafb);
|
|
transition: all 0.3s ease-in-out;
|
|
}
|
|
.dark .sidebar-gradient {
|
|
background: linear-gradient(to bottom, #111827, #1f2937);
|
|
}
|
|
</style>
|