Initial commit: admin dashboard for GameNo.
Vue 3 + Vite dashboard with PrimeVue, i18n, and API integration for institution management.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
<!-- /src/views/payments/PaymentDetailView.vue -->
|
||||
<template>
|
||||
<div class="payment-detail-view" v-if="payment">
|
||||
<PageHeader :title="$t('payments.paymentDetail')" :subtitle="`کد صورتحساب: ${payment._id || payment.id}`">
|
||||
<Button label="بازگشت" icon="pi pi-arrow-left" text severity="secondary" @click="$router.push('/payments')" />
|
||||
</PageHeader>
|
||||
|
||||
<div class="grid mb-4">
|
||||
<!-- Summary Card -->
|
||||
<div class="col-12 lg:col-4">
|
||||
<div class="surface-card p-4 border-round border-1 border-color shadow-sm mb-4">
|
||||
<h3 class="text-lg font-bold mb-3">خلاصه صورتحساب</h3>
|
||||
<div class="flex flex-column gap-3">
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">نام کاربر / دانشجو</span>
|
||||
<span class="font-bold text-color text-base">{{ payment.user?.name || payment.userName || 'کاربر' }} {{ payment.user?.surname || '' }}</span>
|
||||
</div>
|
||||
<div v-if="payment.classes && payment.classes.length">
|
||||
<span class="text-muted text-xs block mb-1">کلاسهای مربوطه</span>
|
||||
<div class="flex flex-wrap gap-1 mt-1">
|
||||
<Tag v-for="c in payment.classes" :key="c._id || c" :value="c.name || 'کلاس'" severity="info" class="text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="payment.course">
|
||||
<span class="text-muted text-xs block mb-1">دوره آموزشی</span>
|
||||
<span class="font-semibold text-color text-sm">{{ payment.course?.title }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">مبلغ کل صورتحساب</span>
|
||||
<span class="font-bold text-color text-xl">{{ toPersianDigits(payment.amount?.toLocaleString()) }} تومان</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">مبلغ کل دریافتی</span>
|
||||
<span class="font-bold text-green-600 text-lg">{{ toPersianDigits((payment.paidAmount || 0).toLocaleString()) }} تومان</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">باقیمانده</span>
|
||||
<span class="font-bold text-red-500 text-lg">{{ toPersianDigits((payment.amount - (payment.paidAmount || 0)).toLocaleString()) }} تومان</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">وضعیت پرداخت</span>
|
||||
<StatusTag :status="payment.status" type="payment" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transaction History & Form -->
|
||||
<div class="col-12 lg:col-8">
|
||||
<div class="surface-card p-4 border-round border-1 border-color shadow-sm mb-4">
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<h3 class="text-lg font-bold m-0">تاریخچه تراکنشهای دریافتی</h3>
|
||||
<PermissionGate permission="payments:update">
|
||||
<Button label="ثبت تراکنش جدید" icon="pi pi-plus" size="small" severity="success" @click="showTransactionModal = true" />
|
||||
</PermissionGate>
|
||||
</div>
|
||||
|
||||
<DataTable :value="payment.transactions || []" class="p-datatable-sm text-sm">
|
||||
<Column field="amount" header="مبلغ تراکنش">
|
||||
<template #body="{ data }">
|
||||
{{ toPersianDigits(data.amount?.toLocaleString()) }} تومان
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="method" header="روش پرداخت">
|
||||
<template #body="{ data }">
|
||||
<Tag :value="data.method === 'online' ? 'درگاه آنلاین' : (data.method === 'card' ? 'کارت به کارت' : 'نقدی')" severity="info" />
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="receiptNumber" header="شماره فیش / پیگیری">
|
||||
<template #body="{ data }">
|
||||
<span dir="ltr">{{ toPersianDigits(data.receiptNumber || '-') }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column field="createdAt" header="تاریخ ثبت">
|
||||
<template #body="{ data }">
|
||||
{{ formatJalali(data.createdAt || data.date) }}
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Record New Transaction Modal -->
|
||||
<Dialog v-model:visible="showTransactionModal" header="ثبت تراکنش جدید" modal :style="{ width: '450px' }">
|
||||
<div class="flex flex-column gap-3 py-2">
|
||||
<div class="flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">مبلغ واریزی (تومان) *</label>
|
||||
<InputNumber v-model="trxForm.amount" class="w-full text-sm" suffix=" تومان" />
|
||||
</div>
|
||||
<div class="flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">روش پرداخت *</label>
|
||||
<Dropdown v-model="trxForm.method" :options="methodOptions" optionLabel="label" optionValue="value" class="w-full text-sm" />
|
||||
</div>
|
||||
<div class="flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">شماره فیش / پیگیری *</label>
|
||||
<InputText v-model.trim="trxForm.receiptNumber" class="w-full text-sm" dir="ltr" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<Button label="انصراف" text severity="secondary" @click="showTransactionModal = false" />
|
||||
<Button label="ثبت تراکنش" icon="pi pi-check" severity="success" :loading="isSubmittingTrx" @click="handleRecordTransaction" />
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { paymentApi } from '@/api/paymentApi';
|
||||
import { usePersianDate } from '@/composables/usePersianDate';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import PageHeader from '@/components/common/PageHeader.vue';
|
||||
import PermissionGate from '@/components/common/PermissionGate.vue';
|
||||
import StatusTag from '@/components/common/StatusTag.vue';
|
||||
import Button from 'primevue/button';
|
||||
import Tag from 'primevue/tag';
|
||||
import DataTable from 'primevue/datatable';
|
||||
import Column from 'primevue/column';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import Dropdown from 'primevue/select';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
|
||||
const route = useRoute();
|
||||
const paymentId = route.params.id;
|
||||
const { toPersianDigits, formatJalali } = usePersianDate();
|
||||
const { showSuccess, showError } = useToast();
|
||||
|
||||
const payment = ref(null);
|
||||
const showTransactionModal = ref(false);
|
||||
const isSubmittingTrx = ref(false);
|
||||
|
||||
const methodOptions = [
|
||||
{ label: 'کارت به کارت', value: 'card' },
|
||||
{ label: 'درگاه آنلاین', value: 'online' },
|
||||
{ label: 'نقدی / شبا', value: 'cash' }
|
||||
];
|
||||
|
||||
const trxForm = reactive({
|
||||
amount: 500000,
|
||||
method: 'card',
|
||||
receiptNumber: ''
|
||||
});
|
||||
|
||||
const fetchPayment = async () => {
|
||||
try {
|
||||
const res = await paymentApi.getOne(paymentId);
|
||||
payment.value = res.data || res;
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecordTransaction = async () => {
|
||||
if (!trxForm.amount) { showError('لطفا مبلغ را وارد کنید'); return; }
|
||||
if (!trxForm.receiptNumber) { showError('لطفا شماره فیش یا پیگیری را وارد کنید'); return; }
|
||||
|
||||
isSubmittingTrx.value = true;
|
||||
try {
|
||||
await paymentApi.recordTransaction(paymentId, trxForm);
|
||||
showSuccess('تراکنش جدید با موفقیت ثبت شد');
|
||||
showTransactionModal.value = false;
|
||||
fetchPayment();
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
} finally {
|
||||
isSubmittingTrx.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchPayment();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user