feat: complete UI/UX improvements, vuesax icons, media server, and section management
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ROLE_LABELS } from "@imen/shared";
|
||||
import { computed, h, ref, watch } from "vue";
|
||||
import { computed, h, onMounted, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { api } from "@/api/http";
|
||||
import VuesaxIcon from "@/components/VuesaxIcon.vue";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { SITE_URL } from "@/utils/format";
|
||||
@@ -10,13 +11,28 @@ const auth = useAuthStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const drawer = ref(false);
|
||||
const unreadCount = ref(0);
|
||||
|
||||
async function fetchUnread() {
|
||||
try {
|
||||
const res = await api<{ total: number }>("/admin/messages", {
|
||||
query: { status: "NEW", pageSize: 1 },
|
||||
});
|
||||
unreadCount.value = res.total;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchUnread);
|
||||
watch(() => route.fullPath, fetchUnread);
|
||||
|
||||
/** Icon badge box + Vuesax glyph (keeps pre-swap menu structure) */
|
||||
const menuIcon = (name: string) => () =>
|
||||
h("span", { class: "admin-menu__icon" }, [h(VuesaxIcon, { name, size: 16 })]);
|
||||
h("span", { class: "admin-menu__icon" }, [h(VuesaxIcon, { name, size: 20 })]);
|
||||
|
||||
const items = computed(() => [
|
||||
{ key: "dashboard", icon: menuIcon("element-3"), label: "داشبورد" },
|
||||
{ key: "dashboard", icon: menuIcon("chart-square"), label: "داشبورد" },
|
||||
{ key: "products", icon: menuIcon("bag-2"), label: "محصولات" },
|
||||
{ key: "categories", icon: menuIcon("category"), label: "دستهبندیها" },
|
||||
{ key: "sections", icon: menuIcon("row-vertical"), label: "بخشهای سایت" },
|
||||
@@ -58,16 +74,35 @@ const selected = computed(() => {
|
||||
const pageTitle = computed(() => PAGE_TITLES[String(route.name ?? "")] ?? "پنل مدیریت");
|
||||
const userInitial = computed(() => (auth.user?.name?.trim().charAt(0) || "ک").slice(0, 1));
|
||||
|
||||
const isChildRoute = computed(() => {
|
||||
const name = String(route.name ?? "");
|
||||
return ["product-new", "product-edit", "category-new", "category-edit", "section-edit"].includes(name);
|
||||
});
|
||||
|
||||
const parentBack = computed(() => {
|
||||
const name = String(route.name ?? "");
|
||||
if (name.startsWith("product")) return { name: "products", label: "محصولات" };
|
||||
if (name.startsWith("category")) return { name: "categories", label: "دستهبندیها" };
|
||||
if (name.startsWith("section")) return { name: "sections", label: "بخشهای سایت" };
|
||||
return { name: "dashboard", label: "داشبورد" };
|
||||
});
|
||||
|
||||
const go = ({ key }: { key: string | number }) => {
|
||||
drawer.value = false;
|
||||
void router.push({ name: String(key) });
|
||||
};
|
||||
|
||||
const navigateTo = (name: string) => {
|
||||
drawer.value = false;
|
||||
void router.push({ name });
|
||||
};
|
||||
|
||||
watch(() => route.fullPath, () => (drawer.value = false));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-layout class="admin-shell">
|
||||
<!-- Desktop Sider (lg and up) -->
|
||||
<a-layout-sider
|
||||
:width="248"
|
||||
breakpoint="lg"
|
||||
@@ -97,42 +132,250 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
</div>
|
||||
</a-layout-sider>
|
||||
|
||||
<!-- Mobile App Hub Drawer (Rich App Grid) -->
|
||||
<a-drawer
|
||||
v-model:open="drawer"
|
||||
placement="right"
|
||||
:width="280"
|
||||
:width="320"
|
||||
:closable="false"
|
||||
class="admin-drawer"
|
||||
:body-style="{ padding: 0, background: '#ffffff' }"
|
||||
class="mobile-hub-drawer"
|
||||
:body-style="{ padding: 0, background: '#f8fafc' }"
|
||||
>
|
||||
<div class="admin-sider__brand">
|
||||
<span class="admin-sider__logo">
|
||||
<img src="/logo.png" alt="" class="size-7 object-contain" />
|
||||
</span>
|
||||
<div class="leading-tight">
|
||||
<div class="text-[14px] font-bold text-[#0f172a]">ایمن پیشگامان</div>
|
||||
<div class="text-[10px] font-medium text-[#94a3b8]">پنل مدیریت</div>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Hub User Profile Header -->
|
||||
<div class="p-4 bg-white border-b border-slate-200/80">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<span class="size-8 rounded-lg bg-orange-50 border border-orange-100 flex items-center justify-center">
|
||||
<img src="/logo.png" alt="" class="size-5 object-contain" />
|
||||
</span>
|
||||
<div>
|
||||
<div class="text-sm font-bold text-slate-800">ایمن پیشگامان</div>
|
||||
<div class="text-[10px] text-slate-400">اپلیکیشن مدیریت</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="size-8 rounded-full flex items-center justify-center text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition"
|
||||
@click="drawer = false"
|
||||
>
|
||||
<VuesaxIcon name="close-circle" :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex items-center gap-3 p-2.5 rounded-xl bg-slate-50 border border-slate-100 cursor-pointer active:scale-[0.99] transition"
|
||||
@click="navigateTo('profile')"
|
||||
>
|
||||
<span class="size-10 rounded-full bg-linear-to-br from-[#ff5a00] to-[#ff8a3d] text-white flex items-center justify-center font-bold text-sm shadow-xs">
|
||||
{{ userInitial }}
|
||||
</span>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-xs font-bold text-slate-800 truncate">{{ auth.user?.name }}</div>
|
||||
<div class="flex items-center gap-2 mt-0.5">
|
||||
<span class="text-[10px] text-orange-600 font-medium">
|
||||
{{ auth.user && ROLE_LABELS[auth.user.role] }}
|
||||
</span>
|
||||
<span class="text-[10px] text-slate-400 font-mono" dir="ltr">{{ auth.user?.phone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<VuesaxIcon name="arrow-left" :size="14" class="text-slate-400" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hub Apps Grid Content -->
|
||||
<div class="flex-1 overflow-y-auto p-4 space-y-4">
|
||||
<div>
|
||||
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">بخشها و کاتالوگ</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-orange-200 active:bg-orange-50/40 transition"
|
||||
:class="{ '!border-orange-500 !bg-orange-50/30 font-bold text-orange-600': route.name === 'dashboard' }"
|
||||
@click="navigateTo('dashboard')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-orange-50 text-orange-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="chart-square" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">داشبورد</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">نمای کلی آمار</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-orange-200 active:bg-orange-50/40 transition"
|
||||
:class="{ '!border-orange-500 !bg-orange-50/30 font-bold text-orange-600': selected.includes('products') }"
|
||||
@click="navigateTo('products')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-orange-50 text-orange-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="bag-2" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">محصولات</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">کاتالوگ و موجودی</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-blue-200 active:bg-blue-50/40 transition"
|
||||
:class="{ '!border-blue-500 !bg-blue-50/30 font-bold text-blue-600': selected.includes('categories') }"
|
||||
@click="navigateTo('categories')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-blue-50 text-blue-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="category" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">دستهبندیها</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">گروههای کالا</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-amber-200 active:bg-amber-50/40 transition relative"
|
||||
:class="{ '!border-amber-500 !bg-amber-50/30 font-bold text-amber-600': route.name === 'messages' }"
|
||||
@click="navigateTo('messages')"
|
||||
>
|
||||
<span v-if="unreadCount > 0" class="absolute top-2 end-2 size-2.5 rounded-full bg-orange-600 ring-2 ring-white animate-pulse" />
|
||||
<div class="size-9 rounded-lg bg-amber-50 text-amber-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="sms" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">پیامهای تماس</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">درخواستهای مشاوره</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">محتوا و وبسایت</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-purple-200 active:bg-purple-50/40 transition"
|
||||
:class="{ '!border-purple-500 !bg-purple-50/30 font-bold text-purple-600': route.name === 'media' }"
|
||||
@click="navigateTo('media')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-purple-50 text-purple-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="gallery" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">کتابخانه رسانه</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">تصاویر WebP</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="flex flex-col items-center justify-center p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-emerald-200 active:bg-emerald-50/40 transition"
|
||||
:class="{ '!border-emerald-500 !bg-emerald-50/30 font-bold text-emerald-600': selected.includes('sections') }"
|
||||
@click="navigateTo('sections')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-emerald-50 text-emerald-600 flex items-center justify-center mb-1.5">
|
||||
<VuesaxIcon name="row-vertical" :size="18" />
|
||||
</div>
|
||||
<span class="text-xs text-slate-700">بخشهای سایت</span>
|
||||
<span class="text-[9px] text-slate-400 mt-0.5">تنظیمات صفحه اصلی</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="auth.isAdmin">
|
||||
<div class="text-[11px] font-bold text-slate-400 mb-2.5 px-1">مدیریت سیستم</div>
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-3 p-3 rounded-xl bg-white border border-slate-200/70 shadow-2xs hover:border-slate-300 active:bg-slate-50 transition text-right"
|
||||
:class="{ '!border-orange-500 font-bold': route.name === 'users' }"
|
||||
@click="navigateTo('users')"
|
||||
>
|
||||
<div class="size-9 rounded-lg bg-slate-100 text-slate-600 flex items-center justify-center shrink-0">
|
||||
<VuesaxIcon name="profile-2user" :size="18" />
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-xs font-bold text-slate-800">مدیریت کاربران پنل</div>
|
||||
<div class="text-[10px] text-slate-400">حسابها و سطح دسترسیها</div>
|
||||
</div>
|
||||
<VuesaxIcon name="arrow-left" :size="14" class="text-slate-400" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hub Footer System Actions -->
|
||||
<div class="p-3 bg-white border-t border-slate-200/80 space-y-2">
|
||||
<a
|
||||
:href="SITE_URL"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="flex items-center justify-between p-2.5 rounded-lg text-slate-600 hover:bg-slate-50 text-xs font-semibold transition"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<VuesaxIcon name="export" :size="16" class="text-slate-400" />
|
||||
مشاهده وبسایت اصلی
|
||||
</span>
|
||||
<VuesaxIcon name="arrow-left" :size="12" class="text-slate-300" />
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="w-full flex items-center justify-center gap-2 p-2.5 rounded-lg text-rose-600 hover:bg-rose-50 text-xs font-semibold transition"
|
||||
@click="auth.logout()"
|
||||
>
|
||||
<VuesaxIcon name="logout" :size="16" />
|
||||
خروج از حساب کاربری
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-sider__nav">
|
||||
<div class="admin-sider__label">منوی اصلی</div>
|
||||
<a-menu
|
||||
class="admin-menu"
|
||||
mode="inline"
|
||||
:selected-keys="selected"
|
||||
:items="items"
|
||||
@click="go"
|
||||
/>
|
||||
</div>
|
||||
</a-drawer>
|
||||
|
||||
<a-layout>
|
||||
<a-layout-header class="admin-header">
|
||||
<div class="admin-header__start">
|
||||
<a-button class="admin-header__menu-btn lg:!hidden" @click="drawer = true">
|
||||
<template #icon><VuesaxIcon name="menu" :size="18" /></template>
|
||||
</a-button>
|
||||
<!-- Dedicated Mobile App Bar (Mobile Only) -->
|
||||
<header class="mobile-app-bar lg:!hidden">
|
||||
<div v-if="isChildRoute" class="flex items-center justify-between w-full">
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-app-bar__back-btn"
|
||||
@click="router.push(parentBack)"
|
||||
>
|
||||
<VuesaxIcon name="arrow-right" :size="20" />
|
||||
<span class="text-xs font-bold text-slate-700">بازگشت به {{ parentBack.label }}</span>
|
||||
</button>
|
||||
<div class="text-xs font-bold text-slate-800 truncate max-w-[140px]">
|
||||
{{ pageTitle }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex items-center justify-between w-full">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<span class="size-8 rounded-lg bg-orange-50 border border-orange-100 flex items-center justify-center">
|
||||
<img src="/logo.png" alt="" class="size-5 object-contain" />
|
||||
</span>
|
||||
<div class="leading-none">
|
||||
<div class="text-xs font-extrabold text-slate-800">ایمن پیشگامان</div>
|
||||
<div class="text-[10px] font-medium text-orange-600 mt-0.5">{{ pageTitle }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1.5">
|
||||
<a
|
||||
:href="SITE_URL"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="size-8 rounded-lg flex items-center justify-center text-slate-500 hover:text-orange-600 hover:bg-orange-50 transition"
|
||||
title="مشاهده سایت"
|
||||
>
|
||||
<VuesaxIcon name="export" :size="18" />
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="size-8 rounded-lg bg-linear-to-br from-[#ff5a00] to-[#ff8a3d] text-white flex items-center justify-center font-bold text-xs shadow-xs"
|
||||
@click="router.push({ name: 'profile' })"
|
||||
>
|
||||
{{ userInitial }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Desktop Header (Desktop Only) -->
|
||||
<a-layout-header class="admin-header !hidden lg:!flex">
|
||||
<div class="admin-header__start">
|
||||
<div class="admin-header__crumb">
|
||||
<span class="admin-header__crumb-kicker">پنل مدیریت</span>
|
||||
<span class="admin-header__crumb-sep" aria-hidden="true">/</span>
|
||||
@@ -179,13 +422,69 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
</div>
|
||||
</a-layout-header>
|
||||
|
||||
<a-layout-content class="admin-content">
|
||||
<!-- Content Viewport -->
|
||||
<a-layout-content class="admin-content" :class="{ 'admin-content--has-bottom-nav': !isChildRoute }">
|
||||
<div class="admin-page">
|
||||
<RouterView v-slot="{ Component, route: r }">
|
||||
<component :is="Component" :key="r.path" />
|
||||
</RouterView>
|
||||
</div>
|
||||
</a-layout-content>
|
||||
|
||||
<!-- Purpose-Built Mobile Bottom Navigation Bar -->
|
||||
<nav v-if="!isChildRoute" class="mobile-nav-bar lg:!hidden">
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-nav-item"
|
||||
:class="{ 'mobile-nav-item--active': route.name === 'dashboard' }"
|
||||
@click="navigateTo('dashboard')"
|
||||
>
|
||||
<div class="mobile-nav-item__icon">
|
||||
<VuesaxIcon name="chart-square" :size="20" />
|
||||
</div>
|
||||
<span class="mobile-nav-item__label">داشبورد</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-nav-item"
|
||||
:class="{ 'mobile-nav-item--active': selected.includes('products') }"
|
||||
@click="navigateTo('products')"
|
||||
>
|
||||
<div class="mobile-nav-item__icon">
|
||||
<VuesaxIcon name="bag-2" :size="20" />
|
||||
</div>
|
||||
<span class="mobile-nav-item__label">محصولات</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-nav-item"
|
||||
:class="{ 'mobile-nav-item--active': route.name === 'messages' }"
|
||||
@click="navigateTo('messages')"
|
||||
>
|
||||
<div class="mobile-nav-item__icon relative">
|
||||
<VuesaxIcon name="sms" :size="20" />
|
||||
<span
|
||||
v-if="unreadCount > 0"
|
||||
class="absolute -top-1 -end-1 size-2 rounded-full bg-orange-600 ring-2 ring-white"
|
||||
/>
|
||||
</div>
|
||||
<span class="mobile-nav-item__label">پیامها</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="mobile-nav-item"
|
||||
:class="{ 'mobile-nav-item--active': drawer || ['categories', 'sections', 'media', 'users', 'profile'].includes(String(route.name ?? '')) }"
|
||||
@click="drawer = true"
|
||||
>
|
||||
<div class="mobile-nav-item__icon">
|
||||
<VuesaxIcon name="category" :size="20" />
|
||||
</div>
|
||||
<span class="mobile-nav-item__label">ابزارها</span>
|
||||
</button>
|
||||
</nav>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
</template>
|
||||
@@ -247,14 +546,22 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.03);
|
||||
}
|
||||
|
||||
.admin-page > :deep(*) {
|
||||
.admin-page > :deep(.page-header) {
|
||||
flex: 0 0 auto !important;
|
||||
overflow: visible !important;
|
||||
padding: 16px 20px 14px !important;
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.admin-page > :deep(*:not(.page-header)) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Pages that don't use AdminPage still scroll inside the panel */
|
||||
.admin-page > :deep(*:not(.ap)) {
|
||||
.admin-page > :deep(*:not(.ap):not(.page-header)) {
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
@@ -276,8 +583,9 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
|
||||
.admin-header__menu-btn {
|
||||
border-radius: 10px !important;
|
||||
border-color: #e2e8f0 !important;
|
||||
border: none !important;
|
||||
color: #475569 !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.admin-header__crumb {
|
||||
@@ -325,26 +633,21 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
height: 36px;
|
||||
padding-inline: 14px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 999px;
|
||||
background: #ffffff;
|
||||
padding-inline: 8px;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
color 0.18s ease,
|
||||
border-color 0.18s ease,
|
||||
background 0.18s ease,
|
||||
box-shadow 0.18s ease;
|
||||
transition: color 0.18s ease;
|
||||
}
|
||||
|
||||
.admin-header__site:hover {
|
||||
color: #e04f00;
|
||||
border-color: #ffd0b5;
|
||||
background: #fff7f2;
|
||||
box-shadow: 0 2px 8px rgba(255, 90, 0, 0.08);
|
||||
color: #ff5a00;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.admin-header__divider {
|
||||
@@ -362,7 +665,7 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
padding-block: 4px;
|
||||
padding-inline: 4px 10px;
|
||||
border: 1px solid #e8ecf2;
|
||||
border-radius: 999px;
|
||||
border-radius: var(--ant-border-radius, 8px);
|
||||
background: #f8fafc;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
@@ -385,7 +688,7 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 999px;
|
||||
border-radius: calc(var(--ant-border-radius, 8px) - 2px);
|
||||
background: linear-gradient(135deg, #ff5a00, #ff8a3d);
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
@@ -498,14 +801,14 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
.admin-menu :deep(.ant-menu-item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px !important;
|
||||
height: 48px !important;
|
||||
margin: 0 0 6px !important;
|
||||
padding-inline: 12px !important;
|
||||
padding-inline: 10px 14px !important;
|
||||
border: 1px solid transparent !important;
|
||||
border-radius: 12px !important;
|
||||
color: #475569 !important;
|
||||
font-weight: 500;
|
||||
line-height: 44px !important;
|
||||
line-height: 48px !important;
|
||||
transition:
|
||||
background 0.2s ease,
|
||||
color 0.2s ease,
|
||||
@@ -516,11 +819,13 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
.admin-menu :deep(.ant-menu-item-icon),
|
||||
.admin-menu :deep(.ant-menu-item .anticon) {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
margin-inline-end: 0 !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
min-width: 36px !important;
|
||||
min-height: 36px !important;
|
||||
margin-inline-end: 12px !important;
|
||||
font-size: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
@@ -528,12 +833,14 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
|
||||
.admin-menu :deep(.admin-menu__icon) {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin-inline-end: 10px;
|
||||
border-radius: 9px;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 36px !important;
|
||||
height: 36px !important;
|
||||
min-width: 36px !important;
|
||||
min-height: 36px !important;
|
||||
flex-shrink: 0 !important;
|
||||
border-radius: 10px !important;
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
transition:
|
||||
@@ -543,11 +850,22 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
}
|
||||
|
||||
.admin-menu :deep(.admin-menu__icon .vx-icon) {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
min-width: 20px !important;
|
||||
min-height: 20px !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
color: inherit;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.admin-menu :deep(.admin-menu__icon svg) {
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.admin-menu :deep(.ant-menu-item:hover) {
|
||||
@@ -556,9 +874,9 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
border-color: #eef1f6 !important;
|
||||
}
|
||||
|
||||
.admin-menu :deep(.ant-menu-item:hover .admin-menu__icon) {
|
||||
background: #e8eef7;
|
||||
color: #475569;
|
||||
.admin-menu :deep(.ant-menu-item:hover:not(.ant-menu-item-selected) .admin-menu__icon) {
|
||||
background: #e8eef7 !important;
|
||||
color: #475569 !important;
|
||||
}
|
||||
|
||||
.admin-menu :deep(.ant-menu-item-selected) {
|
||||
@@ -572,10 +890,133 @@ watch(() => route.fullPath, () => (drawer.value = false));
|
||||
.admin-menu :deep(.ant-menu-item-selected .admin-menu__icon) {
|
||||
background: #ff5a00 !important;
|
||||
color: #ffffff !important;
|
||||
box-shadow: 0 4px 10px rgba(255, 90, 0, 0.32);
|
||||
box-shadow: 0 4px 12px rgba(255, 90, 0, 0.35) !important;
|
||||
}
|
||||
|
||||
.admin-menu :deep(.ant-menu-item-selected::after) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------
|
||||
* Mobile App Experience Styles (Viewport < 1024px)
|
||||
* ------------------------------------------------------------- */
|
||||
.mobile-app-bar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 52px;
|
||||
padding-inline: 14px;
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid #eef1f6;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.02);
|
||||
}
|
||||
|
||||
.mobile-app-bar__back-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 8px;
|
||||
margin-inline-start: -6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #334155;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
|
||||
.mobile-app-bar__back-btn:active {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.mobile-nav-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 40;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 60px;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0px);
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
backdrop-filter: blur(16px);
|
||||
border-top: 1px solid #e2e8f0;
|
||||
box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.mobile-nav-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding-block: 4px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.mobile-nav-item__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 24px;
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.mobile-nav-item__label {
|
||||
margin-top: 2px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mobile-nav-item--active {
|
||||
color: #ff5a00;
|
||||
}
|
||||
|
||||
.mobile-nav-item--active .mobile-nav-item__icon {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
.admin-content {
|
||||
padding: 0 !important;
|
||||
background: #f8fafc !important;
|
||||
overflow-y: auto !important;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.admin-content--has-bottom-nav {
|
||||
padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)) !important;
|
||||
}
|
||||
|
||||
.admin-page {
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.admin-page > :deep(.page-header) {
|
||||
padding: 12px 14px !important;
|
||||
border-bottom: 1px solid #eef1f6 !important;
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
.admin-page > :deep(*:not(.ap):not(.page-header)) {
|
||||
padding: 12px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user