added some text handling 2
This commit is contained in:
@@ -0,0 +1,55 @@
|
|||||||
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
|
import useAxios from '@core/utils/useAxios'
|
||||||
|
import type { Withdraw } from '@/model/wallet'
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawProductOption {
|
||||||
|
product_id: number
|
||||||
|
title: string
|
||||||
|
wallet_id: number
|
||||||
|
available: number
|
||||||
|
unit: string
|
||||||
|
is_main_wallet?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawAddressOption {
|
||||||
|
id: number
|
||||||
|
province?: string
|
||||||
|
city?: string
|
||||||
|
address?: string
|
||||||
|
phone?: string
|
||||||
|
zip?: string
|
||||||
|
default_address?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawOptions {
|
||||||
|
products: PhysicalWithdrawProductOption[]
|
||||||
|
addresses: PhysicalWithdrawAddressOption[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawLineInput {
|
||||||
|
wallet_id: number
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreatePhysicalWithdrawPayload {
|
||||||
|
customer_id: number
|
||||||
|
address_id: number
|
||||||
|
wallets: PhysicalWithdrawLineInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPhysicalWithdrawTable = async (params?: TableInput) =>
|
||||||
|
await useAxios.post<Array<Withdraw>>('/admin/wallet/withdraw_physical/table', params)
|
||||||
|
|
||||||
|
export const getPhysicalWithdrawOptions = async (customerId: number) =>
|
||||||
|
await useAxios.get<PhysicalWithdrawOptions>(`/admin/wallet/withdraw_physical/options/${customerId}`)
|
||||||
|
|
||||||
|
export const createPhysicalWithdraw = async (payload: CreatePhysicalWithdrawPayload) =>
|
||||||
|
await useAxios.post<{ id: number }>('/admin/wallet/withdraw_physical', payload)
|
||||||
|
|
||||||
|
export const adjustPhysicalWithdraw = async (
|
||||||
|
withdrawId: number,
|
||||||
|
payload: { transaction_id: number; direction: 'increase' | 'decrease' }
|
||||||
|
) => await useAxios.post(`/admin/wallet/withdraw_physical/${withdrawId}/adjust`, payload)
|
||||||
|
|
||||||
|
export const approvePhysicalWithdraw = async (withdrawId: number, sts = 51) =>
|
||||||
|
await useAxios.put(`/admin/wallet/withdraw/${withdrawId}`, { sts })
|
||||||
@@ -60,8 +60,10 @@ export interface WalletTableSums {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WithdrawTransaction {
|
export interface WithdrawTransaction {
|
||||||
|
id?: number
|
||||||
title?: string
|
title?: string
|
||||||
gold?: number
|
gold?: number
|
||||||
|
product?: { title?: string; amount?: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Withdraw {
|
export interface Withdraw {
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical" autocomplete="off">
|
||||||
|
<a-form-item label="مشتری" name="customer_id" :rules="inputRules()">
|
||||||
|
<BasicSelect
|
||||||
|
v-model:value="form.customer_id"
|
||||||
|
show-search
|
||||||
|
:options="customerOptions"
|
||||||
|
placeholder="انتخاب مشتری"
|
||||||
|
@change="onCustomerChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item v-if="form.customer_id" label="آدرس تحویل" name="address_id" :rules="inputRules()">
|
||||||
|
<BasicSelect
|
||||||
|
v-model:value="form.address_id"
|
||||||
|
:options="addressOptions"
|
||||||
|
placeholder="انتخاب آدرس"
|
||||||
|
:loading="optionsLoading"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<div v-if="form.customer_id" class="space-y-3">
|
||||||
|
<div class="text-sm font-semibold">محصولات</div>
|
||||||
|
<div
|
||||||
|
v-for="(line, index) in form.lines"
|
||||||
|
:key="`${line.wallet_id}-${index}`"
|
||||||
|
class="grid grid-cols-1 gap-2 rounded-lg border border-gray-200 p-3 md:grid-cols-[1fr_120px_auto]"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
class="!mb-0"
|
||||||
|
:name="['lines', index, 'wallet_id']"
|
||||||
|
:rules="inputRules()"
|
||||||
|
label="محصول"
|
||||||
|
>
|
||||||
|
<BasicSelect
|
||||||
|
v-model:value="line.wallet_id"
|
||||||
|
:options="productOptionsForLine(index)"
|
||||||
|
placeholder="انتخاب محصول"
|
||||||
|
:loading="optionsLoading"
|
||||||
|
@change="() => onProductChange(index)"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
class="!mb-0"
|
||||||
|
:name="['lines', index, 'value']"
|
||||||
|
:rules="inputRules()"
|
||||||
|
label="مقدار"
|
||||||
|
>
|
||||||
|
<BasicInput v-model:value="line.value" type="number" min="0" />
|
||||||
|
</a-form-item>
|
||||||
|
<div class="flex items-end">
|
||||||
|
<BaseButton
|
||||||
|
v-if="form.lines.length > 1"
|
||||||
|
type="text"
|
||||||
|
danger
|
||||||
|
class="mb-1"
|
||||||
|
@click="removeLine(index)"
|
||||||
|
>
|
||||||
|
حذف
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
<div v-if="line.wallet_id" class="col-span-full text-xs text-gray-500">
|
||||||
|
موجودی: {{ availableForWallet(line.wallet_id) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseButton type="dashed" block icon="vuesax-linear:add" @click="addLine">افزودن محصول</BaseButton>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { inputRules } from '@/utils/inputRules'
|
||||||
|
import type { Customer } from '@/model/customer'
|
||||||
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
|
import {
|
||||||
|
getPhysicalWithdrawOptions,
|
||||||
|
type PhysicalWithdrawAddressOption,
|
||||||
|
type PhysicalWithdrawProductOption
|
||||||
|
} from '@/api/physicalWithdraw'
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawFormLine {
|
||||||
|
wallet_id?: number
|
||||||
|
value?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawForm {
|
||||||
|
customer_id?: number
|
||||||
|
address_id?: number
|
||||||
|
lines: PhysicalWithdrawFormLine[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
form: PhysicalWithdrawForm
|
||||||
|
customers: Customer[]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const optionsLoading = ref(false)
|
||||||
|
const products = ref<PhysicalWithdrawProductOption[]>([])
|
||||||
|
const addresses = ref<PhysicalWithdrawAddressOption[]>([])
|
||||||
|
|
||||||
|
const customerOptions = computed(() =>
|
||||||
|
(props.customers ?? []).map((customer) => ({
|
||||||
|
label: `${customer.name ?? '-'} (${customer.mobile ?? '-'})`,
|
||||||
|
value: customer.id
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
const addressOptions = computed(() =>
|
||||||
|
addresses.value.map((addr) => ({
|
||||||
|
label: [addr.province, addr.city, addr.address].filter(Boolean).join(' / '),
|
||||||
|
value: addr.id
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
function productOptionsForLine(index: number) {
|
||||||
|
const usedWalletIds = props.form.lines
|
||||||
|
.map((line, i) => (i === index ? undefined : line.wallet_id))
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
return products.value
|
||||||
|
.filter((product) => !usedWalletIds.includes(product.wallet_id))
|
||||||
|
.map((product) => ({
|
||||||
|
label: `${product.title} (موجودی: ${product.available} ${product.unit})`,
|
||||||
|
value: product.wallet_id
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function availableForWallet(walletId?: number) {
|
||||||
|
const product = products.value.find((item) => item.wallet_id === walletId)
|
||||||
|
if (!product) return '-'
|
||||||
|
return `${product.available} ${product.unit}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLine() {
|
||||||
|
props.form.lines.push({ wallet_id: undefined, value: undefined })
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLine(index: number) {
|
||||||
|
props.form.lines.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onProductChange(index: number) {
|
||||||
|
const line = props.form.lines[index]
|
||||||
|
const product = products.value.find((item) => item.wallet_id === line.wallet_id)
|
||||||
|
if (product && !line.value) {
|
||||||
|
line.value = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onCustomerChange() {
|
||||||
|
props.form.address_id = undefined
|
||||||
|
props.form.lines = [{ wallet_id: undefined, value: undefined }]
|
||||||
|
products.value = []
|
||||||
|
addresses.value = []
|
||||||
|
|
||||||
|
if (!props.form.customer_id) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
optionsLoading.value = true
|
||||||
|
const data = await getPhysicalWithdrawOptions(props.form.customer_id)
|
||||||
|
products.value = data.products ?? []
|
||||||
|
addresses.value = data.addresses ?? []
|
||||||
|
if (addresses.value.length === 1) {
|
||||||
|
props.form.address_id = addresses.value[0].id
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
optionsLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateFields() {
|
||||||
|
return formRef.value?.validateFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetFields() {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
validateFields,
|
||||||
|
resetFields
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
|
<BasicShowContext title="لیست برداشت فیزیکی">
|
||||||
|
<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>
|
||||||
|
</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 flex-wrap items-center justify-center gap-2">
|
||||||
|
<BaseButton
|
||||||
|
v-if="canApprove(record)"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
:loading="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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseTable>
|
||||||
|
</div>
|
||||||
|
</BasicShowContext>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
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 { 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'
|
||||||
|
|
||||||
|
const emits = defineEmits(['add'])
|
||||||
|
|
||||||
|
const [submit, reload] = useBaseTable()
|
||||||
|
const adjustingKey = ref('')
|
||||||
|
const approvingId = ref<number | null>(null)
|
||||||
|
|
||||||
|
const tblProps = computed(() => ({
|
||||||
|
columns: getPhysicalWithdrawColumns(),
|
||||||
|
api: (params: TableInput) => getPhysicalWithdrawTable(params)
|
||||||
|
}))
|
||||||
|
|
||||||
|
function canAdjust(record: Withdraw) {
|
||||||
|
return Number(record.sts) === 50
|
||||||
|
}
|
||||||
|
|
||||||
|
function canApprove(record: Withdraw) {
|
||||||
|
return Number(record.sts) === 50
|
||||||
|
}
|
||||||
|
|
||||||
|
function canChangeStatus(record: Withdraw) {
|
||||||
|
const sts = Number(record.sts)
|
||||||
|
return sts >= 50 && sts <= 53
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusOptions(currentSts: number) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(physicalStatusMap).filter(([key]) => Number(key) !== Number(currentSts))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleApprove(record: Withdraw) {
|
||||||
|
if (!record.id) return
|
||||||
|
approvingId.value = record.id
|
||||||
|
try {
|
||||||
|
await approvePhysicalWithdraw(record.id, 51)
|
||||||
|
message.success('برداشت فیزیکی تایید شد')
|
||||||
|
await reload()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
approvingId.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleStatusChange(withdrawId: number, sts: number) {
|
||||||
|
try {
|
||||||
|
await updateSts(withdrawId, { sts })
|
||||||
|
message.success('وضعیت ثبت شد')
|
||||||
|
await reload()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
fetch: reload
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import type { BaseTableColumn } from '@core/type/baseTable'
|
||||||
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
|
import type { Withdraw } from '@/model/wallet'
|
||||||
|
|
||||||
|
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}`
|
||||||
|
})
|
||||||
|
.join(' | ')
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAddress(record: Withdraw & { address?: { province?: string; city?: string; address?: string } }): string {
|
||||||
|
const addr = record.address ?? (record as { Address?: { province?: string; city?: string; address?: string } }).Address
|
||||||
|
if (!addr) return '-'
|
||||||
|
const parts = [addr.province, addr.city, addr.address].filter(Boolean)
|
||||||
|
return parts.length ? parts.join(' / ') : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPhysicalWithdrawColumns(): BaseTableColumn[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'ردیف',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'row',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
|
customRender: (record: { index: number }) => record.index + 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'کد سند',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
type: 'Number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'مشتری',
|
||||||
|
dataIndex: 'customer.name',
|
||||||
|
key: 'customer.name',
|
||||||
|
type: 'Text'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'محصولات',
|
||||||
|
dataIndex: 'transaction',
|
||||||
|
key: 'products',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
|
customRender: ({ record }) => (
|
||||||
|
<div class="max-w-xs whitespace-normal text-center text-xs leading-5">{formatProductsSummary(record)}</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'آدرس',
|
||||||
|
dataIndex: 'address',
|
||||||
|
key: 'address',
|
||||||
|
filterable: false,
|
||||||
|
sortable: false,
|
||||||
|
customRender: ({ record }) => (
|
||||||
|
<div class="max-w-xs whitespace-normal text-center text-xs leading-5">{formatAddress(record)}</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
dataIndex: 'sts',
|
||||||
|
key: 'sts',
|
||||||
|
filterable: false,
|
||||||
|
customRender: ({ record }) => (
|
||||||
|
<CustomTag textColor="#ca8a04" bgColor="#fef3c7">
|
||||||
|
{record?.sts_str ?? '-'}
|
||||||
|
</CustomTag>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'تاریخ ثبت',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
key: 'created_at',
|
||||||
|
type: 'DateTime',
|
||||||
|
customRender: (record: { record: { created_at?: string } }) => toJalali(record.record.created_at)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'عملیات',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
width: 260,
|
||||||
|
slot: 'action',
|
||||||
|
filterable: false,
|
||||||
|
fixed: 'right'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full min-h-0">
|
||||||
|
<BasicMainCard>
|
||||||
|
<div class="flex h-full min-h-0 w-full flex-col overflow-hidden">
|
||||||
|
<tbl ref="table" class="min-h-0 flex-1 w-full" @add="openCreate" />
|
||||||
|
</div>
|
||||||
|
</BasicMainCard>
|
||||||
|
|
||||||
|
<BasicActionSheet
|
||||||
|
v-model:visible="createVisible"
|
||||||
|
title="ثبت برداشت فیزیکی"
|
||||||
|
modal-width="32rem"
|
||||||
|
drawer-height="90vh"
|
||||||
|
:loading="loading"
|
||||||
|
:show-button="true"
|
||||||
|
:show-cancel="true"
|
||||||
|
:show-ok="true"
|
||||||
|
ok-text="ثبت"
|
||||||
|
cancel-text="انصراف"
|
||||||
|
@ok="submitCreate"
|
||||||
|
@close="closeCreate"
|
||||||
|
@cancel="closeCreate"
|
||||||
|
>
|
||||||
|
<PhysicalWithdrawEditorFormBody
|
||||||
|
ref="editorRef"
|
||||||
|
:form="form"
|
||||||
|
:customers="customers"
|
||||||
|
/>
|
||||||
|
</BasicActionSheet>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import tbl from './components/table.vue'
|
||||||
|
import BasicActionSheet from '@/components/BasicActionSheet.vue'
|
||||||
|
import PhysicalWithdrawEditorFormBody, {
|
||||||
|
type PhysicalWithdrawForm
|
||||||
|
} from './components/PhysicalWithdrawEditorFormBody.vue'
|
||||||
|
import { createPhysicalWithdraw } from '@/api/physicalWithdraw'
|
||||||
|
import { getCustomersList } from '@/api/customer'
|
||||||
|
import type { Customer } from '@/model/customer'
|
||||||
|
|
||||||
|
const table = ref<{ fetch: () => Promise<void> } | null>(null)
|
||||||
|
const editorRef = ref<{
|
||||||
|
validateFields?: () => Promise<unknown>
|
||||||
|
resetFields?: () => void
|
||||||
|
}>()
|
||||||
|
const loading = ref(false)
|
||||||
|
const createVisible = ref(false)
|
||||||
|
const customers = ref<Customer[]>([])
|
||||||
|
|
||||||
|
const form = ref<PhysicalWithdrawForm>({
|
||||||
|
customer_id: undefined,
|
||||||
|
address_id: undefined,
|
||||||
|
lines: [{ wallet_id: undefined, value: undefined }]
|
||||||
|
})
|
||||||
|
|
||||||
|
async function loadCustomers() {
|
||||||
|
try {
|
||||||
|
customers.value = (await getCustomersList()) ?? []
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
form.value = {
|
||||||
|
customer_id: undefined,
|
||||||
|
address_id: undefined,
|
||||||
|
lines: [{ wallet_id: undefined, value: undefined }]
|
||||||
|
}
|
||||||
|
editorRef.value?.resetFields?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreate() {
|
||||||
|
resetForm()
|
||||||
|
createVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCreate() {
|
||||||
|
createVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitCreate() {
|
||||||
|
try {
|
||||||
|
await editorRef.value?.validateFields?.()
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = form.value.lines.filter((line) => line.wallet_id && Number(line.value) > 0)
|
||||||
|
if (!lines.length) {
|
||||||
|
message.error('حداقل یک محصول با مقدار معتبر انتخاب کنید')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await createPhysicalWithdraw({
|
||||||
|
customer_id: Number(form.value.customer_id),
|
||||||
|
address_id: Number(form.value.address_id),
|
||||||
|
wallets: lines.map((line) => ({
|
||||||
|
wallet_id: Number(line.wallet_id),
|
||||||
|
value: Number(line.value)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
message.success('برداشت فیزیکی با موفقیت ثبت شد')
|
||||||
|
createVisible.value = false
|
||||||
|
resetForm()
|
||||||
|
await table.value?.fetch?.()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void loadCustomers()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
|
|
||||||
|
const PhysicalWithdraw: RouteRecordRaw = {
|
||||||
|
name: 'physicalWithdraw',
|
||||||
|
path: '/physical-withdraw',
|
||||||
|
component: () => import('@core/layouts/BasicLayout.vue'),
|
||||||
|
redirect: { name: 'physicalWithdrawMain' },
|
||||||
|
meta: {
|
||||||
|
icon: 'vuesax-linear:truck-tick',
|
||||||
|
title: 'برداشت فیزیکی',
|
||||||
|
disabled: false,
|
||||||
|
hideMenu: false,
|
||||||
|
hideChildren: true,
|
||||||
|
order: 7.5
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'physicalWithdrawMain',
|
||||||
|
path: '',
|
||||||
|
component: () => import('@/pages/menuPages/physicalWithdraw/index.vue'),
|
||||||
|
meta: {
|
||||||
|
icon: 'vuesax-linear:truck-tick',
|
||||||
|
title: 'برداشت فیزیکی',
|
||||||
|
disabled: false,
|
||||||
|
hideMenu: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PhysicalWithdraw
|
||||||
Reference in New Issue
Block a user