Files
boursecu_admin/src/pages/menuPages/physicalWithdraw/components/PhysicalWithdrawAdjustModal.vue
T
2026-07-08 18:24:55 +03:30

234 lines
6.9 KiB
Vue

<template>
<BasicModal
v-model:visible="visibleProxy"
title="ویرایش مقدار برداشت"
width="28rem"
wrap-class-name="physical-withdraw-adjust-modal"
:loading="loading"
:confirm-loading="saving"
@ok="submit"
@close="close"
>
<div v-if="loading" class="py-10 text-center text-sm text-gray-500">در حال بارگذاری...</div>
<div v-else-if="info && formLines.length" class="space-y-4">
<div class="rounded-lg bg-gray-50 p-3 text-sm leading-6">
<div>سند: <span class="font-semibold">#{{ info.withdraw_id }}</span></div>
<div>مشتری: <span class="font-semibold">{{ info.customer_name || '-' }}</span></div>
<div v-if="info.customer_mobile">موبایل: {{ info.customer_mobile }}</div>
</div>
<div
v-for="line in formLines"
:key="line.transaction_id"
class="rounded-lg border border-gray-200 p-3"
>
<div class="mb-2 text-sm font-semibold">{{ line.title }}</div>
<div class="mb-3 grid grid-cols-2 gap-2 text-xs text-gray-600">
<div>مقدار فعلی: <span class="font-medium text-gray-900">{{ formatQty(line.current_value, line) }}</span></div>
<div>قابل افزودن: <span class="font-medium text-gray-900">{{ formatQty(line.available_add, line) }}</span></div>
<div>حداکثر مجاز: <span class="font-medium text-gray-900">{{ formatQty(line.max_value, line) }}</span></div>
<div>حداقل مجاز: <span class="font-medium text-gray-900">{{ formatQty(line.min_value, line) }}</span></div>
</div>
<a-form-item
class="!mb-0"
:validate-status="lineError(line.transaction_id) ? 'error' : ''"
:help="lineError(line.transaction_id)"
>
<template #label>
<AdminLabelWithHelp label="مقدار جدید" :help="physicalWithdrawAdjustValueHelp" />
</template>
<BasicInput
v-model:value="line.valueInput"
type="number"
:step="line.allows_decimal ? '0.001' : '1'"
:place-holder="line.allows_decimal ? 'مثلاً 1.250' : 'مثلاً 2'"
@change="() => clearLineError(line.transaction_id)"
/>
</a-form-item>
</div>
</div>
<div v-else-if="info && !formLines.length" class="py-8 text-center text-sm text-gray-500">
خطی برای ویرایش یافت نشد.
</div>
</BasicModal>
</template>
<script setup lang="ts">
import { message } from 'ant-design-vue'
import AdminLabelWithHelp from '@/components/AdminLabelWithHelp.vue'
import {
adjustPhysicalWithdraw,
getPhysicalWithdrawAdjustInfo,
type PhysicalWithdrawAdjustInfo,
type PhysicalWithdrawAdjustLine
} from '@/api/physicalWithdraw'
import { physicalWithdrawAdjustValueHelp } from '@/utils/adminHelpCopy'
type FormLine = PhysicalWithdrawAdjustLine & { valueInput: string }
const props = defineProps<{
visible: boolean
withdrawId?: number
}>()
const emit = defineEmits<{
'update:visible': [value: boolean]
success: []
}>()
const visibleProxy = computed({
get: () => props.visible,
set: (value: boolean) => emit('update:visible', value),
})
const loading = ref(false)
const saving = ref(false)
const info = ref<PhysicalWithdrawAdjustInfo | null>(null)
const formLines = ref<FormLine[]>([])
const lineErrors = ref<Record<number, string>>({})
watch(
() => [props.visible, props.withdrawId] as const,
([open, withdrawId]) => {
if (open && withdrawId) {
void loadInfo(withdrawId)
}
if (!open) {
resetState()
}
}
)
function resetState() {
info.value = null
formLines.value = []
lineErrors.value = {}
}
function formatQty(value: number, line: Pick<PhysicalWithdrawAdjustLine, 'unit' | 'allows_decimal'>) {
const decimals = line.allows_decimal ? 3 : 0
const formatted = Number(value).toLocaleString('fa-IR', {
minimumFractionDigits: 0,
maximumFractionDigits: decimals
})
return `${formatted} ${line.unit}`
}
function lineError(transactionId: number) {
return lineErrors.value[transactionId] ?? ''
}
function clearLineError(transactionId: number) {
if (lineErrors.value[transactionId]) {
const next = { ...lineErrors.value }
delete next[transactionId]
lineErrors.value = next
}
}
function parseValue(raw: string, line: PhysicalWithdrawAdjustLine): number | null {
const normalized = String(raw ?? '').trim().replace(',', '.')
if (!normalized) return null
const value = Number(normalized)
if (!Number.isFinite(value)) return null
if (!line.allows_decimal && Math.abs(value - Math.round(value)) > 0.000001) {
return null
}
return value
}
function validateLine(line: FormLine): string | null {
const value = parseValue(line.valueInput, line)
if (value == null) {
return line.allows_decimal ? 'مقدار معتبر وارد کنید' : 'مقدار باید عدد صحیح باشد'
}
if (value < line.min_value - 0.000001) {
return `حداقل مقدار ${formatQty(line.min_value, line)} است`
}
if (value > line.max_value + 0.000001) {
return `حداکثر مقدار ${formatQty(line.max_value, line)} است`
}
return null
}
async function loadInfo(withdrawId: number) {
loading.value = true
lineErrors.value = {}
try {
const data = await getPhysicalWithdrawAdjustInfo(withdrawId)
info.value = data
formLines.value = (data.lines ?? []).map((line) => ({
...line,
valueInput: String(line.current_value ?? '')
}))
} catch (e) {
console.error(e)
visibleProxy.value = false
} finally {
loading.value = false
}
}
function close() {
visibleProxy.value = false
}
async function submit() {
if (!props.withdrawId || !formLines.value.length) return
const errors: Record<number, string> = {}
const items: Array<{ transaction_id: number; value: number }> = []
for (const line of formLines.value) {
const error = validateLine(line)
if (error) {
errors[line.transaction_id] = error
continue
}
const value = parseValue(line.valueInput, line)
if (value == null) {
errors[line.transaction_id] = 'مقدار معتبر وارد کنید'
continue
}
if (Math.abs(value - line.current_value) < 0.000001) {
continue
}
items.push({
transaction_id: line.transaction_id,
value
})
}
lineErrors.value = errors
if (Object.keys(errors).length) {
message.error('لطفاً مقادیر را اصلاح کنید')
return
}
if (!items.length) {
message.info('تغییری اعمال نشده است')
return
}
saving.value = true
try {
await adjustPhysicalWithdraw(props.withdrawId, { items })
message.success('مقادیر با موفقیت به‌روزرسانی شد')
visibleProxy.value = false
emit('success')
} catch (e) {
console.error(e)
} finally {
saving.value = false
}
}
</script>
<style>
.physical-withdraw-adjust-modal {
z-index: 1100 !important;
}
</style>