2026-07-04 14:21:27 +03:30
|
|
|
|
<template>
|
|
|
|
|
|
<div class="sms-panel-page h-full min-h-0 w-full min-w-0">
|
|
|
|
|
|
<div class="sms-panel-page__scroll h-full min-h-0 overflow-y-auto overflow-x-hidden px-2 pb-8 md:px-4">
|
|
|
|
|
|
<header class="mb-6 md:mb-8">
|
|
|
|
|
|
<h1 class="sms-panel-heading text-xl font-bold md:text-2xl">پنل پیامک</h1>
|
|
|
|
|
|
<p class="sms-panel-subtitle mt-1 text-sm">مدیریت کیف پول، تعرفه و گزارش ارسال پیامک</p>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="pageLoading" class="flex justify-center py-16">
|
|
|
|
|
|
<a-spin size="large" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<template v-else-if="overview">
|
|
|
|
|
|
<!-- آمار کلی -->
|
|
|
|
|
|
<section
|
|
|
|
|
|
class="sms-stat-cards mb-6 grid grid-cols-1 gap-2 sm:grid-cols-2 sm:gap-4 xl:grid-cols-4 md:mb-8 md:gap-5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-card relative overflow-hidden rounded-xl border p-3 shadow-sm transition-shadow hover:shadow-md md:rounded-2xl md:p-6"
|
|
|
|
|
|
:class="balanceCardClass"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="sms-stat-inline flex gap-2.5 md:block">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-icon flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
|
|
|
|
|
:class="balanceIconClass"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="vuesax-linear:wallet-3" size="18px" class="md:!text-[22px]" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-stat-inline-body sms-balance-stat-body flex min-w-0 flex-1 justify-between gap-2 md:block">
|
|
|
|
|
|
<div class="sms-balance-stat-title flex min-w-0 items-center gap-1 leading-none md:hidden">
|
|
|
|
|
|
<span class="sms-stat-label shrink-0 text-xs font-medium leading-none md:hidden">موجودی کیف پول</span>
|
|
|
|
|
|
<a-tooltip
|
|
|
|
|
|
v-if="overview.wallet.balance <= 0"
|
|
|
|
|
|
trigger="click"
|
|
|
|
|
|
placement="top"
|
|
|
|
|
|
overlay-class-name="sms-balance-info-tooltip"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #title>{{ balanceAlertHint }}</template>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="sms-balance-info-btn inline-flex shrink-0 items-center justify-center text-[var(--admin-text-secondary)] transition-colors hover:text-[var(--admin-text-muted)]"
|
|
|
|
|
|
aria-label="توضیح هشدار موجودی"
|
|
|
|
|
|
@click.stop
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg class="h-4 w-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
|
|
|
|
<circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.1" />
|
|
|
|
|
|
<circle cx="8" cy="5.2" r="0.55" fill="currentColor" />
|
|
|
|
|
|
<path d="M8 7.2v3.3" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
<a-tag
|
|
|
|
|
|
v-if="overview.wallet.is_blocked"
|
|
|
|
|
|
color="error"
|
|
|
|
|
|
class="!m-0 shrink-0 !text-[10px] leading-none"
|
|
|
|
|
|
>
|
|
|
|
|
|
ارسال مسدود
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p
|
|
|
|
|
|
class="sms-stat-value shrink-0 whitespace-nowrap text-base font-bold leading-none tracking-tight md:hidden"
|
|
|
|
|
|
:class="balanceTextClass"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ formatNumber(overview.wallet.balance) }}
|
|
|
|
|
|
<span class="text-xs font-normal opacity-70">ریال</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="hidden w-full md:block">
|
|
|
|
|
|
<div class="mb-2 flex items-center justify-between gap-2">
|
|
|
|
|
|
<div class="sms-balance-stat-title-desktop flex items-center gap-1">
|
|
|
|
|
|
<span class="sms-stat-label text-sm font-medium leading-none">موجودی کیف پول</span>
|
|
|
|
|
|
<a-tooltip
|
|
|
|
|
|
v-if="overview.wallet.balance <= 0"
|
|
|
|
|
|
trigger="click"
|
|
|
|
|
|
placement="top"
|
|
|
|
|
|
overlay-class-name="sms-balance-info-tooltip"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #title>{{ balanceAlertHint }}</template>
|
|
|
|
|
|
<button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
class="sms-balance-info-btn inline-flex shrink-0 items-center justify-center text-[var(--admin-text-secondary)] transition-colors hover:text-[var(--admin-text-muted)]"
|
|
|
|
|
|
aria-label="توضیح هشدار موجودی"
|
|
|
|
|
|
@click.stop
|
|
|
|
|
|
>
|
|
|
|
|
|
<svg class="h-4 w-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
|
|
|
|
<circle cx="8" cy="8" r="6.25" stroke="currentColor" stroke-width="1.1" />
|
|
|
|
|
|
<circle cx="8" cy="5.2" r="0.55" fill="currentColor" />
|
|
|
|
|
|
<path d="M8 7.2v3.3" stroke="currentColor" stroke-width="1.1" stroke-linecap="round" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a-tag
|
|
|
|
|
|
v-if="overview.wallet.is_blocked"
|
|
|
|
|
|
color="error"
|
|
|
|
|
|
class="!m-0 !text-xs"
|
|
|
|
|
|
>
|
|
|
|
|
|
ارسال مسدود
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p
|
|
|
|
|
|
class="sms-stat-value whitespace-nowrap text-3xl font-bold leading-normal tracking-tight"
|
|
|
|
|
|
:class="balanceTextClass"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ formatNumber(overview.wallet.balance) }}
|
|
|
|
|
|
<span class="text-sm font-normal opacity-70">ریال</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-card sms-stat-card--sky rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="sms-stat-inline flex gap-2.5 md:block">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-icon sms-stat-icon--sky flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="vuesax-linear:tag" Size="18px" class="md:!text-[22px]" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-stat-inline-body flex min-w-0 flex-1 justify-between gap-2 md:block">
|
|
|
|
|
|
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
|
|
|
|
|
قیمت هر پارت
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-value sms-stat-value--sky shrink-0 whitespace-nowrap text-base font-bold leading-none md:text-3xl md:leading-normal">
|
|
|
|
|
|
{{ formatNumber(overview.wallet.price_per_part) }}
|
|
|
|
|
|
<span class="sms-stat-unit text-xs font-normal md:text-sm">ریال</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-card sms-stat-card--emerald rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="sms-stat-inline flex gap-2.5 md:block">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-icon sms-stat-icon--emerald flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="vuesax-linear:sms" Size="18px" class="md:!text-[22px]" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-stat-inline-body sms-stat-period-body flex min-w-0 flex-1 items-center md:block">
|
|
|
|
|
|
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
|
|
|
|
|
امروز
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-period-mid min-w-0 flex-1 text-center text-sm leading-none md:hidden">
|
|
|
|
|
|
<span class="sms-stat-value sms-stat-value--emerald font-bold">
|
|
|
|
|
|
{{ formatNumber(overview.stats.today.messages) }}
|
|
|
|
|
|
<span class="sms-stat-unit text-xs font-medium">پیامک</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="sms-stat-meta">
|
|
|
|
|
|
· {{ formatNumber(overview.stats.today.parts) }} پارت
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-meta shrink-0 whitespace-nowrap text-sm leading-none md:hidden">
|
|
|
|
|
|
{{ formatNumber(overview.stats.today.amount) }} ریال
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-value sms-stat-value--emerald hidden text-xl font-bold md:block">
|
|
|
|
|
|
{{ formatNumber(overview.stats.today.messages) }}
|
|
|
|
|
|
<span class="sms-stat-unit text-sm font-medium">پیامک</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-meta hidden text-sm md:mt-2 md:block">
|
|
|
|
|
|
{{ formatNumber(overview.stats.today.parts) }} پارت ·
|
|
|
|
|
|
{{ formatNumber(overview.stats.today.amount) }} ریال
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-card sms-stat-card--violet rounded-xl border p-3 shadow-sm md:rounded-2xl md:p-6"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="sms-stat-inline flex gap-2.5 md:block">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-stat-icon sms-stat-icon--violet flex h-9 w-9 shrink-0 items-center justify-center rounded-lg md:mb-4 md:h-11 md:w-11 md:rounded-xl"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="vuesax-linear:chart" Size="18px" class="md:!text-[22px]" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-stat-inline-body sms-stat-period-body flex min-w-0 flex-1 items-center md:block">
|
|
|
|
|
|
<p class="sms-stat-label shrink-0 text-xs font-medium leading-none md:mb-2 md:text-sm md:leading-normal">
|
|
|
|
|
|
ماه جاری
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-period-mid min-w-0 flex-1 text-center text-sm leading-none md:hidden">
|
|
|
|
|
|
<span class="sms-stat-value sms-stat-value--violet font-bold">
|
|
|
|
|
|
{{ formatNumber(overview.stats.month.messages) }}
|
|
|
|
|
|
<span class="sms-stat-unit text-xs font-medium">پیامک</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="sms-stat-meta">
|
|
|
|
|
|
· {{ formatNumber(overview.stats.month.parts) }} پارت
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-meta shrink-0 whitespace-nowrap text-sm leading-none md:hidden">
|
|
|
|
|
|
{{ formatNumber(overview.stats.month.amount) }} ریال
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-value sms-stat-value--violet hidden text-xl font-bold md:block">
|
|
|
|
|
|
{{ formatNumber(overview.stats.month.messages) }}
|
|
|
|
|
|
<span class="sms-stat-unit text-sm font-medium">پیامک</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p class="sms-stat-meta hidden text-sm md:mt-2 md:block">
|
|
|
|
|
|
{{ formatNumber(overview.stats.month.parts) }} پارت ·
|
|
|
|
|
|
{{ formatNumber(overview.stats.month.amount) }} ریال
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- مدیریت و گزارش -->
|
|
|
|
|
|
<section class="sms-panel-mgmt mb-6 flex flex-col gap-4 md:mb-8 md:gap-6">
|
|
|
|
|
|
<div class="sms-panel-surface rounded-2xl p-4 shadow-sm md:p-6">
|
|
|
|
|
|
<div class="sms-panel-section-head mb-4 flex items-center gap-2 pb-3 md:mb-5 md:pb-4">
|
|
|
|
|
|
<Icon icon="vuesax-linear:setting-2" Size="20px" class="sms-panel-section-icon" />
|
|
|
|
|
|
<h2 class="sms-panel-heading text-base font-semibold">مدیریت کیف پول</h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-col gap-4 md:gap-6">
|
|
|
|
|
|
<div class="flex flex-col gap-3 md:gap-4 xl:flex-row xl:items-stretch">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-panel-subcard sms-panel-subcard--sky flex-1 rounded-xl p-3 shadow-sm md:p-5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mb-3 flex items-center gap-2 md:mb-4">
|
|
|
|
|
|
<span class="sms-stat-icon sms-stat-icon--sky flex h-8 w-8 items-center justify-center rounded-lg">
|
|
|
|
|
|
<Icon icon="vuesax-linear:tag" Size="18px" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p class="sms-subcard-title text-sm font-semibold">تعرفه پیامک</p>
|
|
|
|
|
|
<p class="sms-panel-subtitle hidden text-xs md:block">
|
|
|
|
|
|
قیمت هر پارت برای ارسالهای بعدی
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="grid grid-cols-[1fr_auto] items-end gap-2 md:gap-3">
|
|
|
|
|
|
<a-form-item label="قیمت هر پارت (ریال)" class="sms-compact-field !mb-0">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="pricePerPartField"
|
|
|
|
|
|
inputmode="numeric"
|
|
|
|
|
|
placeholder="قیمت پارت"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="!w-full"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<BaseButton
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="!w-auto shrink-0 whitespace-nowrap"
|
|
|
|
|
|
:loading="actionLoading"
|
|
|
|
|
|
@click="submitPrice"
|
|
|
|
|
|
>
|
|
|
|
|
|
ذخیره قیمت
|
|
|
|
|
|
</BaseButton>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-panel-subcard sms-panel-subcard--emerald flex-1 rounded-xl p-3 shadow-sm md:p-5"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="mb-3 flex items-center gap-2 md:mb-4">
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="sms-stat-icon sms-stat-icon--emerald flex h-8 w-8 items-center justify-center rounded-lg"
|
|
|
|
|
|
>
|
|
|
|
|
|
<Icon icon="vuesax-linear:wallet-add" Size="18px" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p class="sms-subcard-title text-sm font-semibold">شارژ کیف پول</p>
|
|
|
|
|
|
<p class="sms-panel-subtitle hidden text-xs md:block">
|
|
|
|
|
|
افزایش موجودی برای ارسال پیامک
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="grid grid-cols-[1fr_auto] items-end gap-2 md:grid-cols-2 md:gap-3 xl:grid-cols-[1fr_1.2fr_auto]"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="مبلغ شارژ (ریال)" class="sms-compact-field order-1 !mb-0">
|
|
|
|
|
|
<a-input
|
|
|
|
|
|
v-model:value="chargeAmountField"
|
|
|
|
|
|
inputmode="numeric"
|
|
|
|
|
|
placeholder="مبلغ شارژ"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="!w-full"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item
|
|
|
|
|
|
label="توضیح (اختیاری)"
|
|
|
|
|
|
class="sms-compact-field order-3 col-span-2 !mb-0 md:order-2 md:col-span-1"
|
|
|
|
|
|
>
|
|
|
|
|
|
<BasicInput v-model:value="chargeNote" place-holder="توضیح شارژ" size="large" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<BaseButton
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="order-2 !w-auto shrink-0 whitespace-nowrap md:order-3 md:col-span-2 xl:col-span-1"
|
|
|
|
|
|
:loading="actionLoading"
|
|
|
|
|
|
@click="openChargeConfirm"
|
|
|
|
|
|
>
|
|
|
|
|
|
شارژ کیف پول
|
|
|
|
|
|
</BaseButton>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-panel-block rounded-xl border p-4 md:p-5"
|
|
|
|
|
|
:class="isBlocked ? 'sms-panel-block--blocked' : 'sms-panel-block--active'"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="inline-flex max-w-full flex-col gap-2 self-start">
|
|
|
|
|
|
<div class="sms-block-row flex w-full items-center justify-between gap-4">
|
|
|
|
|
|
<span class="sms-panel-heading font-semibold leading-none">
|
|
|
|
|
|
مسدودسازی ارسال پیامک
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<a-switch
|
|
|
|
|
|
v-model:checked="isBlocked"
|
|
|
|
|
|
class="sms-block-switch !m-0 shrink-0"
|
|
|
|
|
|
:class="isBlocked ? 'sms-block-switch--blocked' : 'sms-block-switch--active'"
|
|
|
|
|
|
:loading="blockLoading"
|
|
|
|
|
|
checked-children="مسدود"
|
|
|
|
|
|
un-checked-children="فعال"
|
|
|
|
|
|
@change="onBlockChange"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="sms-panel-subtitle text-sm leading-relaxed">
|
|
|
|
|
|
در حالت مسدود، هیچ پیامکی از این نسخه ارسال نمیشود.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-07-07 20:27:51 +03:30
|
|
|
|
|
|
|
|
|
|
<div class="sms-panel-subcard sms-panel-subcard--sky rounded-xl p-3 shadow-sm md:p-5">
|
|
|
|
|
|
<div class="mb-3 flex items-center gap-2 md:mb-4">
|
|
|
|
|
|
<span class="sms-stat-icon sms-stat-icon--sky flex h-8 w-8 items-center justify-center rounded-lg">
|
|
|
|
|
|
<Icon icon="vuesax-linear:card" Size="18px" />
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p class="sms-subcard-title text-sm font-semibold">اطلاعات واریز شارژ پیامک</p>
|
|
|
|
|
|
<p class="sms-panel-subtitle hidden text-xs md:block">
|
|
|
|
|
|
برای همه مشتریها یکسان است و در پاپآپ «شارژ» هدر نمایش داده میشود.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 md:gap-4">
|
|
|
|
|
|
<a-form-item label="شماره کارت" class="sms-compact-field !mb-0">
|
|
|
|
|
|
<BasicInput v-model:value="depositForm.card_number" place-holder="شماره کارت" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="شماره شبا" class="sms-compact-field !mb-0">
|
|
|
|
|
|
<BasicInput v-model:value="depositForm.sheba" place-holder="IR..." />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="نام صاحب حساب" class="sms-compact-field !mb-0 md:col-span-2">
|
|
|
|
|
|
<BasicInput v-model:value="depositForm.account_holder" place-holder="نام صاحب حساب" />
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="متن راهنما" class="sms-compact-field !mb-0 md:col-span-2">
|
|
|
|
|
|
<a-textarea
|
|
|
|
|
|
v-model:value="depositForm.note"
|
|
|
|
|
|
:auto-size="{ minRows: 2, maxRows: 5 }"
|
|
|
|
|
|
placeholder="متن راهنمای واریز"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-3 flex justify-end md:mt-4">
|
|
|
|
|
|
<BaseButton
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
class="!w-auto shrink-0 whitespace-nowrap"
|
|
|
|
|
|
:loading="depositLoading"
|
|
|
|
|
|
@click="submitDepositInfo"
|
|
|
|
|
|
>
|
|
|
|
|
|
ذخیره اطلاعات واریز
|
|
|
|
|
|
</BaseButton>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-07-04 14:21:27 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="sms-panel-surface sms-report-card rounded-2xl p-4 shadow-sm md:p-6">
|
|
|
|
|
|
<div class="sms-panel-section-head mb-3 flex items-center gap-2 pb-3 md:mb-4 md:pb-4">
|
|
|
|
|
|
<Icon icon="vuesax-linear:calendar" Size="20px" class="sms-panel-section-icon" />
|
|
|
|
|
|
<h2 class="sms-panel-heading text-base font-semibold">گزارش</h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<a-tabs v-model:activeKey="activeReportTab" class="sms-panel-tabs sms-report-tabs">
|
|
|
|
|
|
<a-tab-pane key="sms" tab="گزارش بازهای پیامک">
|
|
|
|
|
|
<div class="flex flex-col gap-3 md:gap-4">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-report-filter-row grid grid-cols-[1fr_1fr_auto] items-center gap-2 md:flex md:flex-wrap md:items-end md:gap-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="reportFrom"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="از تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="reportTo"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="تا تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<BaseButton
|
|
|
|
|
|
class="sms-report-action-btn !w-auto shrink-0 whitespace-nowrap px-3 md:!w-auto"
|
|
|
|
|
|
:loading="reportLoading"
|
|
|
|
|
|
@click="loadReport"
|
|
|
|
|
|
>
|
|
|
|
|
|
دریافت گزارش
|
|
|
|
|
|
</BaseButton>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="report"
|
|
|
|
|
|
class="sms-panel-muted-box space-y-2 rounded-xl p-3 md:space-y-3 md:p-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="grid grid-cols-3 gap-2 md:gap-3">
|
|
|
|
|
|
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد پیامک</span>
|
|
|
|
|
|
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
|
|
|
|
|
{{ formatNumber(report.summary.messages) }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد پارت</span>
|
|
|
|
|
|
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
|
|
|
|
|
{{ formatNumber(report.summary.parts) }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] md:text-xs">مبلغ کل</span>
|
|
|
|
|
|
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
|
|
|
|
|
{{ formatNumber(report.summary.amount) }}
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] font-normal md:text-sm">ریال</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="report.by_provider?.length" class="sms-panel-divider border-t pt-2 md:pt-3">
|
|
|
|
|
|
<p class="sms-stat-label mb-1 text-[10px] font-semibold md:mb-2 md:text-xs">
|
|
|
|
|
|
بر اساس ارائهدهنده
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<ul class="space-y-1 md:space-y-2">
|
|
|
|
|
|
<li
|
|
|
|
|
|
v-for="row in report.by_provider"
|
|
|
|
|
|
:key="row.provider"
|
|
|
|
|
|
class="sms-panel-kpi flex items-center justify-between gap-2 rounded-lg px-2 py-1.5 text-[11px] md:px-3 md:py-2 md:text-xs"
|
|
|
|
|
|
>
|
|
|
|
|
|
<span class="font-medium">{{ row.provider }}</span>
|
|
|
|
|
|
<span class="sms-panel-subtitle shrink-0">
|
|
|
|
|
|
{{ formatNumber(row.messages) }} پیام · {{ formatNumber(row.amount) }} ریال
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="charge" tab="گزارش شارژ کیف پول">
|
|
|
|
|
|
<div class="flex flex-col gap-3 md:gap-4">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-report-filter-row grid grid-cols-[1fr_1fr_auto] items-center gap-2 md:flex md:flex-wrap md:items-end md:gap-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="chargeReportFrom"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="از تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="chargeReportTo"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="تا تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<BaseButton
|
|
|
|
|
|
class="sms-report-action-btn !w-auto shrink-0 whitespace-nowrap px-3 md:!w-auto"
|
|
|
|
|
|
:loading="chargeReportLoading"
|
|
|
|
|
|
@click="loadChargeReport"
|
|
|
|
|
|
>
|
|
|
|
|
|
دریافت گزارش
|
|
|
|
|
|
</BaseButton>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<p class="sms-panel-subtitle text-xs md:text-sm">
|
|
|
|
|
|
در صورت خالی بودن تاریخ، همه شارژها نمایش داده میشوند.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-if="chargeReport"
|
|
|
|
|
|
class="sms-panel-muted-box space-y-2 rounded-xl p-3 md:space-y-3 md:p-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-2 md:gap-3">
|
|
|
|
|
|
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] md:text-xs">تعداد شارژ</span>
|
|
|
|
|
|
<p class="sms-panel-heading text-sm font-bold md:text-lg">
|
|
|
|
|
|
{{ formatNumber(chargeReport.summary.charges) }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-panel-kpi rounded-lg px-2 py-1.5 md:px-3 md:py-2.5">
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] md:text-xs">مبلغ کل شارژ</span>
|
|
|
|
|
|
<p class="sms-stat-value sms-stat-value--emerald text-sm font-bold md:text-lg">
|
|
|
|
|
|
{{ formatNumber(chargeReport.summary.amount) }}
|
|
|
|
|
|
<span class="sms-panel-subtitle text-[10px] font-normal md:text-sm">ریال</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="chargeReport.items.length" class="sms-panel-divider border-t pt-2 md:pt-3">
|
|
|
|
|
|
<p class="sms-stat-label mb-1 text-[10px] font-semibold md:mb-2 md:text-xs">
|
|
|
|
|
|
لیست شارژها
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<ul class="max-h-64 space-y-1 overflow-y-auto md:max-h-80 md:space-y-2">
|
|
|
|
|
|
<li
|
|
|
|
|
|
v-for="item in chargeReport.items"
|
|
|
|
|
|
:key="item.id"
|
|
|
|
|
|
class="sms-panel-kpi rounded-lg px-2 py-2 text-[11px] md:px-3 md:py-2.5 md:text-xs"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="flex items-start justify-between gap-2">
|
|
|
|
|
|
<div class="min-w-0">
|
|
|
|
|
|
<p class="sms-stat-value sms-stat-value--emerald font-semibold">
|
|
|
|
|
|
+{{ formatNumber(item.amount) }} ریال
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p v-if="item.note" class="sms-panel-subtitle mt-0.5">{{ item.note }}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="sms-panel-subtitle shrink-0 text-left">
|
|
|
|
|
|
<p>{{ formatReportDate(item.created_at) }}</p>
|
|
|
|
|
|
<p class="mt-0.5">
|
|
|
|
|
|
موجودی:
|
|
|
|
|
|
{{ formatNumber(item.balance_after) }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p v-else class="sms-panel-subtitle text-center text-xs md:text-sm">
|
|
|
|
|
|
شارژی در این بازه یافت نشد.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- جداول -->
|
|
|
|
|
|
<section class="sms-panel-surface rounded-2xl p-4 shadow-sm md:p-6">
|
|
|
|
|
|
<a-tabs
|
|
|
|
|
|
v-model:activeKey="activeTab"
|
|
|
|
|
|
class="sms-panel-tabs sms-panel-table-tabs"
|
|
|
|
|
|
:animated="false"
|
|
|
|
|
|
destroy-inactive-tab-pane
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-tab-pane key="logs" tab="لاگ ارسال">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-panel-muted-box sms-table-date-filter mb-4 grid grid-cols-2 items-end gap-2 rounded-xl p-3 md:mb-5 md:flex md:flex-wrap md:gap-4 md:p-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="tableFrom"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="انتخاب تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="tableTo"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="انتخاب تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<SmsPanelLogsTable ref="logsTableRef" :from-date="tableFrom" :to-date="tableTo" />
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
<a-tab-pane key="ledger" tab="دفترچه کیف پول">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="sms-panel-muted-box sms-table-date-filter mb-4 grid grid-cols-2 items-end gap-2 rounded-xl p-3 md:mb-5 md:flex md:flex-wrap md:gap-4 md:p-4"
|
|
|
|
|
|
>
|
|
|
|
|
|
<a-form-item label="از تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="tableFrom"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="انتخاب تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
<a-form-item label="تا تاریخ" class="sms-compact-field !mb-0 min-w-0 md:min-w-[160px] md:flex-1">
|
|
|
|
|
|
<BaseDatePicker
|
|
|
|
|
|
v-model:value="tableTo"
|
|
|
|
|
|
format="YYYY-MM-DD"
|
|
|
|
|
|
class="w-full"
|
|
|
|
|
|
placeholder="انتخاب تاریخ"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</a-form-item>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<SmsPanelLedgerTable ref="ledgerTableRef" :from-date="tableFrom" :to-date="tableTo" />
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<BasicModal
|
|
|
|
|
|
:visible="chargeModalVisible"
|
|
|
|
|
|
title="تأیید شارژ کیف پول"
|
|
|
|
|
|
width="380px"
|
|
|
|
|
|
ok-text="شارژ"
|
|
|
|
|
|
:loading="actionLoading"
|
|
|
|
|
|
@ok="submitCharge"
|
|
|
|
|
|
@close="chargeModalVisible = false"
|
|
|
|
|
|
>
|
|
|
|
|
|
<p class="sms-panel-subtitle p-3 text-sm">
|
|
|
|
|
|
مبلغ
|
|
|
|
|
|
<strong class="sms-panel-heading">{{ formatNumber(Number(chargeAmount) || 0) }}</strong>
|
|
|
|
|
|
ریال به کیف پول پیامک اضافه شود؟
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</BasicModal>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import BasicInput from '@/components/BasicInput.vue'
|
|
|
|
|
|
import BasicModal from '@/components/BasicModal.vue'
|
|
|
|
|
|
import BaseDatePicker from '@core/components/BaseDatePicker'
|
|
|
|
|
|
import BaseButton from '@core/components/BaseButton'
|
|
|
|
|
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
|
|
|
|
|
import SmsPanelLogsTable from './components/SmsPanelLogsTable.vue'
|
|
|
|
|
|
import SmsPanelLedgerTable from './components/SmsPanelLedgerTable.vue'
|
|
|
|
|
|
import {
|
|
|
|
|
|
chargeSmsWallet,
|
|
|
|
|
|
getSmsPanelChargeReport,
|
|
|
|
|
|
getSmsPanelOverview,
|
|
|
|
|
|
getSmsPanelReport,
|
|
|
|
|
|
updateSmsBlock,
|
2026-07-07 20:27:51 +03:30
|
|
|
|
updateSmsDepositInfo,
|
2026-07-04 14:21:27 +03:30
|
|
|
|
updateSmsPrice,
|
|
|
|
|
|
type SmsPanelChargeReport,
|
|
|
|
|
|
type SmsPanelOverview,
|
|
|
|
|
|
type SmsPanelReport
|
|
|
|
|
|
} from '@/api/smsPanel'
|
2026-07-07 20:27:51 +03:30
|
|
|
|
import { useSmsWalletStore } from '@/store/smsWalletStore'
|
2026-07-04 14:21:27 +03:30
|
|
|
|
import { toJalaliNoTime } from '@core/utils/useDataTime'
|
|
|
|
|
|
import { message } from 'ant-design-vue'
|
|
|
|
|
|
|
2026-07-07 20:27:51 +03:30
|
|
|
|
const smsWalletStore = useSmsWalletStore()
|
|
|
|
|
|
|
2026-07-04 14:21:27 +03:30
|
|
|
|
const pageLoading = ref(true)
|
|
|
|
|
|
const actionLoading = ref(false)
|
|
|
|
|
|
const blockLoading = ref(false)
|
|
|
|
|
|
const isBlocked = ref(false)
|
|
|
|
|
|
const reportLoading = ref(false)
|
|
|
|
|
|
const chargeReportLoading = ref(false)
|
|
|
|
|
|
const overview = ref<SmsPanelOverview | null>(null)
|
|
|
|
|
|
const report = ref<SmsPanelReport | null>(null)
|
|
|
|
|
|
const chargeReport = ref<SmsPanelChargeReport | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const chargeAmount = ref<number | null>(null)
|
|
|
|
|
|
const chargeNote = ref('')
|
|
|
|
|
|
const pricePerPart = ref<number | null>(null)
|
|
|
|
|
|
const chargeModalVisible = ref(false)
|
|
|
|
|
|
|
2026-07-07 20:27:51 +03:30
|
|
|
|
const depositLoading = ref(false)
|
|
|
|
|
|
const depositForm = reactive({
|
|
|
|
|
|
card_number: '',
|
|
|
|
|
|
sheba: '',
|
|
|
|
|
|
account_holder: '',
|
|
|
|
|
|
note: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-07-04 14:21:27 +03:30
|
|
|
|
const reportFrom = ref('')
|
|
|
|
|
|
const reportTo = ref('')
|
|
|
|
|
|
const chargeReportFrom = ref('')
|
|
|
|
|
|
const chargeReportTo = ref('')
|
|
|
|
|
|
const tableFrom = ref('')
|
|
|
|
|
|
const tableTo = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
const activeReportTab = ref('sms')
|
|
|
|
|
|
const activeTab = ref('logs')
|
|
|
|
|
|
const logsTableRef = ref<InstanceType<typeof SmsPanelLogsTable> | null>(null)
|
|
|
|
|
|
const ledgerTableRef = ref<InstanceType<typeof SmsPanelLedgerTable> | null>(null)
|
|
|
|
|
|
|
|
|
|
|
|
const balanceCardClass = computed(() => {
|
|
|
|
|
|
if (!overview.value) return 'sms-stat-card--neutral'
|
|
|
|
|
|
if (overview.value.wallet.balance < 0) return 'sms-stat-card--danger'
|
|
|
|
|
|
if (overview.value.wallet.balance <= 0) return 'sms-stat-card--warn'
|
|
|
|
|
|
return 'sms-stat-card--ok'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const balanceIconClass = computed(() => {
|
|
|
|
|
|
if (!overview.value) return 'sms-stat-icon--neutral'
|
|
|
|
|
|
if (overview.value.wallet.balance < 0) return 'sms-stat-icon--danger'
|
|
|
|
|
|
if (overview.value.wallet.balance <= 0) return 'sms-stat-icon--warn'
|
|
|
|
|
|
return 'sms-stat-icon--ok'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const balanceTextClass = computed(() => {
|
|
|
|
|
|
if (!overview.value) return 'sms-stat-value--neutral'
|
|
|
|
|
|
if (overview.value.wallet.balance < 0) return 'sms-stat-value--danger'
|
|
|
|
|
|
if (overview.value.wallet.balance <= 0) return 'sms-stat-value--warn'
|
|
|
|
|
|
return 'sms-stat-value--ok'
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const balanceAlertHint = computed(() => {
|
|
|
|
|
|
const sent = overview.value?.wallet.alert_days_sent ?? 0
|
|
|
|
|
|
return `هشدار روزانه حداکثر ۳ روز (${sent}/۳)`
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function formatNumber(value: number) {
|
|
|
|
|
|
return Number(value || 0).toLocaleString('fa-IR')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatReportDate(value: string | null | undefined) {
|
|
|
|
|
|
if (!value) return '—'
|
|
|
|
|
|
return toJalaliNoTime(value)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function formatAmountDisplay(value: number | null) {
|
|
|
|
|
|
if (value == null || !Number.isFinite(value)) return ''
|
|
|
|
|
|
return String(Math.trunc(value)).replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function parseAmountDisplay(value: string) {
|
|
|
|
|
|
const digits = value.replace(/[^\d]/g, '')
|
|
|
|
|
|
return digits === '' ? null : Number(digits)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function useAmountField(source: Ref<number | null>) {
|
|
|
|
|
|
return computed({
|
|
|
|
|
|
get: () => formatAmountDisplay(source.value),
|
|
|
|
|
|
set: (val: string) => {
|
|
|
|
|
|
source.value = parseAmountDisplay(val)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const chargeAmountField = useAmountField(chargeAmount)
|
|
|
|
|
|
const pricePerPartField = useAmountField(pricePerPart)
|
|
|
|
|
|
|
|
|
|
|
|
async function loadOverview() {
|
|
|
|
|
|
pageLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
overview.value = await getSmsPanelOverview()
|
|
|
|
|
|
pricePerPart.value = overview.value.wallet.price_per_part || null
|
|
|
|
|
|
isBlocked.value = overview.value.wallet.is_blocked
|
2026-07-07 20:27:51 +03:30
|
|
|
|
if (overview.value.deposit) {
|
|
|
|
|
|
depositForm.card_number = overview.value.deposit.card_number ?? ''
|
|
|
|
|
|
depositForm.sheba = overview.value.deposit.sheba ?? ''
|
|
|
|
|
|
depositForm.account_holder = overview.value.deposit.account_holder ?? ''
|
|
|
|
|
|
depositForm.note = overview.value.deposit.note ?? ''
|
|
|
|
|
|
}
|
2026-07-04 14:21:27 +03:30
|
|
|
|
} catch {
|
|
|
|
|
|
message.error('خطا در بارگذاری پنل پیامک')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
pageLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function openChargeConfirm() {
|
|
|
|
|
|
const amount = Number(chargeAmount.value ?? 0)
|
|
|
|
|
|
if (!amount || amount <= 0) {
|
|
|
|
|
|
message.warning('مبلغ شارژ را وارد کنید')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
chargeModalVisible.value = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function submitCharge() {
|
|
|
|
|
|
const amount = Number(chargeAmount.value ?? 0)
|
|
|
|
|
|
if (!amount || amount <= 0) return
|
|
|
|
|
|
|
|
|
|
|
|
actionLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await chargeSmsWallet({
|
|
|
|
|
|
amount,
|
|
|
|
|
|
note: chargeNote.value.trim() || undefined
|
|
|
|
|
|
})
|
|
|
|
|
|
message.success(res.message || 'شارژ انجام شد')
|
|
|
|
|
|
chargeModalVisible.value = false
|
|
|
|
|
|
chargeAmount.value = null
|
|
|
|
|
|
chargeNote.value = ''
|
|
|
|
|
|
await loadOverview()
|
|
|
|
|
|
ledgerTableRef.value?.fetch()
|
|
|
|
|
|
if (chargeReport.value) {
|
|
|
|
|
|
await loadChargeReport()
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
message.error(e?.response?.data?.message || 'خطا در شارژ')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 20:27:51 +03:30
|
|
|
|
async function submitDepositInfo() {
|
|
|
|
|
|
depositLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await updateSmsDepositInfo({
|
|
|
|
|
|
card_number: depositForm.card_number.trim(),
|
|
|
|
|
|
sheba: depositForm.sheba.trim(),
|
|
|
|
|
|
account_holder: depositForm.account_holder.trim(),
|
|
|
|
|
|
note: depositForm.note.trim()
|
|
|
|
|
|
})
|
|
|
|
|
|
message.success(res.message || 'اطلاعات واریز ذخیره شد')
|
|
|
|
|
|
if (res.deposit) {
|
|
|
|
|
|
depositForm.card_number = res.deposit.card_number ?? ''
|
|
|
|
|
|
depositForm.sheba = res.deposit.sheba ?? ''
|
|
|
|
|
|
depositForm.account_holder = res.deposit.account_holder ?? ''
|
|
|
|
|
|
depositForm.note = res.deposit.note ?? ''
|
|
|
|
|
|
}
|
|
|
|
|
|
await smsWalletStore.refresh()
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
message.error(e?.response?.data?.message || 'خطا در ذخیره اطلاعات واریز')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
depositLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-04 14:21:27 +03:30
|
|
|
|
async function submitPrice() {
|
|
|
|
|
|
const price = Number(pricePerPart.value ?? 0)
|
|
|
|
|
|
if (Number.isNaN(price) || price < 0) {
|
|
|
|
|
|
message.warning('قیمت معتبر وارد کنید')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
actionLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await updateSmsPrice(price)
|
|
|
|
|
|
message.success(res.message || 'قیمت ذخیره شد')
|
|
|
|
|
|
await loadOverview()
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
message.error(e?.response?.data?.message || 'خطا در ذخیره قیمت')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
actionLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function onBlockChange(checked: boolean) {
|
|
|
|
|
|
blockLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await updateSmsBlock(checked)
|
|
|
|
|
|
message.success(res.message)
|
|
|
|
|
|
isBlocked.value = res.is_blocked
|
|
|
|
|
|
await loadOverview()
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
isBlocked.value = overview.value?.wallet.is_blocked ?? false
|
|
|
|
|
|
message.error(e?.response?.data?.message || 'خطا در تغییر وضعیت')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
blockLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadReport() {
|
|
|
|
|
|
reportLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
report.value = await getSmsPanelReport({
|
|
|
|
|
|
from_date: reportFrom.value || undefined,
|
|
|
|
|
|
to_date: reportTo.value || undefined
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
message.error('خطا در دریافت گزارش')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
reportLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadChargeReport() {
|
|
|
|
|
|
chargeReportLoading.value = true
|
|
|
|
|
|
try {
|
|
|
|
|
|
chargeReport.value = await getSmsPanelChargeReport({
|
|
|
|
|
|
from_date: chargeReportFrom.value || undefined,
|
|
|
|
|
|
to_date: chargeReportTo.value || undefined
|
|
|
|
|
|
})
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
message.error('خطا در دریافت گزارش شارژ')
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
chargeReportLoading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
loadOverview()
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.sms-panel-page__scroll {
|
|
|
|
|
|
-webkit-overflow-scrolling: touch;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-heading {
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-subtitle {
|
|
|
|
|
|
color: var(--admin-text-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-section-icon {
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-section-head {
|
|
|
|
|
|
border-bottom: 1px solid var(--admin-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-surface {
|
|
|
|
|
|
border: 1px solid var(--admin-border);
|
|
|
|
|
|
background: var(--admin-bg-elevated);
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-muted-box {
|
|
|
|
|
|
border: 1px solid var(--admin-border);
|
|
|
|
|
|
background: var(--admin-bg-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-kpi {
|
|
|
|
|
|
background: var(--admin-bg-elevated);
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-divider {
|
|
|
|
|
|
border-color: var(--admin-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-label {
|
|
|
|
|
|
color: var(--admin-text-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-meta {
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-unit {
|
|
|
|
|
|
color: var(--admin-text-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--neutral {
|
|
|
|
|
|
border-color: var(--admin-border);
|
|
|
|
|
|
background: var(--admin-bg-elevated);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--ok {
|
|
|
|
|
|
border-color: rgba(16, 185, 129, 0.28);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.1), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--warn {
|
|
|
|
|
|
border-color: rgba(245, 158, 11, 0.32);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(245, 158, 11, 0.12), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--danger {
|
|
|
|
|
|
border-color: rgba(239, 68, 68, 0.32);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(239, 68, 68, 0.12), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--sky {
|
|
|
|
|
|
border-color: rgba(14, 165, 233, 0.28);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(14, 165, 233, 0.1), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--emerald {
|
|
|
|
|
|
border-color: rgba(16, 185, 129, 0.28);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.1), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-card--violet {
|
|
|
|
|
|
border-color: rgba(139, 92, 246, 0.28);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(139, 92, 246, 0.1), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon {
|
|
|
|
|
|
background: var(--admin-bg-muted);
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--neutral {
|
|
|
|
|
|
background: var(--admin-bg-muted);
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--ok,
|
|
|
|
|
|
.sms-stat-icon--emerald {
|
|
|
|
|
|
background: rgba(16, 185, 129, 0.14);
|
|
|
|
|
|
color: #059669;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--warn {
|
|
|
|
|
|
background: rgba(245, 158, 11, 0.16);
|
|
|
|
|
|
color: #d97706;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--danger {
|
|
|
|
|
|
background: rgba(239, 68, 68, 0.14);
|
|
|
|
|
|
color: #dc2626;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--sky {
|
|
|
|
|
|
background: rgba(14, 165, 233, 0.14);
|
|
|
|
|
|
color: #0284c7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--violet {
|
|
|
|
|
|
background: rgba(139, 92, 246, 0.14);
|
|
|
|
|
|
color: #7c3aed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--neutral {
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--ok {
|
|
|
|
|
|
color: #047857;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--warn {
|
|
|
|
|
|
color: #b45309;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--danger {
|
|
|
|
|
|
color: #b91c1c;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--sky {
|
|
|
|
|
|
color: #0c4a6e;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--emerald {
|
|
|
|
|
|
color: #047857;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--violet {
|
|
|
|
|
|
color: #5b21b6;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-subcard {
|
|
|
|
|
|
border: 1px solid var(--admin-border);
|
|
|
|
|
|
background: var(--admin-bg-elevated);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-subcard--sky {
|
|
|
|
|
|
border-color: rgba(14, 165, 233, 0.24);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(14, 165, 233, 0.08), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-subcard--emerald {
|
|
|
|
|
|
border-color: rgba(16, 185, 129, 0.24);
|
|
|
|
|
|
background: linear-gradient(to bottom right, rgba(16, 185, 129, 0.08), var(--admin-bg-elevated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-subcard-title {
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-block--active {
|
|
|
|
|
|
border-color: rgba(16, 185, 129, 0.28);
|
|
|
|
|
|
background: rgba(16, 185, 129, 0.08);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-block--blocked {
|
|
|
|
|
|
border-color: rgba(239, 68, 68, 0.28);
|
|
|
|
|
|
background: rgba(239, 68, 68, 0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-btn-primary:not(:disabled)) {
|
|
|
|
|
|
background-color: var(--admin-gold-light);
|
|
|
|
|
|
border-color: var(--admin-gold-light);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-btn-primary:not(:disabled):hover),
|
|
|
|
|
|
.sms-panel-page :deep(.ant-btn-primary:not(:disabled):focus) {
|
|
|
|
|
|
background-color: var(--admin-gold-hover);
|
|
|
|
|
|
border-color: var(--admin-gold-hover);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-tabs-ink-bar) {
|
|
|
|
|
|
background: var(--admin-gold-light);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
|
|
|
|
|
|
color: var(--admin-gold-light);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-tabs-tab .ant-tabs-tab-btn) {
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-input-lg),
|
|
|
|
|
|
.sms-panel-page :deep(.ant-picker) {
|
|
|
|
|
|
border-radius: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.ant-form-item-label > label) {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-info-btn {
|
|
|
|
|
|
width: 1.125rem;
|
|
|
|
|
|
height: 1.125rem;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
line-height: 0;
|
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-title-desktop {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-title-desktop :deep(.ant-tooltip) {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
line-height: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-tabs :deep(.ant-tabs-nav) {
|
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-tabs :deep(.ant-tabs-tab) {
|
|
|
|
|
|
padding: 0.6rem 1rem;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.ant-tabs-content-holder),
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.ant-tabs-content),
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.ant-tabs-tabpane),
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.sms-panel-table),
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.hr-tamin-base-table),
|
|
|
|
|
|
.sms-panel-table-tabs :deep(.scrollable-container) {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
max-width: 100%;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-report-tabs :deep(.ant-tabs-nav) {
|
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
|
.sms-report-tabs :deep(.ant-tabs-nav) {
|
|
|
|
|
|
margin-bottom: 1.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-report-card .sms-compact-field .ant-picker) {
|
|
|
|
|
|
height: 2rem;
|
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-report-card .sms-report-action-btn.ant-btn) {
|
|
|
|
|
|
height: 2rem;
|
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
|
line-height: 1.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-report-card .sms-report-filter-row) {
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (min-width: 768px) {
|
|
|
|
|
|
.sms-panel-page :deep(.sms-report-card .sms-report-filter-row) {
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-report-card .sms-compact-field.ant-form-item) {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-switch--active:not(.ant-switch-checked)) {
|
|
|
|
|
|
background: #22c55e;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-switch--active:not(.ant-switch-checked):hover:not(.ant-switch-disabled)) {
|
|
|
|
|
|
background: #16a34a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-switch--blocked.ant-switch-checked) {
|
|
|
|
|
|
background: #ef4444;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-switch--blocked.ant-switch-checked:hover:not(.ant-switch-disabled)) {
|
|
|
|
|
|
background: #dc2626;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-row .ant-switch) {
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-block-row .ant-switch-inner) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 767px) {
|
|
|
|
|
|
.sms-stat-inline {
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-inline-body {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
height: 2.25rem;
|
|
|
|
|
|
min-height: 2.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-inline-body > p {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-body > p {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-title {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
line-height: 1;
|
|
|
|
|
|
min-height: 2.25rem;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-title :deep(.ant-tooltip) {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
line-height: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-balance-stat-body :deep(.ant-tooltip) {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
line-height: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-period-body {
|
|
|
|
|
|
gap: 0.375rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-period-mid {
|
|
|
|
|
|
padding-inline: 0.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-compact-field .ant-form-item-label) {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-compact-field .ant-input-lg),
|
|
|
|
|
|
.sms-panel-page :deep(.sms-compact-field .hr-tamin-input .ant-input-lg) {
|
|
|
|
|
|
padding-top: 0.375rem;
|
|
|
|
|
|
padding-bottom: 0.375rem;
|
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2) {
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full) {
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.flex-col) {
|
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.25rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.justify-between.items-center.gap-2) {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
min-width: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.justify-between.items-center.gap-2 > .flex.md\:hidden) {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .w-full > .flex.flex-col > .block.md\:hidden) {
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .hr-tamin-base-table .flex.items-center.justify-between.mb-2 > .hidden.md\:block) {
|
|
|
|
|
|
display: none !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .ant-affix) {
|
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table .ant-affix > div) {
|
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.sms-panel-table-tabs .ant-tabs-content) {
|
|
|
|
|
|
margin-right: 0 !important;
|
|
|
|
|
|
margin-left: 0 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card:not(:has(.base-table-card-header))) {
|
|
|
|
|
|
padding: 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .space-y-3) {
|
|
|
|
|
|
gap: 0.625rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card) {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
border-radius: 0.875rem;
|
|
|
|
|
|
border-color: var(--admin-border);
|
|
|
|
|
|
box-shadow: 0 1px 3px var(--admin-shadow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-mobile-card:hover) {
|
|
|
|
|
|
box-shadow: 0 2px 8px var(--admin-shadow);
|
|
|
|
|
|
transform: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-header) {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0.625rem 0.875rem;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-bottom: 1px solid var(--admin-border);
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: var(--admin-bg-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-row-badge) {
|
|
|
|
|
|
height: 1.625rem;
|
|
|
|
|
|
width: 1.625rem;
|
|
|
|
|
|
background: var(--admin-gold-light);
|
|
|
|
|
|
color: var(--admin-menu-selected-text);
|
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
box-shadow: 0 1px 2px rgb(212 175 55 / 0.35);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-header-value) {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2) {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0.75rem 0.875rem;
|
|
|
|
|
|
gap: 0.5rem 0.875rem;
|
|
|
|
|
|
background: var(--admin-bg-elevated);
|
|
|
|
|
|
border-bottom: 1px solid var(--admin-border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 > div) {
|
|
|
|
|
|
min-height: 1.125rem;
|
|
|
|
|
|
padding: 0.25rem 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 .base-table-card-grid-label) {
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
|
color: var(--admin-text-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-grid-2 .base-table-card-grid-value) {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--admin-text);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked) {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-box) {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0.75rem 0.875rem 0.875rem;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
background: var(--admin-bg-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-label) {
|
|
|
|
|
|
margin: 0 0 0.5rem;
|
|
|
|
|
|
padding-bottom: 0.375rem;
|
|
|
|
|
|
border-bottom: 1px dashed var(--admin-border);
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
font-size: 0.6875rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--admin-text-secondary);
|
|
|
|
|
|
letter-spacing: 0.01em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .base-table-card-stacked-value) {
|
|
|
|
|
|
margin-top: 0 !important;
|
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
|
color: var(--admin-text-muted);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .group\/row.mb-2) {
|
|
|
|
|
|
margin-bottom: 0.125rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .group\/row .flex) {
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .w-1\/3) {
|
|
|
|
|
|
width: 30%;
|
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
|
line-height: 1.2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-panel-page :deep(.block.md\:hidden .space-y-3 > div .text-sm) {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="less">
|
|
|
|
|
|
html.is-dark {
|
|
|
|
|
|
.sms-panel-page {
|
|
|
|
|
|
.sms-stat-value--ok,
|
|
|
|
|
|
.sms-stat-value--emerald {
|
|
|
|
|
|
color: #6ee7b7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--warn {
|
|
|
|
|
|
color: #fcd34d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--danger {
|
|
|
|
|
|
color: #fda4af;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--sky {
|
|
|
|
|
|
color: #7dd3fc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-value--violet {
|
|
|
|
|
|
color: #c4b5fd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--ok,
|
|
|
|
|
|
.sms-stat-icon--emerald {
|
|
|
|
|
|
color: #6ee7b7;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--warn {
|
|
|
|
|
|
color: #fcd34d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--danger {
|
|
|
|
|
|
color: #fda4af;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--sky {
|
|
|
|
|
|
color: #7dd3fc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.sms-stat-icon--violet {
|
|
|
|
|
|
color: #c4b5fd;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|