2026-09-13 21:02:25 +03:30
|
|
|
|
<template>
|
|
|
|
|
|
<!--
|
|
|
|
|
|
Shared implementation of legacy ViewOmdeCustomer.vue (376 lines) and
|
|
|
|
|
|
ViewSingleCustomer.vue (425 lines) — the customer detail screens.
|
|
|
|
|
|
|
|
|
|
|
|
Both show the same four panels (profile, orders, financial documents, wallet)
|
|
|
|
|
|
against the same customer record; legacy duplicated the whole page per
|
|
|
|
|
|
customer kind. The differing bits are parameterised:
|
|
|
|
|
|
- the wholesale screen adds an activity-log panel (legacy log/ago/{id})
|
|
|
|
|
|
- the retail screen adds a wallet panel (legacy single/wallet/{id})
|
|
|
|
|
|
-->
|
|
|
|
|
|
<a-spin :spinning="loading">
|
|
|
|
|
|
<a-card :title="title">
|
|
|
|
|
|
<a-descriptions bordered size="small" :column="3">
|
|
|
|
|
|
<a-descriptions-item label="نام">{{ customer?.name ?? '-' }}</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="موبایل">{{ customer?.mobile ?? '-' }}</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="کد ملی">
|
|
|
|
|
|
{{ customer?.national_code ?? '-' }}
|
|
|
|
|
|
</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="شهر">{{ customer?.city?.title ?? '-' }}</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="کارشناس فروش">
|
|
|
|
|
|
{{ customer?.reagent?.name ?? '-' }}
|
|
|
|
|
|
</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="وضعیت">
|
|
|
|
|
|
<a-tag :color="customer?.active === 1 ? 'green' : 'red'">
|
|
|
|
|
|
{{ customer?.active === 1 ? 'فعال' : 'غیر فعال' }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="سطح اعتبار">
|
|
|
|
|
|
{{ customer?.gram_level ?? '-' }} گرم
|
|
|
|
|
|
</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="بدهی گرمی">{{ customer?.remain ?? '-' }}</a-descriptions-item>
|
|
|
|
|
|
<a-descriptions-item label="بدهی ریالی">
|
|
|
|
|
|
{{ customer?.rial_remain ?? '-' }}
|
|
|
|
|
|
</a-descriptions-item>
|
2026-10-08 10:40:21 +03:30
|
|
|
|
<!-- legacy ViewOmdeCustomer.vue:28-30 -->
|
|
|
|
|
|
<a-descriptions-item label="میزان فروش">{{ dashboard?.grams_sell ?? '-' }}</a-descriptions-item>
|
2026-09-13 21:02:25 +03:30
|
|
|
|
</a-descriptions>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="dashboard" class="stats mt-4">
|
|
|
|
|
|
<a-tag color="blue">کل سفارشها: {{ dashboard.total_order }}</a-tag>
|
|
|
|
|
|
<a-tag color="orange">سفارش باز: {{ dashboard.open_order }}</a-tag>
|
|
|
|
|
|
<a-tag color="green">اسناد: {{ dashboard.ledger_entries }}</a-tag>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
|
2026-10-06 16:48:36 +03:30
|
|
|
|
<!-- D27 — link and balance in the accounting software. -->
|
|
|
|
|
|
<CustomerAccountingCard :customer-id="id" />
|
|
|
|
|
|
|
2026-09-13 21:02:25 +03:30
|
|
|
|
<a-tabs class="mt-4" :default-active-key="defaultTab">
|
|
|
|
|
|
<a-tab-pane key="orders" tab="سفارش ها">
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
:data-source="orders"
|
|
|
|
|
|
:columns="orderColumns"
|
|
|
|
|
|
:pagination="{ pageSize: 10 }"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.dataIndex === 'caj'">{{ jdate(record.caj) }}</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<a-tab-pane key="documents" tab="سند های مالی">
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
:data-source="documents"
|
|
|
|
|
|
:columns="documentColumns"
|
|
|
|
|
|
:pagination="{ pageSize: 10 }"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.dataIndex === 'confirmed'">
|
|
|
|
|
|
<a-tag :color="record.confirmed === 1 ? 'green' : ''">
|
|
|
|
|
|
{{ record.confirmed === 1 ? 'تایید شده' : 'تایید نشده' }}
|
|
|
|
|
|
</a-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else-if="column.dataIndex === 'caj'">{{ jdate(record.caj) }}</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<a-tab-pane v-if="showWallet" key="wallet" tab="کیف پول">
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
:data-source="wallet"
|
|
|
|
|
|
:columns="walletColumns"
|
|
|
|
|
|
:pagination="false"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.dataIndex === 'caj'">{{ jdate(record.caj) }}</template>
|
|
|
|
|
|
<template v-else-if="column.dataIndex === 'expires_at'">
|
|
|
|
|
|
{{ jdate(record.expires_at) }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
|
|
|
|
|
|
<a-tab-pane v-if="showActivity" key="activity" tab="فعالیت ها">
|
|
|
|
|
|
<a-table
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
:data-source="activity"
|
|
|
|
|
|
:columns="activityColumns"
|
|
|
|
|
|
:pagination="{ pageSize: 15 }"
|
|
|
|
|
|
row-key="id"
|
|
|
|
|
|
>
|
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
|
<template v-if="column.dataIndex === 'caj'">{{ jdateTime(record.caj) }}</template>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-table>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
</a-spin>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { onMounted, ref } from 'vue';
|
|
|
|
|
|
import { useRoute } from 'vue-router';
|
|
|
|
|
|
import { jdate, jdateTime } from '../../utils/filters';
|
|
|
|
|
|
import api from '../../api/client';
|
2026-10-06 16:48:36 +03:30
|
|
|
|
import CustomerAccountingCard from '../../components/accounting/CustomerAccountingCard.vue';
|
2026-09-13 21:02:25 +03:30
|
|
|
|
|
|
|
|
|
|
const props = withDefaults(
|
|
|
|
|
|
defineProps<{
|
|
|
|
|
|
title?: string;
|
|
|
|
|
|
/** The retail screen shows the wallet panel. */
|
|
|
|
|
|
showWallet?: boolean;
|
|
|
|
|
|
/** The wholesale screen shows the activity log. */
|
|
|
|
|
|
showActivity?: boolean;
|
|
|
|
|
|
defaultTab?: string;
|
|
|
|
|
|
}>(),
|
|
|
|
|
|
{ title: 'مشاهده اطلاعات مشتری', showWallet: false, showActivity: false, defaultTab: 'orders' },
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const id = route.params.id as string;
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
const customer = ref<Record<string, any> | undefined>(undefined);
|
|
|
|
|
|
const dashboard = ref<Record<string, any> | undefined>(undefined);
|
|
|
|
|
|
const orders = ref<Record<string, any>[]>([]);
|
|
|
|
|
|
const documents = ref<Record<string, any>[]>([]);
|
|
|
|
|
|
const wallet = ref<Record<string, any>[]>([]);
|
|
|
|
|
|
const activity = ref<Record<string, any>[]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
const orderColumns = [
|
|
|
|
|
|
{ title: 'شماره سفارش', dataIndex: 'name' },
|
|
|
|
|
|
{ title: 'وزن', dataIndex: 'w' },
|
|
|
|
|
|
{ title: 'تعداد آیتم', dataIndex: 'items_count' },
|
|
|
|
|
|
{ title: 'شعبه', dataIndex: 'branch' },
|
|
|
|
|
|
{ title: 'تاریخ', dataIndex: 'caj' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const documentColumns = [
|
|
|
|
|
|
{ title: 'نوع', dataIndex: 'type' },
|
|
|
|
|
|
{ title: 'مقدار', dataIndex: 'value' },
|
|
|
|
|
|
{ title: 'وضعیت', dataIndex: 'confirmed' },
|
|
|
|
|
|
{ title: 'توضیحات', dataIndex: 'note' },
|
|
|
|
|
|
{ title: 'تاریخ', dataIndex: 'caj' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const walletColumns = [
|
|
|
|
|
|
{ title: 'مقدار (گرم)', dataIndex: 'value' },
|
|
|
|
|
|
{ title: 'تاریخ اعطا', dataIndex: 'caj' },
|
|
|
|
|
|
{ title: 'انقضا', dataIndex: 'expires_at' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const activityColumns = [
|
|
|
|
|
|
{ title: 'عملیات', dataIndex: 'action' },
|
|
|
|
|
|
{ title: 'جزئیات', dataIndex: 'detail' },
|
|
|
|
|
|
{ title: 'تاریخ', dataIndex: 'caj' },
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
const [c, d, o, docs] = await Promise.all([
|
|
|
|
|
|
api.get(`customer/${id}`),
|
|
|
|
|
|
api.get(`major/dashboard/${id}`).catch(() => ({ data: undefined })),
|
|
|
|
|
|
api.get(`admin/customers-omde/${id}/orders`).catch(() => ({ data: [] })),
|
|
|
|
|
|
api.get(`admin/customers-omde/${id}/documents`).catch(() => ({ data: [] })),
|
|
|
|
|
|
]);
|
|
|
|
|
|
customer.value = c.data;
|
|
|
|
|
|
dashboard.value = d.data;
|
|
|
|
|
|
orders.value = o.data;
|
|
|
|
|
|
documents.value = docs.data;
|
|
|
|
|
|
|
|
|
|
|
|
if (props.showWallet) {
|
|
|
|
|
|
const w = await api
|
|
|
|
|
|
.get(`admin/customers-omde/${id}/wallet`)
|
|
|
|
|
|
.catch(() => ({ data: [] }));
|
|
|
|
|
|
wallet.value = w.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (props.showActivity) {
|
|
|
|
|
|
const a = await api.get(`log/ago/${id}`).catch(() => ({ data: [] }));
|
|
|
|
|
|
activity.value = a.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log(e);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.stats {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
.mt-4 {
|
|
|
|
|
|
margin-top: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|