added rahimi dashboard
This commit is contained in:
@@ -5,6 +5,7 @@ import type {
|
||||
DashboardFinance,
|
||||
DashboardMaster,
|
||||
DashboardOrderPeakHours,
|
||||
MontzarReconcilePayload,
|
||||
PriceChartPoint,
|
||||
ProfitConversion,
|
||||
ProfitConversionsResponse,
|
||||
@@ -70,3 +71,9 @@ export const storeProfitConversion = async (payload: {
|
||||
|
||||
export const deleteProfitConversion = async (id: number) =>
|
||||
await useAxios.delete(`/admin/dashboard/profit-conversions/${id}`)
|
||||
|
||||
export const getMontzarReconcile = async (startDate: string, endDate: string) =>
|
||||
await useAxios.post<MontzarReconcilePayload>('/admin/dashboard/montzar-reconcile', {
|
||||
startDate: startDate.slice(0, 10),
|
||||
endDate: endDate.slice(0, 10),
|
||||
})
|
||||
|
||||
@@ -20,6 +20,8 @@ html {
|
||||
--admin-shadow: rgba(0, 0, 0, 0.06);
|
||||
--admin-menu-selected: @gold-color-muted;
|
||||
--admin-menu-selected-text: @gold-color-dark;
|
||||
/* متن روی دکمه/سطح طلایی — سفید روی طلا خوانا نیست */
|
||||
--admin-on-primary: #141414;
|
||||
--admin-gradient-surface: #ffffff;
|
||||
--admin-gradient-surface-fade: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
@@ -44,6 +46,7 @@ html.is-dark {
|
||||
--admin-shadow: rgba(0, 0, 0, 0.45);
|
||||
--admin-menu-selected: @gold-color-light;
|
||||
--admin-menu-selected-text: #141414;
|
||||
--admin-on-primary: #141414;
|
||||
--admin-gradient-surface: #141414;
|
||||
--admin-gradient-surface-fade: rgba(20, 20, 20, 0.85);
|
||||
|
||||
@@ -70,20 +73,24 @@ body {
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
.ant-btn-primary:not(.ant-btn-dangerous) {
|
||||
background: var(--admin-gold-light) !important;
|
||||
border-color: var(--admin-gold-light) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
.ant-btn-primary:not([disabled]):hover,
|
||||
.ant-btn-primary:not([disabled]):focus {
|
||||
.ant-btn-primary:not(.ant-btn-dangerous):not([disabled]):hover,
|
||||
.ant-btn-primary:not(.ant-btn-dangerous):not([disabled]):focus {
|
||||
background: var(--admin-gold-hover) !important;
|
||||
border-color: var(--admin-gold-hover) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
}
|
||||
|
||||
.ant-btn-primary:not([disabled]):active {
|
||||
.ant-btn-primary:not(.ant-btn-dangerous):not([disabled]):active {
|
||||
background: var(--admin-gold) !important;
|
||||
border-color: var(--admin-gold) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
}
|
||||
|
||||
.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item-selected,
|
||||
|
||||
@@ -80,13 +80,13 @@ html.is-dark .ant-picker-focused {
|
||||
box-shadow: 0 0 0 2px rgba(212, 175, 55, 0.18) !important;
|
||||
}
|
||||
html.is-dark .ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) {
|
||||
color: var(--admin-menu-selected-text) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
background: var(--admin-gold-light) !important;
|
||||
border-color: var(--admin-gold-light) !important;
|
||||
}
|
||||
html.is-dark .ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):hover,
|
||||
html.is-dark .ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):active {
|
||||
color: var(--admin-menu-selected-text) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
background: var(--admin-gold-hover) !important;
|
||||
border-color: var(--admin-gold-hover) !important;
|
||||
}
|
||||
@@ -105,14 +105,17 @@ html.is-dark .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-dis
|
||||
html.is-dark .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before {
|
||||
background-color: var(--admin-gold-light) !important;
|
||||
}
|
||||
html.is-dark .ant-btn-primary {
|
||||
html.is-dark .ant-btn-primary:not(.ant-btn-dangerous) {
|
||||
background: var(--admin-gold-light) !important;
|
||||
border-color: var(--admin-gold-light) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
html.is-dark .ant-btn-primary:not([disabled]):hover,
|
||||
html.is-dark .ant-btn-primary:not([disabled]):focus {
|
||||
html.is-dark .ant-btn-primary:not(.ant-btn-dangerous):not([disabled]):hover,
|
||||
html.is-dark .ant-btn-primary:not(.ant-btn-dangerous):not([disabled]):focus {
|
||||
background: var(--admin-gold-hover) !important;
|
||||
border-color: var(--admin-gold-hover) !important;
|
||||
color: var(--admin-on-primary) !important;
|
||||
}
|
||||
html.is-dark .ant-btn-default:not([disabled]):hover,
|
||||
html.is-dark .ant-btn-default:not([disabled]):focus {
|
||||
|
||||
@@ -143,3 +143,100 @@ export interface DashboardWalletSums {
|
||||
cash?: number
|
||||
cashToCash?: number
|
||||
}
|
||||
|
||||
export interface MontzarReconcileMandeh {
|
||||
gold: number
|
||||
cash: number
|
||||
gold_abs: number
|
||||
cash_abs: number
|
||||
gold_state: string
|
||||
rial_state: string
|
||||
gold_unit: string
|
||||
cash_unit: string
|
||||
}
|
||||
|
||||
export interface MontzarSiteSummary {
|
||||
buy_gold: number
|
||||
sell_gold: number
|
||||
buy_rial: number
|
||||
sell_rial: number
|
||||
net_gold: number
|
||||
net_rial: number
|
||||
}
|
||||
|
||||
export interface MontzarColleagueRow {
|
||||
index?: number
|
||||
id?: number | string | null
|
||||
doc_id?: number | string | null
|
||||
date?: string | null
|
||||
date_ymd?: string | null
|
||||
desc?: string
|
||||
kind?: string
|
||||
bed_gold?: number
|
||||
bes_gold?: number
|
||||
bed_rial?: number
|
||||
bes_rial?: number
|
||||
net_gold?: number
|
||||
net_rial?: number
|
||||
remain_gold?: number
|
||||
remain_rial?: number
|
||||
gold_state?: string | null
|
||||
rial_state?: string | null
|
||||
}
|
||||
|
||||
export interface MontzarLocalItem {
|
||||
id: number
|
||||
type: string
|
||||
direction?: number
|
||||
direction_label?: string
|
||||
amount: number
|
||||
cash: number
|
||||
montzar_cash?: number
|
||||
covered?: boolean
|
||||
provider_id?: number | string | null
|
||||
product_id?: number | string | null
|
||||
product_title?: string | null
|
||||
customer_name?: string | null
|
||||
note?: string
|
||||
created_at?: string | null
|
||||
date_ymd?: string | null
|
||||
}
|
||||
|
||||
export interface MontzarReconcileDiff {
|
||||
code: string
|
||||
reason: string
|
||||
colleague?: MontzarColleagueRow | null
|
||||
local?: MontzarLocalItem | null
|
||||
}
|
||||
|
||||
export interface MontzarReconcilePayload {
|
||||
mandeh: MontzarReconcileMandeh | null
|
||||
colleague_available?: boolean
|
||||
colleague_error?: string | null
|
||||
last_table_remain?: { gold: number; rial: number } | null
|
||||
summary: {
|
||||
local_all: MontzarSiteSummary
|
||||
local_covered: MontzarSiteSummary
|
||||
local_uncovered: MontzarSiteSummary
|
||||
local_matched: MontzarSiteSummary
|
||||
colleague_site: MontzarSiteSummary
|
||||
diff_site_gold: number
|
||||
diff_site_rial: number
|
||||
order_count: number
|
||||
covered_count: number
|
||||
uncovered_count: number
|
||||
matched_count: number
|
||||
diff_count: number
|
||||
gift_count?: number
|
||||
colleague_row_count: number
|
||||
local_order_count: number
|
||||
}
|
||||
colleague_rows: MontzarColleagueRow[]
|
||||
local_orders: MontzarLocalItem[]
|
||||
local_uncovered_orders?: MontzarLocalItem[]
|
||||
local_gifts: MontzarLocalItem[]
|
||||
matched: Array<{ code: string; colleague: MontzarColleagueRow; local: MontzarLocalItem }>
|
||||
diffs: MontzarReconcileDiff[]
|
||||
colleague_other?: MontzarReconcileDiff[]
|
||||
range: { start: string; end: string }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
<template>
|
||||
<div class="income-mix">
|
||||
<DashboardSectionHeader title="ترکیب درآمد" :help="help" />
|
||||
|
||||
<div v-if="total <= 0" class="income-mix__empty">
|
||||
در این بازه داده مالی ثبت نشده
|
||||
</div>
|
||||
|
||||
<div v-else class="income-mix__body">
|
||||
<div class="income-mix__chart">
|
||||
<div class="income-mix__donut">
|
||||
<apexchart
|
||||
type="donut"
|
||||
width="100%"
|
||||
height="168"
|
||||
:options="chartOptions"
|
||||
:series="chartSeries"
|
||||
/>
|
||||
<div class="income-mix__center">
|
||||
<span class="income-mix__center-label">جمع سود</span>
|
||||
<span class="income-mix__center-amount" dir="ltr">
|
||||
<bdi>{{ compactTotal }}</bdi>
|
||||
</span>
|
||||
<span class="income-mix__center-unit">{{ DASHBOARD_RIAL_UNIT }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="income-mix__list">
|
||||
<li v-for="row in rows" :key="row.key" class="income-mix__row">
|
||||
<span class="income-mix__dot" :style="{ background: row.color }" />
|
||||
<div class="income-mix__meta">
|
||||
<div class="income-mix__name-row">
|
||||
<span class="income-mix__name">{{ row.label }}</span>
|
||||
<span class="income-mix__pct-pill" :style="{ color: row.color, background: row.tint }">
|
||||
{{ row.pctLabel }}
|
||||
</span>
|
||||
</div>
|
||||
<span class="income-mix__hint">{{ row.hint }}</span>
|
||||
<span class="income-mix__amount" dir="ltr">
|
||||
<bdi>{{ formatDashboardAmount(row.value) }}</bdi>
|
||||
<span class="income-mix__unit">{{ DASHBOARD_RIAL_UNIT }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
||||
import { dashboardCharts } from '@/pages/dashboard/dashboardSectionCopy'
|
||||
import { DASHBOARD_RIAL_UNIT, formatDashboardAmount } from '@/pages/dashboard/dashboardFormat'
|
||||
import { useAdminTheme } from '@core/composable/useAdminTheme'
|
||||
import type { DashboardFinance } from '@/model/dashboard'
|
||||
|
||||
const props = defineProps<{
|
||||
finance?: DashboardFinance | null
|
||||
}>()
|
||||
|
||||
const { isDark } = useAdminTheme()
|
||||
const help = dashboardCharts.incomeMix
|
||||
|
||||
const sood = computed(() => Number(props.finance?.fullPriceSood ?? 0))
|
||||
const karmozd = computed(
|
||||
() => Number(props.finance?.fullPriceKarmozd ?? props.finance?.fullPriceKaermozd ?? 0),
|
||||
)
|
||||
const total = computed(() => sood.value + karmozd.value)
|
||||
|
||||
function tint(hex: string, dark: boolean) {
|
||||
return dark ? `${hex}33` : `${hex}1a`
|
||||
}
|
||||
|
||||
const rows = computed(() => {
|
||||
const t = total.value
|
||||
const dark = isDark.value
|
||||
const items = [
|
||||
{
|
||||
key: 'sood',
|
||||
label: 'سود اختلاف قیمت',
|
||||
hint: 'فاصله مظنه و قیمت مشتری',
|
||||
value: sood.value,
|
||||
color: '#10b981',
|
||||
},
|
||||
{
|
||||
key: 'fee',
|
||||
label: 'کارمزد معامله',
|
||||
hint: 'کارمزد گرفتهشده از سفارشها',
|
||||
value: karmozd.value,
|
||||
color: '#f59e0b',
|
||||
},
|
||||
]
|
||||
|
||||
return items.map((item) => {
|
||||
const pct = t > 0 ? Math.round((item.value / t) * 1000) / 10 : 0
|
||||
return {
|
||||
...item,
|
||||
pct,
|
||||
pctLabel: `${pct.toLocaleString('fa-IR')}٪`,
|
||||
tint: tint(item.color, dark),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const chartSeries = computed(() => rows.value.map((r) => r.value))
|
||||
|
||||
const compactTotal = computed(() => {
|
||||
const n = total.value
|
||||
if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`
|
||||
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
|
||||
return formatDashboardAmount(n)
|
||||
})
|
||||
|
||||
const chartOptions = computed(() => {
|
||||
const dark = isDark.value
|
||||
return {
|
||||
chart: {
|
||||
type: 'donut',
|
||||
fontFamily: 'inherit',
|
||||
foreColor: dark ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)',
|
||||
animations: { enabled: true, speed: 450 },
|
||||
},
|
||||
theme: { mode: dark ? 'dark' : 'light' },
|
||||
labels: rows.value.map((r) => r.label),
|
||||
colors: rows.value.map((r) => r.color),
|
||||
stroke: { width: 0 },
|
||||
legend: { show: false },
|
||||
dataLabels: { enabled: false },
|
||||
plotOptions: {
|
||||
pie: {
|
||||
expandOnClick: false,
|
||||
donut: {
|
||||
size: '74%',
|
||||
labels: { show: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: dark ? 'dark' : 'light',
|
||||
y: {
|
||||
formatter: (v: number) => `${formatDashboardAmount(v)} ${DASHBOARD_RIAL_UNIT}`,
|
||||
},
|
||||
},
|
||||
states: {
|
||||
hover: { filter: { type: 'lighten', value: 0.08 } },
|
||||
active: { filter: { type: 'none' } },
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.income-mix {
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.income-mix__empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 11rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.income-mix__body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(140px, 168px) minmax(0, 1fr);
|
||||
gap: 0.75rem 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.income-mix__body {
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.income-mix__list {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.income-mix__chart {
|
||||
width: 100%;
|
||||
max-width: 168px;
|
||||
}
|
||||
|
||||
.income-mix__donut {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.income-mix__donut :deep(.apexcharts-canvas) {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.income-mix__center {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
padding: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.income-mix__center-label {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
color: var(--admin-text-secondary);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.income-mix__center-amount {
|
||||
margin-top: 0.15rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--admin-text);
|
||||
line-height: 1.2;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.income-mix__center-unit {
|
||||
margin-top: 0.1rem;
|
||||
font-size: 0.5625rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.income-mix__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.income-mix__row {
|
||||
display: grid;
|
||||
grid-template-columns: 0.55rem minmax(0, 1fr);
|
||||
gap: 0.55rem;
|
||||
align-items: start;
|
||||
padding: 0.55rem 0.6rem;
|
||||
border-radius: 0.6rem;
|
||||
border: 1px solid var(--admin-border);
|
||||
background: var(--admin-bg-muted);
|
||||
}
|
||||
|
||||
.income-mix__dot {
|
||||
width: 0.55rem;
|
||||
height: 0.55rem;
|
||||
margin-top: 0.3rem;
|
||||
border-radius: 999px;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 12%, transparent);
|
||||
}
|
||||
|
||||
.income-mix__meta {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.income-mix__name-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.income-mix__name {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.income-mix__pct-pill {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 999px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.income-mix__hint {
|
||||
font-size: 0.625rem;
|
||||
line-height: 1.4;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.income-mix__amount {
|
||||
margin-top: 0.1rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.income-mix__unit {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,584 @@
|
||||
<template>
|
||||
<section v-if="visible" class="montzar-simple">
|
||||
<DashboardSectionHeader title="آبشده و همکار" :help="sectionHelp" />
|
||||
|
||||
<a-spin :spinning="loading">
|
||||
<div v-if="error" class="montzar-simple__error">
|
||||
{{ error }}
|
||||
<a-button class="mt-2" size="small" @click="load">تلاش مجدد</a-button>
|
||||
</div>
|
||||
|
||||
<template v-else-if="data">
|
||||
<div class="montzar-simple__grid">
|
||||
<article class="montzar-simple__card">
|
||||
<span class="montzar-simple__label">خرید مشتری</span>
|
||||
<p class="montzar-simple__hint">آبشده در این بازه</p>
|
||||
<p class="montzar-simple__value montzar-simple__value--inline">
|
||||
<bdi class="montzar-simple__amount number-ltr">{{ formatGram(data.summary.local_all.buy_gold) }}</bdi>
|
||||
<span class="montzar-simple__unit">گرم</span>
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article class="montzar-simple__card">
|
||||
<span class="montzar-simple__label">فروش مشتری</span>
|
||||
<p class="montzar-simple__hint">آبشده در این بازه</p>
|
||||
<p class="montzar-simple__value montzar-simple__value--inline">
|
||||
<bdi class="montzar-simple__amount number-ltr">{{ formatGram(data.summary.local_all.sell_gold) }}</bdi>
|
||||
<span class="montzar-simple__unit">گرم</span>
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<article class="montzar-simple__card">
|
||||
<span class="montzar-simple__label">ثبتشده نزد همکار</span>
|
||||
<p class="montzar-simple__hint">از همکار خرید یا فروش شده</p>
|
||||
<div class="montzar-simple__value montzar-simple__value--stack">
|
||||
<p class="montzar-simple__ratio">
|
||||
<bdi class="montzar-simple__amount">{{ data.summary.covered_count }}</bdi>
|
||||
<span class="montzar-simple__sep">از</span>
|
||||
<bdi class="montzar-simple__amount montzar-simple__amount--muted">{{ data.summary.order_count }}</bdi>
|
||||
</p>
|
||||
<span class="montzar-simple__unit">سفارش</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article
|
||||
class="montzar-simple__card"
|
||||
:class="data.summary.uncovered_count ? 'montzar-simple__card--bad' : 'montzar-simple__card--ok'"
|
||||
>
|
||||
<span class="montzar-simple__label">
|
||||
{{ data.summary.uncovered_count ? 'ثبتنشده نزد همکار' : 'ثبتنشده نزد همکار' }}
|
||||
</span>
|
||||
<p class="montzar-simple__hint">
|
||||
{{
|
||||
data.summary.uncovered_count
|
||||
? 'در اپ هست ولی از همکار خرید/فروش نشده'
|
||||
: 'همه سفارشها نزد همکار ثبت شدهاند'
|
||||
}}
|
||||
</p>
|
||||
<div class="montzar-simple__value montzar-simple__value--stack">
|
||||
<p class="montzar-simple__ratio">
|
||||
<bdi class="montzar-simple__amount">{{ data.summary.uncovered_count }}</bdi>
|
||||
<span class="montzar-simple__unit">سفارش</span>
|
||||
</p>
|
||||
<p v-if="uncoveredGold > 0" class="montzar-simple__sub-line">
|
||||
<bdi class="number-ltr">{{ formatGram(uncoveredGold) }}</bdi>
|
||||
<span>گرم</span>
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="montzar-simple__actions">
|
||||
<a-button
|
||||
v-if="data.summary.uncovered_count"
|
||||
type="primary"
|
||||
size="small"
|
||||
danger
|
||||
@click="openTab('uncovered')"
|
||||
>
|
||||
ببین کدام سفارشها نزد همکار ثبت نشده
|
||||
</a-button>
|
||||
<a-button size="small" @click="openTab('orders')">همه سفارشها</a-button>
|
||||
<a-button size="small" type="link" class="!px-1" @click="showMore = !showMore">
|
||||
{{ showMore ? 'بستن جزئیات' : 'جزئیات بیشتر' }}
|
||||
</a-button>
|
||||
<a-button size="small" @click="load">بروزرسانی</a-button>
|
||||
</div>
|
||||
|
||||
<div v-if="showMore" class="montzar-simple__more">
|
||||
<p v-if="data.mandeh" class="montzar-simple__more-line">
|
||||
مانده حساب نزد همکار الان:
|
||||
<b class="number-ltr" dir="ltr">
|
||||
{{ formatGram(data.mandeh.gold_abs) }} گرم ·
|
||||
{{ formatDashboardAmount(data.mandeh.cash_abs) }} ریال
|
||||
</b>
|
||||
({{ data.mandeh.gold_state }} / {{ data.mandeh.rial_state }})
|
||||
</p>
|
||||
<p v-if="data.colleague_available" class="montzar-simple__more-line">
|
||||
اختلاف گرم دفتر همکار با سفارشهای ثبتشده این بازه:
|
||||
<b class="number-ltr" dir="ltr">{{ formatSignedGram(data.summary.diff_site_gold) }} گرم</b>
|
||||
</p>
|
||||
<div class="montzar-simple__actions mt-1">
|
||||
<a-button
|
||||
v-if="data.summary.diff_count"
|
||||
size="small"
|
||||
danger
|
||||
@click="openTab('diffs')"
|
||||
>
|
||||
موارد اختلاف ({{ data.summary.diff_count }})
|
||||
</a-button>
|
||||
<a-button
|
||||
v-if="data.colleague_available"
|
||||
size="small"
|
||||
@click="openTab('colleague')"
|
||||
>
|
||||
ریز دفتر همکار
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</a-spin>
|
||||
|
||||
<BasicModal
|
||||
v-model:visible="modalOpen"
|
||||
:title="modalTitle"
|
||||
width="920px"
|
||||
:custom-footer="true"
|
||||
@close="modalOpen = false"
|
||||
>
|
||||
<div v-if="activeTab === 'uncovered'" class="overflow-x-auto">
|
||||
<p class="montzar-simple__modal-intro">
|
||||
این سفارشها در اپ ثبت شدهاند ولی از همکار خرید یا فروش نشدهاند.
|
||||
</p>
|
||||
<table class="montzar-simple__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>شناسه</th>
|
||||
<th>زمان</th>
|
||||
<th>خرید/فروش</th>
|
||||
<th>مشتری</th>
|
||||
<th>گرم</th>
|
||||
<th>ریال</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in uncoveredOrders" :key="`u-${row.id}`">
|
||||
<td>{{ row.id }}</td>
|
||||
<td class="whitespace-nowrap">{{ row.created_at }}</td>
|
||||
<td>{{ row.direction_label }}</td>
|
||||
<td>{{ row.customer_name || '—' }}</td>
|
||||
<td class="number-ltr" dir="ltr">{{ formatGram(row.amount) }}</td>
|
||||
<td class="number-ltr" dir="ltr">{{ formatDashboardAmount(row.cash) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="!uncoveredOrders.length" class="montzar-simple__empty">موردی نیست</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeTab === 'orders'" class="overflow-x-auto">
|
||||
<table class="montzar-simple__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>شناسه</th>
|
||||
<th>زمان</th>
|
||||
<th>خرید/فروش</th>
|
||||
<th>مشتری</th>
|
||||
<th>گرم</th>
|
||||
<th>نزد همکار؟</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in data?.local_orders ?? []" :key="`o-${row.id}`">
|
||||
<td>{{ row.id }}</td>
|
||||
<td class="whitespace-nowrap">{{ row.created_at }}</td>
|
||||
<td>{{ row.direction_label }}</td>
|
||||
<td>{{ row.customer_name || '—' }}</td>
|
||||
<td class="number-ltr" dir="ltr">{{ formatGram(row.amount) }}</td>
|
||||
<td>{{ row.covered ? 'بله' : 'خیر' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeTab === 'colleague'" class="overflow-x-auto">
|
||||
<table class="montzar-simple__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>تاریخ</th>
|
||||
<th>شرح</th>
|
||||
<th>نوع</th>
|
||||
<th>طلا</th>
|
||||
<th>ریال</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, i) in data?.colleague_rows ?? []"
|
||||
:key="`c-${row.id ?? i}`"
|
||||
>
|
||||
<td class="whitespace-nowrap">{{ row.date }}</td>
|
||||
<td>{{ row.desc || '—' }}</td>
|
||||
<td>{{ kindLabel(row.kind) }}</td>
|
||||
<td class="number-ltr" dir="ltr">{{ formatSignedGram(row.net_gold ?? 0) }}</td>
|
||||
<td class="number-ltr" dir="ltr">{{ formatSignedAmount(row.net_rial ?? 0) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="(diff, i) in data?.diffs ?? []"
|
||||
:key="`d-${i}`"
|
||||
class="montzar-simple__diff"
|
||||
>
|
||||
<b>{{ diffCodeLabel(diff.code) }}</b>
|
||||
<p class="m-0 mt-1">{{ diff.reason }}</p>
|
||||
<div v-if="diff.local" class="montzar-simple__diff-meta">
|
||||
سفارش اپ شماره {{ diff.local.id }} · {{ formatGram(diff.local.amount) }} گرم
|
||||
</div>
|
||||
<div v-if="diff.colleague" class="montzar-simple__diff-meta">
|
||||
دفتر همکار: {{ diff.colleague.desc || '—' }} ·
|
||||
{{ formatSignedGram(diff.colleague.net_gold ?? 0) }} گرم
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!(data?.diffs?.length)" class="montzar-simple__empty">مورد اختلافی نیست</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<a-button @click="modalOpen = false">بستن</a-button>
|
||||
</template>
|
||||
</BasicModal>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import BasicModal from '@/components/BasicModal.vue'
|
||||
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
||||
import { getMontzarReconcile } from '@/api/dashboard'
|
||||
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
||||
import { formatDashboardAmount } from '@/pages/dashboard/dashboardFormat'
|
||||
import type { MontzarLocalItem, MontzarReconcilePayload } from '@/model/dashboard'
|
||||
import { priceProviderSetting } from '@/utils/config'
|
||||
|
||||
const props = defineProps<{
|
||||
startDate: string
|
||||
endDate: string
|
||||
}>()
|
||||
|
||||
const visible = computed(() => priceProviderSetting.value === 'montzar')
|
||||
const loading = ref(false)
|
||||
const error = ref('')
|
||||
const data = ref<MontzarReconcilePayload | null>(null)
|
||||
const modalOpen = ref(false)
|
||||
const showMore = ref(false)
|
||||
const activeTab = ref<'uncovered' | 'orders' | 'colleague' | 'diffs'>('uncovered')
|
||||
|
||||
const sectionHelp: DashboardHelp = {
|
||||
description:
|
||||
'هر خرید یا فروش آبشده مشتری باید نزد همکار هم ثبت شود. اینجا میبینید چند تا ثبت شده و چند تا جا مانده.',
|
||||
formula: 'ثبتنشده = سفارش موفق آبشده بدون شناسه ثبت نزد همکار',
|
||||
}
|
||||
|
||||
const uncoveredGold = computed(() => {
|
||||
const u = data.value?.summary.local_uncovered
|
||||
if (!u) return 0
|
||||
return Number(u.buy_gold || 0) + Number(u.sell_gold || 0)
|
||||
})
|
||||
|
||||
const uncoveredOrders = computed<MontzarLocalItem[]>(() => {
|
||||
if (data.value?.local_uncovered_orders?.length) {
|
||||
return data.value.local_uncovered_orders
|
||||
}
|
||||
return (data.value?.local_orders ?? []).filter((o) => !o.covered)
|
||||
})
|
||||
|
||||
const modalTitle = computed(() => {
|
||||
if (activeTab.value === 'uncovered') return 'سفارشهایی که نزد همکار ثبت نشدهاند'
|
||||
if (activeTab.value === 'orders') return 'همه خرید و فروش آبشده'
|
||||
if (activeTab.value === 'diffs') return 'موارد اختلاف'
|
||||
return 'ریز دفتر همکار'
|
||||
})
|
||||
|
||||
function formatGram(value?: number) {
|
||||
return Number(value ?? 0).toLocaleString('en-US', { maximumFractionDigits: 3 })
|
||||
}
|
||||
|
||||
function formatSignedGram(value?: number) {
|
||||
const n = Number(value ?? 0)
|
||||
return `${n > 0 ? '+' : ''}${formatGram(n)}`
|
||||
}
|
||||
|
||||
function formatSignedAmount(value?: number) {
|
||||
const n = Math.round(Number(value ?? 0))
|
||||
return `${n > 0 ? '+' : ''}${formatDashboardAmount(Math.abs(n))}`
|
||||
}
|
||||
|
||||
function kindLabel(kind?: string) {
|
||||
const map: Record<string, string> = {
|
||||
site_sell: 'فروش از سایت',
|
||||
site_buy: 'خرید از سایت',
|
||||
settlement: 'تسویه',
|
||||
physical: 'فیزیکی',
|
||||
gift: 'پاداش',
|
||||
other: 'سایر',
|
||||
}
|
||||
return map[kind ?? ''] ?? kind ?? '—'
|
||||
}
|
||||
|
||||
function diffCodeLabel(code: string) {
|
||||
const map: Record<string, string> = {
|
||||
local_uncovered: 'در اپ هست، نزد همکار ثبت نشده',
|
||||
colleague_site_only: 'فقط در دفتر همکار هست',
|
||||
local_covered_not_in_ledger: 'نزد همکار ثبت شده، در دفتر بازه نیست',
|
||||
gift_uncovered: 'پاداش بدون ثبت نزد همکار',
|
||||
}
|
||||
return map[code] ?? code
|
||||
}
|
||||
|
||||
function openTab(tab: 'uncovered' | 'orders' | 'colleague' | 'diffs') {
|
||||
activeTab.value = tab
|
||||
modalOpen.value = true
|
||||
}
|
||||
|
||||
async function load() {
|
||||
if (!visible.value) {
|
||||
data.value = null
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
try {
|
||||
data.value = (await getMontzarReconcile(props.startDate, props.endDate)) as MontzarReconcilePayload
|
||||
} catch (e: unknown) {
|
||||
data.value = null
|
||||
error.value = String(
|
||||
(e as { response?: { data?: { message?: string } }; message?: string })?.response?.data
|
||||
?.message
|
||||
|| (e as { message?: string })?.message
|
||||
|| 'خطا در محاسبه'
|
||||
)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [visible.value, props.startDate, props.endDate] as const,
|
||||
() => {
|
||||
void load()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
defineExpose({ load })
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.montzar-simple {
|
||||
margin-top: 0.5rem;
|
||||
border-radius: 0.75rem;
|
||||
border: 1px solid var(--admin-border);
|
||||
background: var(--admin-bg-elevated);
|
||||
padding: 0.65rem 0.75rem 0.75rem;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.montzar-simple__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.montzar-simple__grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.montzar-simple__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.montzar-simple__card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
min-height: 4.5rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid var(--admin-border);
|
||||
background: var(--admin-bg-muted);
|
||||
}
|
||||
|
||||
.montzar-simple__card--bad {
|
||||
border-color: rgba(244, 63, 94, 0.45);
|
||||
background: rgba(244, 63, 94, 0.1);
|
||||
}
|
||||
|
||||
.montzar-simple__card--ok {
|
||||
border-color: rgba(16, 185, 129, 0.4);
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
|
||||
.montzar-simple__label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--admin-text);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.montzar-simple__hint {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
font-size: 0.625rem;
|
||||
line-height: 1.45;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.montzar-simple__value {
|
||||
margin: 0.15rem 0 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.montzar-simple__value--inline {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
justify-content: flex-start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.montzar-simple__value--stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.1rem;
|
||||
}
|
||||
|
||||
.montzar-simple__ratio {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.montzar-simple__sep {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.montzar-simple__amount {
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.montzar-simple__amount--muted {
|
||||
font-weight: 700;
|
||||
color: var(--admin-text-muted);
|
||||
}
|
||||
|
||||
.montzar-simple__card--bad .montzar-simple__amount {
|
||||
color: #e11d48;
|
||||
}
|
||||
|
||||
.montzar-simple__card--ok .montzar-simple__amount {
|
||||
color: #059669;
|
||||
}
|
||||
|
||||
.montzar-simple__unit {
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.montzar-simple__sub-line {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.2rem;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-text-secondary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.montzar-simple__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin-top: 0.65rem;
|
||||
}
|
||||
|
||||
.montzar-simple__more {
|
||||
margin-top: 0.5rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px dashed var(--admin-border);
|
||||
}
|
||||
|
||||
.montzar-simple__more-line {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--admin-text-secondary);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.montzar-simple__more-line b {
|
||||
color: var(--admin-text);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.montzar-simple__error {
|
||||
padding: 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #e11d48;
|
||||
}
|
||||
|
||||
.montzar-simple__empty {
|
||||
padding: 1.5rem 0;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.montzar-simple__modal-intro {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 12px;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
|
||||
.montzar-simple__table {
|
||||
width: 100%;
|
||||
min-width: 520px;
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
color: var(--admin-text);
|
||||
}
|
||||
|
||||
.montzar-simple__table th,
|
||||
.montzar-simple__table td {
|
||||
padding: 0.4rem 0.5rem;
|
||||
border-bottom: 1px solid var(--admin-border);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.montzar-simple__table th {
|
||||
color: var(--admin-text-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.montzar-simple__diff {
|
||||
border: 1px solid var(--admin-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.6rem 0.75rem;
|
||||
font-size: 12px;
|
||||
background: var(--admin-bg-muted);
|
||||
}
|
||||
|
||||
.montzar-simple__diff-meta {
|
||||
margin-top: 0.25rem;
|
||||
font-size: 11px;
|
||||
color: var(--admin-text-secondary);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
html.is-dark {
|
||||
.montzar-simple__card--bad .montzar-simple__amount {
|
||||
color: #fda4af;
|
||||
}
|
||||
|
||||
.montzar-simple__card--ok .montzar-simple__amount {
|
||||
color: #6ee7b7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -275,47 +275,6 @@ export function useDashboardPage() {
|
||||
?? (order.sts === 2 ? 'موفق' : order.sts === 1 ? 'باز' : 'ناموفق')
|
||||
}
|
||||
|
||||
const financeChartOptions = computed(() => {
|
||||
const sood = finance.value?.fullPriceSood ?? 0
|
||||
const karmozd = finance.value?.fullPriceKarmozd ?? finance.value?.fullPriceKaermozd ?? 0
|
||||
const gifts = finance.value?.GiftsPrise ?? 0
|
||||
const total = finance.value?.totalTradingProfit ?? sood + karmozd
|
||||
|
||||
return {
|
||||
...chartSurfaceOptions(),
|
||||
chart: { type: 'donut', fontFamily: 'inherit', offsetY: 4, foreColor: chartSurfaceOptions().chart.foreColor },
|
||||
labels: ['سود اختلاف قیمت', 'کارمزد معامله', 'ارزش هدایا (گرم)'],
|
||||
colors: ['#10b981', '#f59e0b', '#8b5cf6'],
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
fontSize: '12px',
|
||||
horizontalAlign: 'center',
|
||||
offsetY: 6,
|
||||
itemMargin: { horizontal: 12, vertical: 4 },
|
||||
},
|
||||
dataLabels: { enabled: false },
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: '68%',
|
||||
labels: {
|
||||
show: true,
|
||||
total: {
|
||||
show: true,
|
||||
label: 'جمع سود معاملات',
|
||||
formatter: () => `${formatDashboardAmount(total)} ${DASHBOARD_RIAL_UNIT}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
y: { formatter: (v: number) => `${formatDashboardAmount(v)} ${DASHBOARD_RIAL_UNIT}` },
|
||||
},
|
||||
series: [sood, karmozd, gifts],
|
||||
}
|
||||
})
|
||||
|
||||
const flowChartOptions = computed(() => {
|
||||
const mobile = isMobile.value
|
||||
const surface = chartSurfaceOptions()
|
||||
@@ -628,7 +587,6 @@ export function useDashboardPage() {
|
||||
directionClass,
|
||||
stsClass,
|
||||
stsLabel,
|
||||
financeChartOptions,
|
||||
flowChartOptions,
|
||||
flowChartHeight,
|
||||
peakHoursChartOptions,
|
||||
|
||||
@@ -88,8 +88,8 @@ export const dashboardDateMetrics = {
|
||||
|
||||
export const dashboardCharts = {
|
||||
incomeMix: help(
|
||||
'تفکیک درآمد: سود اختلاف قیمت، کارمزد ریالی، و ارزش هدایا.',
|
||||
'درآمد = سود اختلاف قیمت + کارمزد + (گرم هدیه × مظنه)'
|
||||
'سهم سود اختلاف قیمت و کارمزد از جمع سود معاملات در بازه انتخابی. هدایا هزینهٔ پاداشاند و اینجا جزو سود نیستند.',
|
||||
'جمع سود = سود اختلاف قیمت + کارمزد'
|
||||
),
|
||||
buySellVolume: help(
|
||||
'مقدار طلای خرید و فروش مشتری در هر روز (فقط سفارش موفق).',
|
||||
@@ -129,12 +129,18 @@ export const dashboardCustomerMetrics = {
|
||||
} as const satisfies Record<string, DashboardHelp>
|
||||
|
||||
export const dashboardWallet = help(
|
||||
'مجموع موجودی طلا و ریال همه مشتریان.',
|
||||
'مجموع موجودی طلا و ریال همه مشتریان (بدهی اپ به کاربران). این عدد دفتر اکانت همکار نیست.',
|
||||
'ارزش طلا به ریال = جمع گرم × مظنه لحظهای'
|
||||
)
|
||||
|
||||
export const dashboardWalletMetrics = {
|
||||
gold: help('مجموع طلای موجود در کیف همه مشتریان.', 'جمع گرم طلا در کیفها'),
|
||||
cash: help('مجموع ریال موجود در کیف همه مشتریان.', 'جمع ریال در کیفها'),
|
||||
gold: help(
|
||||
'مجموع طلای موجود در کیف همه مشتریان — بدهی به مشتری؛ با ماندهٔ پنل همکار یکی نیست.',
|
||||
'جمع گرم طلا در کیفها'
|
||||
),
|
||||
cash: help(
|
||||
'مجموع ریال موجود در کیف همه مشتریان — شارژ/مانده مشتری؛ با ماندهٔ ریالی همکار یکی نیست.',
|
||||
'جمع ریال در کیفها'
|
||||
),
|
||||
goldToCash: help('ارزش ریالی کل طلای مشتریان با مظنه فعلی.', 'جمع گرم × قیمت هر گرم'),
|
||||
} as const satisfies Record<string, DashboardHelp>
|
||||
|
||||
@@ -139,25 +139,12 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<DashboardMontzarReconcile :start-date="startDate" :end-date="endDate" />
|
||||
|
||||
<!-- Charts row 1 -->
|
||||
<section class="mt-2 grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||
<div class="dashboard-panel">
|
||||
<DashboardSectionHeader title="ترکیب درآمد" :help="copy.charts.incomeMix" />
|
||||
<div class="dashboard-chart-wrap">
|
||||
<apexchart
|
||||
v-if="(finance?.fullPriceSood ?? 0) + (finance?.fullPriceKarmozd ?? 0) + (finance?.GiftsPrise ?? 0) > 0"
|
||||
type="donut"
|
||||
height="220"
|
||||
:options="financeChartOptions"
|
||||
:series="financeChartOptions.series"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="dashboard-empty flex h-44 items-center justify-center text-xs"
|
||||
>
|
||||
در این بازه داده مالی ثبت نشده
|
||||
</div>
|
||||
</div>
|
||||
<DashboardIncomeMix :finance="finance" />
|
||||
</div>
|
||||
|
||||
<div class="dashboard-panel">
|
||||
@@ -461,6 +448,8 @@ import DashboardQuickWalletModal from '@/pages/dashboard/components/DashboardQui
|
||||
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
||||
import DashboardStatCard from '@/pages/dashboard/components/DashboardStatCard.vue'
|
||||
import DashboardProfitSplit from '@/pages/dashboard/components/DashboardProfitSplit.vue'
|
||||
import DashboardMontzarReconcile from '@/pages/dashboard/components/DashboardMontzarReconcile.vue'
|
||||
import DashboardIncomeMix from '@/pages/dashboard/components/DashboardIncomeMix.vue'
|
||||
import {
|
||||
dashboardCharts,
|
||||
dashboardCustomerMetrics,
|
||||
@@ -481,12 +470,13 @@ import { metalBalanceTitle, metalLabel } from '@/utils/adminMetalLabels'
|
||||
import { formatOrderServiceFee } from '@/utils/orderServiceFee'
|
||||
import { getMainMetal } from '@/utils/metalLabels'
|
||||
import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit'
|
||||
import { initAccountingSettingsFromApi, pargarEnabledSetting } from '@/utils/config'
|
||||
import { initAccountingSettingsFromApi, initStoreModeFromSettings, pargarEnabledSetting } from '@/utils/config'
|
||||
|
||||
const pargarEnabled = pargarEnabledSetting
|
||||
|
||||
onMounted(() => {
|
||||
void initAccountingSettingsFromApi()
|
||||
void initStoreModeFromSettings()
|
||||
})
|
||||
|
||||
const copy = {
|
||||
@@ -533,7 +523,6 @@ const {
|
||||
directionClass,
|
||||
stsClass,
|
||||
stsLabel,
|
||||
financeChartOptions,
|
||||
flowChartOptions,
|
||||
flowChartHeight,
|
||||
peakHoursChartOptions,
|
||||
|
||||
Reference in New Issue
Block a user