added some text handling 3
This commit is contained in:
@@ -400,7 +400,13 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
||||
support_messenger_text:
|
||||
'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیشفرض اپ استفاده میشود.',
|
||||
deposit_support_message_text:
|
||||
'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیشفرض: «شماره حساب».',
|
||||
`متن پیام هنگام کلیک «پیام به پشتیبانی» در مودال واریز (SMS و پیامرسان).
|
||||
|
||||
متغیر:
|
||||
• %نام = نام صاحب حساب
|
||||
|
||||
پیشفرض اپ:
|
||||
باسلام، %نام هستم. کدپیگیری و مبلغ واریزی من:`,
|
||||
deposit_account_holder:
|
||||
'نام صاحب حساب واریز — در متن راهنما و پیام پشتیبانی با ٪نام یا %نام جایگزین میشود.',
|
||||
deposit_card_number:
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
<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>
|
||||
+45
-18
@@ -1,26 +1,40 @@
|
||||
<template>
|
||||
<a-form ref="formRef" :model="form" layout="vertical" autocomplete="off">
|
||||
<a-form-item label="مشتری" name="customer_id" :rules="inputRules()">
|
||||
<a-form-item name="customer_id" :rules="inputRules()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="مشتری" :help="physicalWithdrawCustomerHelp" />
|
||||
</template>
|
||||
<BasicSelect
|
||||
v-model:value="form.customer_id"
|
||||
show-search
|
||||
:options="customerOptions"
|
||||
:items="customerItems"
|
||||
key-name="id"
|
||||
value-name="displayName"
|
||||
placeholder="انتخاب مشتری"
|
||||
:has-tooltip="false"
|
||||
@change="onCustomerChange"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="form.customer_id" label="آدرس تحویل" name="address_id" :rules="inputRules()">
|
||||
<a-form-item v-if="form.customer_id" name="address_id" :rules="inputRules()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="آدرس تحویل" :help="physicalWithdrawAddressHelp" />
|
||||
</template>
|
||||
<BasicSelect
|
||||
v-model:value="form.address_id"
|
||||
:options="addressOptions"
|
||||
:items="addressItems"
|
||||
key-name="id"
|
||||
value-name="displayName"
|
||||
placeholder="انتخاب آدرس"
|
||||
:loading="optionsLoading"
|
||||
:has-tooltip="false"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<div v-if="form.customer_id" class="space-y-3">
|
||||
<div class="text-sm font-semibold">محصولات</div>
|
||||
<div class="flex items-center gap-2 text-sm font-semibold">
|
||||
<span>محصولات</span>
|
||||
<AdminFieldHelp :text="physicalWithdrawProductHelp" />
|
||||
</div>
|
||||
<div
|
||||
v-for="(line, index) in form.lines"
|
||||
:key="`${line.wallet_id}-${index}`"
|
||||
@@ -30,13 +44,16 @@
|
||||
class="!mb-0"
|
||||
:name="['lines', index, 'wallet_id']"
|
||||
:rules="inputRules()"
|
||||
label="محصول"
|
||||
>
|
||||
<template #label>محصول</template>
|
||||
<BasicSelect
|
||||
v-model:value="line.wallet_id"
|
||||
:options="productOptionsForLine(index)"
|
||||
:items="productItemsForLine(index)"
|
||||
key-name="wallet_id"
|
||||
value-name="displayName"
|
||||
placeholder="انتخاب محصول"
|
||||
:loading="optionsLoading"
|
||||
:has-tooltip="false"
|
||||
@change="() => onProductChange(index)"
|
||||
/>
|
||||
</a-form-item>
|
||||
@@ -44,8 +61,10 @@
|
||||
class="!mb-0"
|
||||
:name="['lines', index, 'value']"
|
||||
:rules="inputRules()"
|
||||
label="مقدار"
|
||||
>
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="مقدار" :help="physicalWithdrawAmountHelp" />
|
||||
</template>
|
||||
<BasicInput v-model:value="line.value" type="number" min="0" />
|
||||
</a-form-item>
|
||||
<div class="flex items-end">
|
||||
@@ -73,6 +92,14 @@
|
||||
import { inputRules } from '@/utils/inputRules'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import AdminLabelWithHelp from '@/components/AdminLabelWithHelp.vue'
|
||||
import AdminFieldHelp from '@/components/AdminFieldHelp.vue'
|
||||
import {
|
||||
physicalWithdrawAddressHelp,
|
||||
physicalWithdrawAmountHelp,
|
||||
physicalWithdrawCustomerHelp,
|
||||
physicalWithdrawProductHelp
|
||||
} from '@/utils/adminHelpCopy'
|
||||
import {
|
||||
getPhysicalWithdrawOptions,
|
||||
type PhysicalWithdrawAddressOption,
|
||||
@@ -100,21 +127,21 @@ const optionsLoading = ref(false)
|
||||
const products = ref<PhysicalWithdrawProductOption[]>([])
|
||||
const addresses = ref<PhysicalWithdrawAddressOption[]>([])
|
||||
|
||||
const customerOptions = computed(() =>
|
||||
const customerItems = computed(() =>
|
||||
(props.customers ?? []).map((customer) => ({
|
||||
label: `${customer.name ?? '-'} (${customer.mobile ?? '-'})`,
|
||||
value: customer.id
|
||||
...customer,
|
||||
displayName: `${customer.name ?? '-'} (${customer.mobile ?? '-'})`
|
||||
}))
|
||||
)
|
||||
|
||||
const addressOptions = computed(() =>
|
||||
const addressItems = computed(() =>
|
||||
addresses.value.map((addr) => ({
|
||||
label: [addr.province, addr.city, addr.address].filter(Boolean).join(' / '),
|
||||
value: addr.id
|
||||
...addr,
|
||||
displayName: [addr.province, addr.city, addr.address].filter(Boolean).join(' / ')
|
||||
}))
|
||||
)
|
||||
|
||||
function productOptionsForLine(index: number) {
|
||||
function productItemsForLine(index: number) {
|
||||
const usedWalletIds = props.form.lines
|
||||
.map((line, i) => (i === index ? undefined : line.wallet_id))
|
||||
.filter(Boolean)
|
||||
@@ -122,8 +149,8 @@ function productOptionsForLine(index: number) {
|
||||
return products.value
|
||||
.filter((product) => !usedWalletIds.includes(product.wallet_id))
|
||||
.map((product) => ({
|
||||
label: `${product.title} (موجودی: ${product.available} ${product.unit})`,
|
||||
value: product.wallet_id
|
||||
...product,
|
||||
displayName: `${product.title} (موجودی: ${product.available} ${product.unit})`
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +1,81 @@
|
||||
<template>
|
||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||
<BasicShowContext title="لیست برداشت فیزیکی">
|
||||
<template #extra>
|
||||
<AdminFieldHelp :text="physicalWithdrawPageIntro" />
|
||||
</template>
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<BaseTable class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<BaseButton type="primary" size="small" icon="vuesax-linear:add-square" @click="emits('add')">
|
||||
ثبت برداشت فیزیکی
|
||||
</BaseButton>
|
||||
<div class="flex items-center gap-2">
|
||||
<BaseButton type="primary" size="small" icon="vuesax-linear:add-square" @click="emits('add')">
|
||||
ثبت برداشت فیزیکی
|
||||
</BaseButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<div class="flex flex-col items-center gap-2 py-1">
|
||||
<div
|
||||
v-for="tx in record.transaction ?? []"
|
||||
:key="tx.id"
|
||||
class="flex w-full max-w-[220px] items-center justify-between gap-2 rounded border border-gray-200 px-2 py-1"
|
||||
>
|
||||
<span class="truncate text-xs">{{ tx.title?.trim() || 'محصول' }}</span>
|
||||
<span class="shrink-0 text-xs font-semibold">{{ Math.abs(Number(tx.gold ?? 0)) }}</span>
|
||||
<div v-if="canAdjust(record)" class="flex shrink-0 gap-1">
|
||||
<BaseButton
|
||||
size="small"
|
||||
class="!min-w-0 !px-2"
|
||||
:loading="isAdjusting(record.id, tx.id, 'decrease')"
|
||||
@click="handleAdjust(record, tx, 'decrease')"
|
||||
>
|
||||
کم
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
size="small"
|
||||
type="primary"
|
||||
class="!min-w-0 !px-2"
|
||||
:loading="isAdjusting(record.id, tx.id, 'increase')"
|
||||
@click="handleAdjust(record, tx, 'increase')"
|
||||
>
|
||||
زیاد
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<a-tooltip v-if="canAdjust(record)">
|
||||
<template #title>ویرایش مقدار</template>
|
||||
<Icon
|
||||
icon="vuesax-linear:edit-2"
|
||||
color="#2563EB"
|
||||
class="cursor-pointer"
|
||||
@click="openAdjustModal(record)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-2">
|
||||
<BaseButton
|
||||
v-if="canApprove(record)"
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="isApproving(record.id)"
|
||||
<a-tooltip v-if="canApprove(record)">
|
||||
<template #title>{{ physicalWithdrawApproveHelp }}</template>
|
||||
<Icon
|
||||
icon="vuesax-linear:tick-circle"
|
||||
color="#16A34A"
|
||||
class="cursor-pointer"
|
||||
:class="{ 'opacity-40 pointer-events-none': isApproving(record.id) }"
|
||||
@click="handleApprove(record)"
|
||||
>
|
||||
تایید
|
||||
</BaseButton>
|
||||
<ADropdown v-if="canChangeStatus(record)" :trigger="['click']">
|
||||
<BaseButton size="small">وضعیت</BaseButton>
|
||||
<template #overlay>
|
||||
<a-menu class="flex flex-col">
|
||||
<a-menu-item
|
||||
v-for="(item, key) in statusOptions(record.sts)"
|
||||
:key="key"
|
||||
class="border-none px-2 py-1"
|
||||
@click="handleStatusChange(record.id, Number(key))"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
<a-tooltip v-if="record.sts >= 50 && record.sts <= 70">
|
||||
<template #title>دریافت فاکتور</template>
|
||||
<Icon
|
||||
icon="vuesax-linear:document-download"
|
||||
color="#1D4ED8"
|
||||
class="cursor-pointer"
|
||||
@click="$router.push({ name: 'factor', params: { id: record.id } })"
|
||||
/>
|
||||
/>
|
||||
</a-tooltip>
|
||||
|
||||
<ADropdown v-if="canChangeStatus(record)" :trigger="['click']">
|
||||
<a-tooltip>
|
||||
<template #title>{{ physicalWithdrawStatusHelp }}</template>
|
||||
<Icon icon="vuesax-linear:status-up" color="#CA8A04" class="cursor-pointer" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu class="flex flex-col min-w-[10rem]">
|
||||
<a-menu-item
|
||||
v-for="(item, key) in statusOptions(record.sts)"
|
||||
:key="key"
|
||||
class="border-none px-3 py-2"
|
||||
:style="{ color: item.color }"
|
||||
@click="handleStatusChange(record.id, Number(key))"
|
||||
>
|
||||
{{ item.title }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</ADropdown>
|
||||
|
||||
<a-tooltip v-if="record.sts >= 50 && record.sts <= 70">
|
||||
<template #title>دریافت فاکتور</template>
|
||||
<Icon
|
||||
icon="vuesax-linear:document-download"
|
||||
color="#1D4ED8"
|
||||
class="cursor-pointer"
|
||||
@click="$router.push({ name: 'factor', params: { id: record.id } })"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
</BasicShowContext>
|
||||
|
||||
<PhysicalWithdrawAdjustModal
|
||||
v-model:visible="adjustVisible"
|
||||
:withdraw-id="selectedWithdrawId"
|
||||
@success="reload"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -86,21 +84,25 @@ import type { TableInput } from '@core/type/baseTable'
|
||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import { getPhysicalWithdrawColumns } from '../hooks/physicalWithdrawColumns'
|
||||
import {
|
||||
adjustPhysicalWithdraw,
|
||||
approvePhysicalWithdraw,
|
||||
getPhysicalWithdrawTable
|
||||
} from '@/api/physicalWithdraw'
|
||||
import { approvePhysicalWithdraw, getPhysicalWithdrawTable } from '@/api/physicalWithdraw'
|
||||
import { updateSts } from '@/api/wallet'
|
||||
import { message } from 'ant-design-vue'
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
import { status as physicalStatusMap } from '@/pages/menuPages/withdraw/information/hooks/status'
|
||||
import AdminFieldHelp from '@/components/AdminFieldHelp.vue'
|
||||
import PhysicalWithdrawAdjustModal from './PhysicalWithdrawAdjustModal.vue'
|
||||
import {
|
||||
physicalWithdrawApproveHelp,
|
||||
physicalWithdrawPageIntro,
|
||||
physicalWithdrawStatusHelp
|
||||
} from '@/utils/adminHelpCopy'
|
||||
|
||||
const emits = defineEmits(['add'])
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const adjustingKey = ref('')
|
||||
const approvingId = ref<number | null>(null)
|
||||
const adjustVisible = ref(false)
|
||||
const selectedWithdrawId = ref<number>()
|
||||
|
||||
const tblProps = computed(() => ({
|
||||
columns: getPhysicalWithdrawColumns(),
|
||||
@@ -108,7 +110,7 @@ const tblProps = computed(() => ({
|
||||
}))
|
||||
|
||||
function canAdjust(record: Withdraw) {
|
||||
return Number(record.sts) === 50
|
||||
return Number(record.sts) === 50 && (record.transaction?.length ?? 0) > 0
|
||||
}
|
||||
|
||||
function canApprove(record: Withdraw) {
|
||||
@@ -126,39 +128,18 @@ function statusOptions(currentSts: number) {
|
||||
)
|
||||
}
|
||||
|
||||
function adjustKey(withdrawId?: number, transactionId?: number, direction?: string) {
|
||||
return `${withdrawId ?? 0}-${transactionId ?? 0}-${direction ?? ''}`
|
||||
}
|
||||
|
||||
function isAdjusting(withdrawId?: number, transactionId?: number, direction?: string) {
|
||||
return adjustingKey.value === adjustKey(withdrawId, transactionId, direction)
|
||||
}
|
||||
|
||||
function isApproving(withdrawId?: number) {
|
||||
return approvingId.value === withdrawId
|
||||
}
|
||||
|
||||
async function handleAdjust(
|
||||
record: Withdraw,
|
||||
tx: { id?: number },
|
||||
direction: 'increase' | 'decrease'
|
||||
) {
|
||||
if (!record.id || !tx.id) return
|
||||
const key = adjustKey(record.id, tx.id, direction)
|
||||
adjustingKey.value = key
|
||||
try {
|
||||
await adjustPhysicalWithdraw(record.id, { transaction_id: tx.id, direction })
|
||||
message.success('مقدار بروزرسانی شد')
|
||||
await reload()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
if (adjustingKey.value === key) adjustingKey.value = ''
|
||||
}
|
||||
function openAdjustModal(record: Withdraw) {
|
||||
if (!record.id) return
|
||||
selectedWithdrawId.value = record.id
|
||||
adjustVisible.value = true
|
||||
}
|
||||
|
||||
async function handleApprove(record: Withdraw) {
|
||||
if (!record.id) return
|
||||
if (!record.id || isApproving(record.id)) return
|
||||
approvingId.value = record.id
|
||||
try {
|
||||
await approvePhysicalWithdraw(record.id, 51)
|
||||
|
||||
@@ -3,15 +3,25 @@ import { toJalali } from '@core/utils/useDataTime'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
|
||||
function formatLineQty(tx: { gold?: number; product?: { unit?: string; trade_by_quantity?: boolean } }, title: string): string {
|
||||
const qty = Math.abs(Number(tx.gold ?? 0))
|
||||
const unit = tx.product?.unit || 'گرم'
|
||||
const decimals = tx.product?.trade_by_quantity ? 0 : 3
|
||||
const formatted = qty.toLocaleString('fa-IR', {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: decimals
|
||||
})
|
||||
return `${title}: ${formatted} ${unit}`
|
||||
}
|
||||
|
||||
function formatProductsSummary(record: Withdraw): string {
|
||||
const lines = record.transaction ?? []
|
||||
if (!lines.length) return '-'
|
||||
|
||||
return lines
|
||||
.map((tx) => {
|
||||
const qty = Math.abs(Number(tx.gold ?? 0))
|
||||
const title = String(tx.title ?? '').trim() || 'محصول'
|
||||
return `${title}: ${qty}`
|
||||
const title = String(tx.title ?? '').trim() || tx.product?.title || 'محصول'
|
||||
return formatLineQty(tx, title)
|
||||
})
|
||||
.join(' | ')
|
||||
}
|
||||
@@ -87,7 +97,7 @@ export function getPhysicalWithdrawColumns(): BaseTableColumn[] {
|
||||
title: 'عملیات',
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: 260,
|
||||
width: 140,
|
||||
slot: 'action',
|
||||
filterable: false,
|
||||
fixed: 'right'
|
||||
|
||||
@@ -61,6 +61,7 @@ async function loadCustomers() {
|
||||
customers.value = (await getCustomersList()) ?? []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error('خطا در بارگذاری لیست مشتریان')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +76,7 @@ function resetForm() {
|
||||
|
||||
function openCreate() {
|
||||
resetForm()
|
||||
void loadCustomers()
|
||||
createVisible.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<div class="flex items-center gap-2">
|
||||
<AdminFieldHelp :text="pricingPageIntro" />
|
||||
<BaseButton
|
||||
type="primary"
|
||||
size="small"
|
||||
|
||||
Reference in New Issue
Block a user