2026-06-21 21:55:54 +03:30
|
|
|
<template>
|
|
|
|
|
<BasicActionSheet
|
|
|
|
|
v-model:visible="visibleProxy"
|
|
|
|
|
:title="modalTitle"
|
2026-06-23 22:31:07 +03:30
|
|
|
modal-width="24rem"
|
2026-06-21 21:55:54 +03:30
|
|
|
drawer-height="auto"
|
|
|
|
|
compact
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:show-button="false"
|
|
|
|
|
@close="handleClose"
|
|
|
|
|
>
|
|
|
|
|
<div class="dashboard-wallet-modal">
|
|
|
|
|
<div class="dashboard-wallet-modal__section">
|
|
|
|
|
<label class="dashboard-wallet-modal__label">مشتری</label>
|
|
|
|
|
<a-select
|
|
|
|
|
v-model:value="customerId"
|
|
|
|
|
show-search
|
|
|
|
|
allow-clear
|
|
|
|
|
placeholder="انتخاب کنید"
|
|
|
|
|
class="dashboard-wallet-modal__select w-full"
|
|
|
|
|
size="large"
|
|
|
|
|
:options="customerOptions"
|
|
|
|
|
:filter-option="filterCustomer"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-06-23 22:31:07 +03:30
|
|
|
<div v-if="selectedCustomer" class="dashboard-wallet-modal__balances">
|
2026-06-21 21:55:54 +03:30
|
|
|
<div>
|
|
|
|
|
<span class="text-slate-500">ریالی</span>
|
|
|
|
|
<p class="font-semibold text-slate-800 dark:text-slate-100">{{ formatCash(selectedCustomer) }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<WalletChargeForm
|
|
|
|
|
ref="chargeFormRef"
|
|
|
|
|
class="dashboard-wallet-modal__form"
|
|
|
|
|
:model-value="formCharge"
|
2026-06-23 22:31:07 +03:30
|
|
|
:targets="chargeTargets"
|
2026-06-21 21:55:54 +03:30
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div class="dashboard-wallet-modal__footer">
|
|
|
|
|
<a-button danger size="large" block @click="closeModal">
|
|
|
|
|
بستن
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button
|
|
|
|
|
type="primary"
|
|
|
|
|
:danger="formCharge.direction === 'discharge'"
|
|
|
|
|
size="large"
|
|
|
|
|
block
|
|
|
|
|
:loading="submitting"
|
|
|
|
|
@click="submit"
|
|
|
|
|
>
|
|
|
|
|
{{ okText }}
|
|
|
|
|
</a-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</BasicActionSheet>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, ref, watch } from 'vue'
|
|
|
|
|
import { message } from 'ant-design-vue'
|
|
|
|
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
|
|
|
|
import WalletChargeForm from '@/pages/menuPages/customers/base/components/WalletChargeForm.vue'
|
|
|
|
|
import {
|
2026-06-23 22:31:07 +03:30
|
|
|
applyChargeTarget,
|
2026-06-21 21:55:54 +03:30
|
|
|
buildChargePayload,
|
|
|
|
|
createEmptyChargeForm,
|
2026-06-23 22:31:07 +03:30
|
|
|
resolveChargeTarget,
|
2026-06-21 21:55:54 +03:30
|
|
|
walletChargeOkText,
|
|
|
|
|
walletChargeSuccessMessage,
|
|
|
|
|
walletChargeTitle,
|
|
|
|
|
} from '@/pages/menuPages/customers/base/utils/walletChargeForm'
|
|
|
|
|
import { getCustomersList } from '@/api/customer'
|
2026-06-23 22:31:07 +03:30
|
|
|
import { chargeWalletUnified, getWalletChargeTargets } from '@/api/wallet'
|
2026-06-21 21:55:54 +03:30
|
|
|
import type { Customer } from '@/model/customer'
|
2026-06-23 22:31:07 +03:30
|
|
|
import type { Charge, Wallet, WalletChargeTarget } from '@/model/wallet'
|
2026-06-21 21:55:54 +03:30
|
|
|
import { usePrice } from '@core/utils/usePrice'
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ visible: boolean }>()
|
|
|
|
|
const emit = defineEmits<{ (e: 'update:visible', value: boolean): void }>()
|
|
|
|
|
|
|
|
|
|
const visibleProxy = computed({
|
|
|
|
|
get: () => props.visible,
|
|
|
|
|
set: (value: boolean) => emit('update:visible', value),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
const submitting = ref(false)
|
|
|
|
|
const customers = ref<Customer[]>([])
|
2026-06-23 22:31:07 +03:30
|
|
|
const chargeTargets = ref<WalletChargeTarget[]>([])
|
2026-06-21 21:55:54 +03:30
|
|
|
const formCharge = ref<Charge>(createEmptyChargeForm())
|
|
|
|
|
const chargeFormRef = ref<InstanceType<typeof WalletChargeForm>>()
|
|
|
|
|
|
|
|
|
|
const customerId = computed({
|
|
|
|
|
get: () => formCharge.value.customer_id ?? undefined,
|
|
|
|
|
set: (value: number | undefined) => {
|
|
|
|
|
formCharge.value.customer_id = value
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const customerOptions = computed(() =>
|
|
|
|
|
customers.value.map((customer) => ({
|
|
|
|
|
value: customer.id,
|
|
|
|
|
label: `${customer.name ?? ''} — ${customer.mobile ?? ''}`.trim(),
|
|
|
|
|
})),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const selectedCustomer = computed(() =>
|
|
|
|
|
customers.value.find((customer) => customer.id === formCharge.value.customer_id),
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-23 22:31:07 +03:30
|
|
|
const selectedTarget = computed(() =>
|
|
|
|
|
resolveChargeTarget(chargeTargets.value, formCharge.value.chargeTargetKey),
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-21 21:55:54 +03:30
|
|
|
const modalTitle = computed(() =>
|
2026-06-23 22:31:07 +03:30
|
|
|
walletChargeTitle(selectedTarget.value, formCharge.value.direction),
|
2026-06-21 21:55:54 +03:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const okText = computed(() => walletChargeOkText(formCharge.value.direction))
|
|
|
|
|
|
|
|
|
|
function filterCustomer(input: string, option?: { label?: string }) {
|
|
|
|
|
return String(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function customerWallet(customer: Customer): Wallet | undefined {
|
|
|
|
|
const wallet = customer.wallet as Wallet | Wallet[] | undefined
|
|
|
|
|
return Array.isArray(wallet) ? wallet[0] : wallet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatCash(customer: Customer) {
|
|
|
|
|
const cash = customerWallet(customer)?.cash ?? customer.wallet_cash
|
|
|
|
|
return usePrice(Number(cash ?? 0), true, '')
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-23 22:31:07 +03:30
|
|
|
async function loadChargeTargets() {
|
|
|
|
|
const data = await getWalletChargeTargets()
|
|
|
|
|
chargeTargets.value = data?.targets ?? []
|
|
|
|
|
const firstTarget = chargeTargets.value[0]
|
|
|
|
|
if (firstTarget) {
|
|
|
|
|
Object.assign(formCharge.value, applyChargeTarget(formCharge.value, firstTarget))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 21:55:54 +03:30
|
|
|
async function loadCustomers() {
|
|
|
|
|
loading.value = true
|
|
|
|
|
try {
|
2026-06-23 22:31:07 +03:30
|
|
|
const [customerList] = await Promise.all([getCustomersList(), loadChargeTargets()])
|
|
|
|
|
customers.value = customerList ?? []
|
2026-06-21 21:55:54 +03:30
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e)
|
|
|
|
|
message.error('خطا در بارگذاری لیست مشتریان')
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function resetForm() {
|
|
|
|
|
formCharge.value = createEmptyChargeForm()
|
2026-06-23 22:31:07 +03:30
|
|
|
const firstTarget = chargeTargets.value[0]
|
|
|
|
|
if (firstTarget) {
|
|
|
|
|
Object.assign(formCharge.value, applyChargeTarget(formCharge.value, firstTarget))
|
|
|
|
|
}
|
2026-06-21 21:55:54 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleClose() {
|
|
|
|
|
resetForm()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeModal() {
|
|
|
|
|
visibleProxy.value = false
|
|
|
|
|
resetForm()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function submit() {
|
|
|
|
|
if (!formCharge.value.customer_id) {
|
|
|
|
|
message.warning('مشتری را انتخاب کنید')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await chargeFormRef.value?.validateFields?.()
|
|
|
|
|
} catch {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
submitting.value = true
|
|
|
|
|
try {
|
2026-06-23 22:31:07 +03:30
|
|
|
await chargeWalletUnified(buildChargePayload(formCharge.value))
|
2026-06-21 21:55:54 +03:30
|
|
|
message.success(walletChargeSuccessMessage(formCharge.value.direction))
|
|
|
|
|
closeModal()
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e)
|
|
|
|
|
message.error('خطا در ثبت عملیات')
|
|
|
|
|
} finally {
|
|
|
|
|
submitting.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.visible,
|
|
|
|
|
(open) => {
|
|
|
|
|
if (open) {
|
|
|
|
|
void loadCustomers()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.dashboard-wallet-modal {
|
|
|
|
|
@apply flex flex-col gap-4 px-4 pb-2 pt-1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__select :deep(.ant-select-selector) {
|
|
|
|
|
@apply !px-3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__select :deep(.ant-select-selection-placeholder) {
|
|
|
|
|
@apply text-slate-400;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__section {
|
|
|
|
|
@apply flex flex-col gap-1.5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__label {
|
|
|
|
|
@apply text-xs font-semibold text-slate-600 dark:text-slate-300;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__balances {
|
2026-06-23 22:31:07 +03:30
|
|
|
@apply grid grid-cols-1 gap-3 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2.5 text-xs dark:border-slate-600 dark:bg-slate-800/60;
|
2026-06-21 21:55:54 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__footer {
|
|
|
|
|
@apply sticky bottom-0 grid grid-cols-2 gap-2 border-t border-slate-200 bg-white pt-3 dark:border-slate-600 dark:bg-slate-900;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.ant-form-item) {
|
|
|
|
|
margin-bottom: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.ant-form-item-label > label) {
|
|
|
|
|
@apply text-xs font-semibold text-slate-600 dark:text-slate-300;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__direction .ant-radio-button-wrapper) {
|
|
|
|
|
height: 2.5rem;
|
|
|
|
|
line-height: 2.4rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__preview) {
|
|
|
|
|
margin-bottom: 0.75rem;
|
|
|
|
|
padding: 0.65rem 0.85rem;
|
|
|
|
|
font-size: 0.8125rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__preview--charge) {
|
|
|
|
|
color: #166534;
|
|
|
|
|
background: #f0fdf4;
|
|
|
|
|
border-color: #bbf7d0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__preview--charge .wallet-charge-form__preview-sign) {
|
|
|
|
|
color: #15803d;
|
|
|
|
|
background: #dcfce7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__preview--discharge) {
|
|
|
|
|
color: #991b1b;
|
|
|
|
|
background: #fef2f2;
|
|
|
|
|
border-color: #fecaca;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-wallet-modal__form :deep(.wallet-charge-form__preview--discharge .wallet-charge-form__preview-sign) {
|
|
|
|
|
color: #b91c1c;
|
|
|
|
|
background: #fee2e2;
|
|
|
|
|
}
|
|
|
|
|
</style>
|