feat: implement media download functionality and enhance case handling in reports

This commit is contained in:
2026-09-21 02:20:10 +03:30
parent 4ce663c669
commit 51b13631a3
16 changed files with 334 additions and 164 deletions
@@ -23,8 +23,21 @@ const walletBalance = computed(() => Number(pageProps.value.auth?.wallet_balance
const amount = ref('');
const selectedCardId = ref(props.bankCards[0]?.id ?? null);
const MIN_WITHDRAW_AMOUNT = 10000;
const numericAmount = computed(() => Number(convertPersianToEnglish(amount.value).replace(/\D/g, '')) || 0);
const selectedCard = computed(() => props.bankCards.find((card) => card.id === selectedCardId.value) ?? null);
const amountError = computed(() => {
if (!amount.value) return '';
if (numericAmount.value < MIN_WITHDRAW_AMOUNT) return `حداقل مبلغ برداشت ${formatPrice(MIN_WITHDRAW_AMOUNT)} تومان است.`;
if (numericAmount.value > walletBalance.value) return 'مبلغ برداشت از موجودی کیف پول بیشتر است.';
return '';
});
const canSubmit = computed(() => Boolean(selectedCard.value) && numericAmount.value >= MIN_WITHDRAW_AMOUNT && !amountError.value);
const formatCardNumber = (cardNumber) => convertEnglishToPersian(cardNumber.replace(/(\d{4})(?=\d)/g, '$1 - '));
const selectFullBalance = () => {
@@ -37,15 +50,7 @@ const withdrawForm = useForm({
});
const submitWithdraw = () => {
if (numericAmount.value < 10000) {
toast.error('حداقل مبلغ برداشت ۱۰٬۰۰۰ تومان است.');
return;
}
if (!selectedCardId.value) {
toast.error('لطفا کارت بانکی مقصد را انتخاب کنید.');
return;
}
if (!canSubmit.value) return;
withdrawForm.amount = numericAmount.value;
withdrawForm.bank_card_id = selectedCardId.value;
@@ -95,72 +100,99 @@ const deleteCard = (card) => {
<template>
<UiCard title="درخواست برداشت" variant="default">
<div class="grid grid-cols-2 max-lg:grid-cols-1 gap-6 items-start">
<div class="flex flex-col gap-4">
<p class="text-[11px] font-normal text-[#576989] leading-6">
<div class="flex flex-col gap-5">
<div class="flex items-start justify-between gap-4 max-md:flex-col">
<p class="text-[11px] font-normal text-[#576989] leading-6 max-w-xl">
مبلغ مورد نظر پس از بررسی و تایید توسط کارشناسان، از کیف پول شما کسر و به کارت انتخابی واریز می‌شود.
</p>
<div class="flex items-end gap-3 max-sm:flex-col max-sm:items-stretch">
<div class="flex items-center gap-2 bg-[#F5F7FA] border border-[#DEE3EB] rounded-lg px-3 py-2 shrink-0">
<span class="text-[11px] font-normal text-[#576989]">موجودی قابل برداشت:</span>
<span class="text-[12px] font-semibold text-[#3E4C63]">{{ formatPrice(walletBalance) }} تومان</span>
</div>
</div>
<section class="flex flex-col gap-3 border-t border-[#EBEBEB] pt-4">
<div class="flex items-center justify-between">
<h3 class="text-[12px] font-semibold text-[#3E4C63]">۱. کارت بانکی مقصد</h3>
<button v-if="bankCards.length" type="button" class="text-[11px] font-semibold text-[#365891]" @click="cardModalVisible = true">
+ افزودن کارت
</button>
</div>
<div v-if="!bankCards.length" class="flex flex-col items-center gap-3 bg-[#F8FAFC] border border-dashed border-[#C1D0E8] rounded-xl p-5">
<p class="text-[11px] font-normal text-[#576989] text-center leading-6">
هنوز کارتی ثبت نکرده‌اید. برای ثبت درخواست برداشت ابتدا یک کارت بانکی اضافه کنید.
</p>
<UiButton label="افزودن کارت بانکی" severity="secondary" size="small" @click="cardModalVisible = true" />
</div>
<div v-else class="grid grid-cols-2 max-lg:grid-cols-1 gap-3">
<label
v-for="card in bankCards"
:key="card.id"
class="flex items-center gap-3 border rounded-xl px-4 py-3 cursor-pointer transition-colors"
:class="selectedCardId === card.id ? 'border-[#365891] bg-[#EEF2F8]' : 'border-[#DEE3EB] bg-white hover:bg-[#F8FAFC]'"
>
<input
v-model="selectedCardId"
type="radio"
name="bank_card"
:value="card.id"
class="accent-[#365891]"
/>
<span class="flex flex-col gap-1 flex-1">
<span class="text-[12px] font-semibold text-[#3E4C63]" dir="ltr">{{ formatCardNumber(card.card_number) }}</span>
<span class="text-[11px] font-normal text-[#576989]">{{ card.holder_name }}</span>
</span>
<button type="button" class="text-[11px] font-semibold text-[#7A1419]" @click.prevent="deleteCard(card)">حذف</button>
</label>
</div>
</section>
<section class="flex flex-col gap-3 border-t border-[#EBEBEB] pt-4">
<h3 class="text-[12px] font-semibold text-[#3E4C63]">۲. مبلغ برداشت</h3>
<div class="flex flex-col gap-2 max-w-md">
<UiInput
v-model="amount"
label="مبلغ برداشت"
placeholder="مبلغ مورد نظر را به تومان وارد کنید"
prefixEnd="تومان"
prefixClasses="!border-dominant-50"
class="!w-full"
customClass="!border-dominant-50 !font-normal !text-[12px]"
:error="amountError"
/>
<div class="flex items-center justify-between gap-2 flex-wrap">
<span class="text-[11px] font-normal text-[#576989]">حداقل مبلغ برداشت {{ formatPrice(MIN_WITHDRAW_AMOUNT) }} تومان است.</span>
<button
type="button"
class="text-[11px] font-normal text-[#576989] bg-[#F5F7FA] border border-[#DEE3EB] rounded-lg px-3 py-1.5 hover:bg-[#EEF2F8] disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="walletBalance < MIN_WITHDRAW_AMOUNT"
@click="selectFullBalance"
>
برداشت کل موجودی
</button>
</div>
</div>
<div class="flex items-center gap-2 flex-wrap">
<button
type="button"
class="text-[11px] font-normal text-[#576989] bg-[#F5F7FA] border border-[#DEE3EB] rounded-lg px-3 py-1.5 hover:bg-[#EEF2F8]"
@click="selectFullBalance"
>
کل موجودی ({{ formatPrice(walletBalance) }} تومان)
</button>
</div>
</section>
<div class="flex items-center justify-between gap-4 max-md:flex-col max-md:items-stretch border-t border-[#EBEBEB] pt-4">
<p v-if="canSubmit" class="text-[11px] font-normal text-[#576989] leading-6">
<span class="font-[600] text-[#7A1419]">{{ formatPrice(numericAmount) }} تومان</span>
به کارت
<span class="font-[600] text-[#3E4C63]" dir="ltr">{{ formatCardNumber(selectedCard.card_number) }}</span>
واریز می‌شود.
</p>
<p v-else class="text-[11px] font-normal text-[#576989] leading-6">
برای ثبت درخواست، کارت مقصد را انتخاب و مبلغ را وارد کنید.
</p>
<UiButton
label="ثبت درخواست برداشت"
severity="success"
size="small"
class="self-start"
class="shrink-0"
:loading="withdrawForm.processing"
:disabled="withdrawForm.processing"
:disabled="withdrawForm.processing || !canSubmit"
@click="submitWithdraw"
/>
</div>
<div class="flex flex-col gap-3">
<div class="flex items-center justify-between">
<span class="text-[11px] font-semibold text-[#3E4C63]">کارت بانکی مقصد</span>
<button type="button" class="text-[11px] font-semibold text-[#365891]" @click="cardModalVisible = true">
+ افزودن کارت
</button>
</div>
<p v-if="!bankCards.length" class="bg-[#F8FAFC] border border-dashed border-[#C1D0E8] rounded-xl p-4 text-[11px] font-normal text-[#576989] text-center leading-6">
هنوز کارتی ثبت نکرده‌اید. برای ثبت درخواست برداشت ابتدا یک کارت بانکی اضافه کنید.
</p>
<label
v-for="card in bankCards"
:key="card.id"
class="flex items-center gap-3 border rounded-xl px-4 py-3 cursor-pointer transition-colors"
:class="selectedCardId === card.id ? 'border-[#365891] bg-[#EEF2F8]' : 'border-[#DEE3EB] bg-white hover:bg-[#F8FAFC]'"
>
<input
v-model="selectedCardId"
type="radio"
name="bank_card"
:value="card.id"
class="accent-[#365891]"
/>
<span class="flex flex-col gap-1 flex-1">
<span class="text-[12px] font-semibold text-[#3E4C63]" dir="ltr">{{ formatCardNumber(card.card_number) }}</span>
<span class="text-[11px] font-normal text-[#576989]">{{ card.holder_name }}</span>
</span>
<button type="button" class="text-[11px] font-semibold text-[#7A1419]" @click.prevent="deleteCard(card)">حذف</button>
</label>
</div>
</div>
<Dialog v-model:visible="cardModalVisible" :modal="true" :closable="true" :dismissableMask="false" :style="{ width: '420px' }" class="fast-request-dialog" :pt="{ header: '!bg-client-primaryBlue' }">