better dashboard style
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
<template>
|
||||
<template v-if="isMobile">
|
||||
<button
|
||||
type="button"
|
||||
class="dashboard-operations-trigger"
|
||||
:class="{ 'is-open': menuOpen }"
|
||||
@click="menuOpen = true"
|
||||
>
|
||||
<BaseIcon icon="vuesax-linear:setting-2" :size="18" />
|
||||
<span>عملیات</span>
|
||||
<BaseIcon
|
||||
icon="vuesax-linear:arrow-down"
|
||||
:size="14"
|
||||
class="dashboard-operations-trigger__chevron"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<BasicActionSheet
|
||||
v-model:visible="menuOpen"
|
||||
title="عملیات سریع"
|
||||
compact
|
||||
drawer-height="auto"
|
||||
:show-button="false"
|
||||
>
|
||||
<div class="dash-quick-sheet">
|
||||
<header class="dash-quick-sheet__header">
|
||||
<h3 class="dash-quick-sheet__title">عملیات سریع</h3>
|
||||
<p class="dash-quick-sheet__subtitle">دسترسی سریع به ابزارهای میز کار</p>
|
||||
</header>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="dash-quick-sheet__featured"
|
||||
@click="runAction('wallet')"
|
||||
>
|
||||
<span class="dash-quick-sheet__featured-icon">
|
||||
<BaseIcon icon="vuesax-linear:empty-wallet" :size="22" />
|
||||
</span>
|
||||
<span class="dash-quick-sheet__featured-text">
|
||||
<span class="dash-quick-sheet__featured-label">شارژ / دشارژ کیف پول</span>
|
||||
<span class="dash-quick-sheet__featured-hint">مدیریت موجودی ریالی و طلایی</span>
|
||||
</span>
|
||||
<BaseIcon icon="vuesax-linear:arrow-left-2" :size="16" class="dash-quick-sheet__featured-arrow" />
|
||||
</button>
|
||||
|
||||
<section v-for="group in mobileMenuGroups" :key="group.title" class="dash-quick-sheet__section">
|
||||
<p class="dash-quick-sheet__section-title">{{ group.title }}</p>
|
||||
<div class="dash-quick-sheet__list">
|
||||
<button
|
||||
v-for="item in group.items"
|
||||
:key="item.key"
|
||||
type="button"
|
||||
class="dash-quick-sheet__item"
|
||||
:class="[
|
||||
item.tone ? `dash-quick-sheet__item--${item.tone}` : '',
|
||||
{ 'dash-quick-sheet__item--nav': item.nav },
|
||||
]"
|
||||
:disabled="item.disabled"
|
||||
@click="runAction(item.key)"
|
||||
>
|
||||
<span class="dash-quick-sheet__badge" :class="`dash-quick-sheet__badge--${item.tone ?? 'slate'}`">
|
||||
<BaseIcon :icon="item.icon" :size="18" />
|
||||
</span>
|
||||
<span class="dash-quick-sheet__item-body">
|
||||
<span class="dash-quick-sheet__item-label">{{ item.label }}</span>
|
||||
<span v-if="item.hint" class="dash-quick-sheet__item-hint">{{ item.hint }}</span>
|
||||
</span>
|
||||
<span v-if="item.badge" class="dash-quick-sheet__count">{{ item.badge }}</span>
|
||||
<BaseIcon
|
||||
v-if="item.nav"
|
||||
icon="vuesax-linear:arrow-left-2"
|
||||
:size="15"
|
||||
class="dash-quick-sheet__item-arrow"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</BasicActionSheet>
|
||||
</template>
|
||||
|
||||
<div v-else class="flex max-w-full flex-wrap items-center justify-end gap-1.5 sm:justify-start">
|
||||
<DashboardQuickActionBtn
|
||||
v-for="item in actionItems"
|
||||
:key="item.key"
|
||||
:icon="item.icon"
|
||||
:label="item.shortLabel"
|
||||
:variant="item.variant"
|
||||
:loading="item.loading"
|
||||
:disabled="item.disabled"
|
||||
:to="item.to"
|
||||
@click="item.onClick?.()"
|
||||
>
|
||||
<template v-if="item.key === 'restore'">
|
||||
{{ item.shortLabel }}
|
||||
<span v-if="productSnapshotCount" class="mr-0.5 opacity-80">({{ productSnapshotCount }})</span>
|
||||
</template>
|
||||
</DashboardQuickActionBtn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||
import DashboardQuickActionBtn from '@/pages/dashboard/components/DashboardQuickActionBtn.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
|
||||
type ActionVariant = 'default' | 'accent' | 'danger' | 'primary'
|
||||
type ItemTone = 'slate' | 'emerald' | 'rose' | 'amber' | 'sky' | 'violet'
|
||||
|
||||
type ActionItem = {
|
||||
key: string
|
||||
label: string
|
||||
shortLabel: string
|
||||
icon: string
|
||||
variant?: ActionVariant
|
||||
danger?: boolean
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
to?: RouteLocationRaw
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
type MobileMenuItem = {
|
||||
key: string
|
||||
label: string
|
||||
hint?: string
|
||||
badge?: string
|
||||
icon: string
|
||||
tone?: ItemTone
|
||||
disabled?: boolean
|
||||
nav?: boolean
|
||||
}
|
||||
|
||||
type MobileMenuGroup = {
|
||||
title: string
|
||||
items: MobileMenuItem[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
quickActionLoading: boolean
|
||||
hasProductSnapshot: boolean
|
||||
productSnapshotCount: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
wallet: []
|
||||
deactivateAll: []
|
||||
restoreProducts: []
|
||||
closeAllBuy: []
|
||||
closeAllSell: []
|
||||
syncPrices: []
|
||||
syncShow: []
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const menuOpen = ref(false)
|
||||
|
||||
const actionItems = computed<ActionItem[]>(() => [
|
||||
{
|
||||
key: 'wallet',
|
||||
label: 'شارژ / دشارژ کیف پول',
|
||||
shortLabel: 'کیف پول',
|
||||
icon: 'vuesax-linear:empty-wallet',
|
||||
variant: 'accent',
|
||||
onClick: () => emit('wallet'),
|
||||
},
|
||||
{
|
||||
key: 'deactivate-all',
|
||||
label: 'بستن همه محصولات',
|
||||
shortLabel: 'بستن همه محصولات',
|
||||
icon: 'vuesax-linear:close-circle',
|
||||
variant: 'danger',
|
||||
danger: true,
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('deactivateAll'),
|
||||
},
|
||||
{
|
||||
key: 'restore',
|
||||
label: 'بازگردانی محصولات قبلی',
|
||||
shortLabel: 'بازگردانی محصولات',
|
||||
icon: 'vuesax-linear:refresh-circle',
|
||||
disabled: !props.hasProductSnapshot,
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('restoreProducts'),
|
||||
},
|
||||
{
|
||||
key: 'close-buy',
|
||||
label: 'بستن خرید همه محصولات',
|
||||
shortLabel: 'بستن خرید',
|
||||
icon: 'vuesax-linear:money-receive',
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('closeAllBuy'),
|
||||
},
|
||||
{
|
||||
key: 'close-sell',
|
||||
label: 'بستن فروش همه محصولات',
|
||||
shortLabel: 'بستن فروش',
|
||||
icon: 'vuesax-linear:money-send',
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('closeAllSell'),
|
||||
},
|
||||
{
|
||||
key: 'sync-prices',
|
||||
label: 'همگامسازی قیمت محصولات',
|
||||
shortLabel: 'همگامسازی قیمت',
|
||||
icon: 'vuesax-linear:coin',
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('syncPrices'),
|
||||
},
|
||||
{
|
||||
key: 'sync-show',
|
||||
label: 'بروزرسانی نمایش محصولات',
|
||||
shortLabel: 'بروز نمایش',
|
||||
icon: 'vuesax-linear:eye',
|
||||
loading: props.quickActionLoading,
|
||||
onClick: () => emit('syncShow'),
|
||||
},
|
||||
{
|
||||
key: 'products',
|
||||
label: 'مدیریت محصولات',
|
||||
shortLabel: 'محصولات',
|
||||
icon: 'vuesax-linear:box',
|
||||
to: { name: 'productsMain_limitedOrder' },
|
||||
},
|
||||
{
|
||||
key: 'orders',
|
||||
label: 'لیست سفارشات',
|
||||
shortLabel: 'سفارشات',
|
||||
icon: 'vuesax-linear:receipt-2',
|
||||
variant: 'primary',
|
||||
to: { name: 'ordersMain' },
|
||||
},
|
||||
])
|
||||
|
||||
const mobileMenuGroups = computed<MobileMenuGroup[]>(() => [
|
||||
{
|
||||
title: 'محصولات',
|
||||
items: [
|
||||
{
|
||||
key: 'deactivate-all',
|
||||
label: 'بستن همه محصولات',
|
||||
icon: 'vuesax-linear:close-circle',
|
||||
tone: 'rose',
|
||||
disabled: props.quickActionLoading,
|
||||
},
|
||||
{
|
||||
key: 'restore',
|
||||
label: 'بازگردانی محصولات',
|
||||
hint: props.hasProductSnapshot ? 'بازگشت به وضعیت قبلی' : 'ابتدا محصولات را ببندید',
|
||||
icon: 'vuesax-linear:refresh-circle',
|
||||
tone: 'sky',
|
||||
badge: props.productSnapshotCount ? String(props.productSnapshotCount) : undefined,
|
||||
disabled: !props.hasProductSnapshot || props.quickActionLoading,
|
||||
},
|
||||
{
|
||||
key: 'close-buy',
|
||||
label: 'بستن خرید همه',
|
||||
icon: 'vuesax-linear:money-receive',
|
||||
tone: 'slate',
|
||||
disabled: props.quickActionLoading,
|
||||
},
|
||||
{
|
||||
key: 'close-sell',
|
||||
label: 'بستن فروش همه',
|
||||
icon: 'vuesax-linear:money-send',
|
||||
tone: 'slate',
|
||||
disabled: props.quickActionLoading,
|
||||
},
|
||||
{
|
||||
key: 'sync-prices',
|
||||
label: 'همگامسازی قیمت',
|
||||
icon: 'vuesax-linear:coin',
|
||||
tone: 'amber',
|
||||
disabled: props.quickActionLoading,
|
||||
},
|
||||
{
|
||||
key: 'sync-show',
|
||||
label: 'بروزرسانی نمایش',
|
||||
icon: 'vuesax-linear:eye',
|
||||
tone: 'violet',
|
||||
disabled: props.quickActionLoading,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'صفحات',
|
||||
items: [
|
||||
{
|
||||
key: 'products',
|
||||
label: 'مدیریت محصولات',
|
||||
icon: 'vuesax-linear:box',
|
||||
tone: 'slate',
|
||||
nav: true,
|
||||
},
|
||||
{
|
||||
key: 'orders',
|
||||
label: 'لیست سفارشات',
|
||||
icon: 'vuesax-linear:receipt-2',
|
||||
tone: 'amber',
|
||||
nav: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const mobileActions: Record<string, () => void> = {
|
||||
wallet: () => emit('wallet'),
|
||||
'deactivate-all': () => emit('deactivateAll'),
|
||||
restore: () => emit('restoreProducts'),
|
||||
'close-buy': () => emit('closeAllBuy'),
|
||||
'close-sell': () => emit('closeAllSell'),
|
||||
'sync-prices': () => emit('syncPrices'),
|
||||
'sync-show': () => emit('syncShow'),
|
||||
products: () => void router.push({ name: 'productsMain_limitedOrder' }),
|
||||
orders: () => void router.push({ name: 'ordersMain' }),
|
||||
}
|
||||
|
||||
function runAction(key: string) {
|
||||
menuOpen.value = false
|
||||
mobileActions[key]?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dashboard-operations-trigger {
|
||||
@apply inline-flex h-9 w-full items-center justify-center gap-2 rounded-lg border border-white/30 bg-white px-3.5 text-sm font-semibold text-slate-800 shadow-md transition-all;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.dashboard-operations-trigger:hover {
|
||||
@apply bg-slate-50;
|
||||
}
|
||||
|
||||
.dashboard-operations-trigger.is-open {
|
||||
@apply border-amber-300 bg-amber-50 text-slate-900;
|
||||
}
|
||||
|
||||
.dashboard-operations-trigger__chevron {
|
||||
@apply opacity-60 transition-transform duration-200;
|
||||
}
|
||||
|
||||
.dashboard-operations-trigger.is-open .dashboard-operations-trigger__chevron {
|
||||
@apply rotate-180 opacity-80;
|
||||
}
|
||||
|
||||
.dash-quick-sheet {
|
||||
@apply pb-2;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__header {
|
||||
@apply mb-4 px-1 text-center;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__title {
|
||||
@apply text-base font-bold text-slate-800 dark:text-slate-100;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__subtitle {
|
||||
@apply mt-0.5 text-xs text-slate-500;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured {
|
||||
@apply mb-5 flex w-full items-center gap-3 rounded-2xl bg-gradient-to-l from-emerald-600 to-teal-600 p-3.5 text-right text-white shadow-lg shadow-emerald-900/15 transition-transform active:scale-[0.98];
|
||||
appearance: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured-icon {
|
||||
@apply flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-white/20 backdrop-blur-sm;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured-text {
|
||||
@apply flex min-w-0 flex-1 flex-col gap-0.5;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured-label {
|
||||
@apply text-sm font-bold leading-tight;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured-hint {
|
||||
@apply text-[11px] leading-tight text-white/75;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__featured-arrow {
|
||||
@apply shrink-0 opacity-70;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__section + .dash-quick-sheet__section {
|
||||
@apply mt-4;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__section-title {
|
||||
@apply mb-2 px-1 text-[11px] font-semibold tracking-wide text-slate-400;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__list {
|
||||
@apply overflow-hidden rounded-2xl border border-slate-100 bg-slate-50/80 dark:border-slate-700/60 dark:bg-slate-800/40;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item {
|
||||
@apply flex w-full items-center gap-3 border-0 bg-transparent px-3 py-3 text-right transition-colors;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item + .dash-quick-sheet__item {
|
||||
@apply border-t border-slate-100 dark:border-slate-700/60;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item:active:not(:disabled) {
|
||||
@apply bg-white dark:bg-slate-700/50;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item:disabled {
|
||||
@apply cursor-not-allowed opacity-45;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge {
|
||||
@apply flex h-9 w-9 shrink-0 items-center justify-center rounded-xl;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--slate {
|
||||
@apply bg-slate-200/80 text-slate-600 dark:bg-slate-700 dark:text-slate-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--emerald {
|
||||
@apply bg-emerald-100 text-emerald-600 dark:bg-emerald-900/40 dark:text-emerald-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--rose {
|
||||
@apply bg-rose-100 text-rose-600 dark:bg-rose-900/40 dark:text-rose-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--amber {
|
||||
@apply bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--sky {
|
||||
@apply bg-sky-100 text-sky-600 dark:bg-sky-900/40 dark:text-sky-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__badge--violet {
|
||||
@apply bg-violet-100 text-violet-600 dark:bg-violet-900/40 dark:text-violet-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item-body {
|
||||
@apply flex min-w-0 flex-1 flex-col gap-0.5;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item-label {
|
||||
@apply text-[13px] font-semibold leading-tight text-slate-800 dark:text-slate-100;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item-hint {
|
||||
@apply text-[11px] leading-tight text-slate-400;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item--rose .dash-quick-sheet__item-label {
|
||||
@apply text-rose-700 dark:text-rose-300;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__count {
|
||||
@apply rounded-full bg-slate-200/90 px-2 py-0.5 text-[10px] font-bold text-slate-600 dark:bg-slate-600 dark:text-slate-200;
|
||||
}
|
||||
|
||||
.dash-quick-sheet__item-arrow {
|
||||
@apply shrink-0 text-slate-300 dark:text-slate-500;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user