added some text handling 3
This commit is contained in:
Vendored
+4
@@ -10,6 +10,7 @@ export {}
|
|||||||
declare module '@vue/runtime-core' {
|
declare module '@vue/runtime-core' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
_index: typeof import('./src/core/components/_sample/_index.vue')['default']
|
_index: typeof import('./src/core/components/_sample/_index.vue')['default']
|
||||||
|
AAlert: typeof import('ant-design-vue/es')['Alert']
|
||||||
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||||
AButton: typeof import('ant-design-vue/es')['Button']
|
AButton: typeof import('ant-design-vue/es')['Button']
|
||||||
@@ -21,6 +22,7 @@ declare module '@vue/runtime-core' {
|
|||||||
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
|
||||||
ADivider: typeof import('ant-design-vue/es')['Divider']
|
ADivider: typeof import('ant-design-vue/es')['Divider']
|
||||||
AdminFieldHelp: typeof import('./src/components/AdminFieldHelp.vue')['default']
|
AdminFieldHelp: typeof import('./src/components/AdminFieldHelp.vue')['default']
|
||||||
|
AdminHelpTooltipContent: typeof import('./src/components/AdminHelpTooltipContent.vue')['default']
|
||||||
AdminLabelWithHelp: typeof import('./src/components/AdminLabelWithHelp.vue')['default']
|
AdminLabelWithHelp: typeof import('./src/components/AdminLabelWithHelp.vue')['default']
|
||||||
ADrawer: typeof import('ant-design-vue/es')['Drawer']
|
ADrawer: typeof import('ant-design-vue/es')['Drawer']
|
||||||
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
|
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
|
||||||
@@ -41,6 +43,7 @@ declare module '@vue/runtime-core' {
|
|||||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||||
|
ARadio: typeof import('ant-design-vue/es')['Radio']
|
||||||
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
ARadioButton: typeof import('ant-design-vue/es')['RadioButton']
|
||||||
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
|
||||||
ARow: typeof import('ant-design-vue/es')['Row']
|
ARow: typeof import('ant-design-vue/es')['Row']
|
||||||
@@ -56,6 +59,7 @@ declare module '@vue/runtime-core' {
|
|||||||
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
ATextarea: typeof import('ant-design-vue/es')['Textarea']
|
||||||
ATimePicker: typeof import('ant-design-vue/es')['TimePicker']
|
ATimePicker: typeof import('ant-design-vue/es')['TimePicker']
|
||||||
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
|
||||||
|
AUpload: typeof import('ant-design-vue/es')['Upload']
|
||||||
BaseButton: typeof import('./src/core/components/BaseButton/index.ts')['default']
|
BaseButton: typeof import('./src/core/components/BaseButton/index.ts')['default']
|
||||||
BaseCard: typeof import('./src/core/components/BaseCard/index.ts')['default']
|
BaseCard: typeof import('./src/core/components/BaseCard/index.ts')['default']
|
||||||
BaseCollapse: typeof import('./src/components/BaseCollapse.vue')['default']
|
BaseCollapse: typeof import('./src/components/BaseCollapse.vue')['default']
|
||||||
|
|||||||
@@ -46,9 +46,35 @@ export const getPhysicalWithdrawOptions = async (customerId: number) =>
|
|||||||
export const createPhysicalWithdraw = async (payload: CreatePhysicalWithdrawPayload) =>
|
export const createPhysicalWithdraw = async (payload: CreatePhysicalWithdrawPayload) =>
|
||||||
await useAxios.post<{ id: number }>('/admin/wallet/withdraw_physical', payload)
|
await useAxios.post<{ id: number }>('/admin/wallet/withdraw_physical', payload)
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawAdjustLine {
|
||||||
|
transaction_id: number
|
||||||
|
title: string
|
||||||
|
current_value: number
|
||||||
|
available_add: number
|
||||||
|
max_value: number
|
||||||
|
min_value: number
|
||||||
|
allows_decimal: boolean
|
||||||
|
unit: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawAdjustInfo {
|
||||||
|
withdraw_id: number
|
||||||
|
customer_name: string
|
||||||
|
customer_mobile: string
|
||||||
|
lines: PhysicalWithdrawAdjustLine[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalWithdrawAdjustItem {
|
||||||
|
transaction_id: number
|
||||||
|
value: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPhysicalWithdrawAdjustInfo = async (withdrawId: number) =>
|
||||||
|
await useAxios.get<PhysicalWithdrawAdjustInfo>(`/admin/wallet/withdraw_physical/${withdrawId}/adjust-info`)
|
||||||
|
|
||||||
export const adjustPhysicalWithdraw = async (
|
export const adjustPhysicalWithdraw = async (
|
||||||
withdrawId: number,
|
withdrawId: number,
|
||||||
payload: { transaction_id: number; direction: 'increase' | 'decrease' }
|
payload: { items: PhysicalWithdrawAdjustItem[] }
|
||||||
) => await useAxios.post(`/admin/wallet/withdraw_physical/${withdrawId}/adjust`, payload)
|
) => await useAxios.post(`/admin/wallet/withdraw_physical/${withdrawId}/adjust`, payload)
|
||||||
|
|
||||||
export const approvePhysicalWithdraw = async (withdrawId: number, sts = 51) =>
|
export const approvePhysicalWithdraw = async (withdrawId: number, sts = 51) =>
|
||||||
|
|||||||
@@ -1,23 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="text"
|
v-if="hasContent"
|
||||||
placement="top"
|
placement="top"
|
||||||
:title="text"
|
|
||||||
overlay-class-name="admin-field-help-tooltip"
|
overlay-class-name="admin-field-help-tooltip"
|
||||||
:overlay-style="{ zIndex: TOOLTIP_ABOVE_MODAL_Z_INDEX }"
|
:overlay-style="{ zIndex: TOOLTIP_ABOVE_MODAL_Z_INDEX }"
|
||||||
:overlay-inner-style="{
|
:overlay-inner-style="tooltipInnerStyle"
|
||||||
maxWidth: '22rem',
|
|
||||||
direction: 'rtl',
|
|
||||||
textAlign: 'right',
|
|
||||||
whiteSpace: 'pre-line',
|
|
||||||
}"
|
|
||||||
:mouse-enter-delay="0.15"
|
:mouse-enter-delay="0.15"
|
||||||
>
|
>
|
||||||
|
<template #title>
|
||||||
|
<AdminHelpTooltipContent :content="parsed" />
|
||||||
|
</template>
|
||||||
<span
|
<span
|
||||||
class="admin-field-help"
|
class="admin-field-help"
|
||||||
:class="rootClass"
|
:class="rootClass"
|
||||||
role="img"
|
role="img"
|
||||||
:aria-label="text"
|
:aria-label="plainText"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<IosInfoBadge :size="size" />
|
<IosInfoBadge :size="size" />
|
||||||
@@ -27,11 +24,17 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import IosInfoBadge from '@/pages/dashboard/components/IosInfoBadge.vue'
|
import IosInfoBadge from '@/pages/dashboard/components/IosInfoBadge.vue'
|
||||||
|
import AdminHelpTooltipContent from '@/components/AdminHelpTooltipContent.vue'
|
||||||
import { TOOLTIP_ABOVE_MODAL_Z_INDEX } from '@/constants/overlayZIndex'
|
import { TOOLTIP_ABOVE_MODAL_Z_INDEX } from '@/constants/overlayZIndex'
|
||||||
|
import {
|
||||||
|
adminHelpPlainText,
|
||||||
|
parseAdminHelp,
|
||||||
|
type AdminHelpContent,
|
||||||
|
} from '@/utils/adminHelpFormat'
|
||||||
|
|
||||||
withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
text?: string
|
text?: string | AdminHelpContent
|
||||||
rootClass?: string
|
rootClass?: string
|
||||||
size?: 'xs' | 'sm' | 'md'
|
size?: 'xs' | 'sm' | 'md'
|
||||||
}>(),
|
}>(),
|
||||||
@@ -41,6 +44,16 @@ withDefaults(
|
|||||||
size: 'xs',
|
size: 'xs',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const parsed = computed(() => parseAdminHelp(props.text))
|
||||||
|
const hasContent = computed(() => Boolean(parsed.value.description || parsed.value.formula))
|
||||||
|
const plainText = computed(() => adminHelpPlainText(parsed.value))
|
||||||
|
|
||||||
|
const tooltipInnerStyle = {
|
||||||
|
maxWidth: '24rem',
|
||||||
|
direction: 'rtl',
|
||||||
|
textAlign: 'right',
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@@ -57,4 +70,9 @@ withDefaults(
|
|||||||
.ant-tooltip.admin-field-help-tooltip {
|
.ant-tooltip.admin-field-help-tooltip {
|
||||||
z-index: 1250 !important;
|
z-index: 1250 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-tooltip.admin-field-help-tooltip .ant-tooltip-inner {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div class="admin-help-tooltip-content" dir="rtl">
|
||||||
|
<p v-if="content.description" class="admin-help-tooltip-content__text">
|
||||||
|
{{ content.description }}
|
||||||
|
</p>
|
||||||
|
<div v-if="content.formula" class="admin-help-tooltip-content__formula">
|
||||||
|
<span class="admin-help-tooltip-content__formula-label">فرمول</span>
|
||||||
|
<p class="admin-help-tooltip-content__formula-text">{{ content.formula }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { AdminHelpContent } from '@/utils/adminHelpFormat'
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
content: AdminHelpContent
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.admin-help-tooltip-content {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-help-tooltip-content__text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-help-tooltip-content__formula {
|
||||||
|
margin-top: 0.55rem;
|
||||||
|
padding: 0.45rem 0.55rem;
|
||||||
|
border-radius: 0.45rem;
|
||||||
|
border: 1px solid rgba(245, 158, 11, 0.35);
|
||||||
|
background: rgba(245, 158, 11, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-help-tooltip-content__formula-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
font-size: 0.625rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
color: #b45309;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-help-tooltip-content__formula-text {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: inherit;
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.is-dark .admin-help-tooltip-content__formula {
|
||||||
|
border-color: rgba(252, 211, 77, 0.35);
|
||||||
|
background: rgba(252, 211, 77, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.is-dark .admin-help-tooltip-content__formula-label {
|
||||||
|
color: #fcd34d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -66,7 +66,7 @@ const localValue = computed({
|
|||||||
get() {
|
get() {
|
||||||
let a =
|
let a =
|
||||||
props.type === 'price'
|
props.type === 'price'
|
||||||
? props.value
|
? props.value != null && props.value !== ''
|
||||||
? usePrice(props.value as number, true, '').trim()
|
? usePrice(props.value as number, true, '').trim()
|
||||||
: ''
|
: ''
|
||||||
: props.value
|
: props.value
|
||||||
|
|||||||
+6
-1
@@ -63,7 +63,12 @@ export interface WithdrawTransaction {
|
|||||||
id?: number
|
id?: number
|
||||||
title?: string
|
title?: string
|
||||||
gold?: number
|
gold?: number
|
||||||
product?: { title?: string; amount?: number }
|
product?: {
|
||||||
|
title?: string
|
||||||
|
amount?: number
|
||||||
|
unit?: string
|
||||||
|
trade_by_quantity?: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Withdraw {
|
export interface Withdraw {
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
placement="top"
|
placement="top"
|
||||||
:title="tooltipText"
|
|
||||||
overlay-class-name="dashboard-help-tooltip"
|
overlay-class-name="dashboard-help-tooltip"
|
||||||
:overlay-style="{ zIndex: TOOLTIP_ABOVE_MODAL_Z_INDEX }"
|
:overlay-style="{ zIndex: TOOLTIP_ABOVE_MODAL_Z_INDEX }"
|
||||||
:overlay-inner-style="{ maxWidth: '26rem', direction: 'rtl', textAlign: 'right', whiteSpace: 'pre-line' }"
|
:overlay-inner-style="tooltipInnerStyle"
|
||||||
:mouse-enter-delay="0.15"
|
:mouse-enter-delay="0.15"
|
||||||
>
|
>
|
||||||
|
<template #title>
|
||||||
|
<AdminHelpTooltipContent :content="parsed" />
|
||||||
|
</template>
|
||||||
<span
|
<span
|
||||||
class="dashboard-help-trigger inline-flex shrink-0 items-center justify-center"
|
class="dashboard-help-trigger inline-flex shrink-0 items-center justify-center"
|
||||||
role="img"
|
role="img"
|
||||||
:aria-label="tooltipText"
|
:aria-label="plainText"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<IosInfoBadge :size="badgeSize" />
|
<IosInfoBadge :size="badgeSize" />
|
||||||
@@ -21,7 +23,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
import type { DashboardHelp } from '@/pages/dashboard/dashboardSectionCopy'
|
||||||
import IosInfoBadge from '@/pages/dashboard/components/IosInfoBadge.vue'
|
import IosInfoBadge from '@/pages/dashboard/components/IosInfoBadge.vue'
|
||||||
|
import AdminHelpTooltipContent from '@/components/AdminHelpTooltipContent.vue'
|
||||||
import { TOOLTIP_ABOVE_MODAL_Z_INDEX } from '@/constants/overlayZIndex'
|
import { TOOLTIP_ABOVE_MODAL_Z_INDEX } from '@/constants/overlayZIndex'
|
||||||
|
import { adminHelpPlainText } from '@/utils/adminHelpFormat'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -35,13 +39,18 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const badgeSize = computed(() => props.size)
|
const badgeSize = computed(() => props.size)
|
||||||
|
|
||||||
const tooltipText = computed(() => {
|
const parsed = computed(() => ({
|
||||||
const blocks = [props.help.description?.trim()].filter(Boolean)
|
description: props.help.description?.trim() ?? '',
|
||||||
if (props.help.formula?.trim()) {
|
formula: props.help.formula?.trim() || undefined,
|
||||||
blocks.push(props.help.formula.trim())
|
}))
|
||||||
}
|
|
||||||
return blocks.join('\n\n')
|
const plainText = computed(() => adminHelpPlainText(parsed.value))
|
||||||
})
|
|
||||||
|
const tooltipInnerStyle = {
|
||||||
|
maxWidth: '26rem',
|
||||||
|
direction: 'rtl',
|
||||||
|
textAlign: 'right',
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -56,4 +65,9 @@ const tooltipText = computed(() => {
|
|||||||
z-index: 1250 !important;
|
z-index: 1250 !important;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-tooltip.dashboard-help-tooltip .ant-tooltip-inner {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="dashboard-income-summary">
|
<section class="dashboard-income-summary">
|
||||||
<DashboardSectionHeader title="خلاصه درآمد معاملات" :help="help" />
|
<DashboardSectionHeader title="خلاصه درآمد معاملات" :help="help" />
|
||||||
<p class="dashboard-income-summary__intro">
|
|
||||||
سود اختلاف قیمت و کارمزد جدا محاسبه میشوند. جمع = هر دو. همه مبالغ
|
|
||||||
<strong>ریال</strong> هستند (نه تومان).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="dashboard-income-summary__grid">
|
<div class="dashboard-income-summary__grid">
|
||||||
<article
|
<article
|
||||||
@@ -95,13 +91,6 @@ const rows = computed(() => [
|
|||||||
color: var(--admin-text);
|
color: var(--admin-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-income-summary__intro {
|
|
||||||
margin: 0 0 0.5rem;
|
|
||||||
font-size: 0.6875rem;
|
|
||||||
line-height: 1.55;
|
|
||||||
color: var(--admin-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-income-summary__grid {
|
.dashboard-income-summary__grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const dashboardKpi = {
|
|||||||
} as const satisfies Record<string, DashboardHelp>
|
} as const satisfies Record<string, DashboardHelp>
|
||||||
|
|
||||||
export const dashboardTradingIncomeSummary = help(
|
export const dashboardTradingIncomeSummary = help(
|
||||||
'تفکیک درآمد از معاملات در بازه انتخابی: سود اختلاف قیمت و کارمزد ریالی جدا، و جمع هر دو.',
|
'در بازهٔ انتخابی، درآمد معاملات را به دو بخش میبینید: سود اختلاف قیمت و کارمزد ریالی. همهٔ مبالغ به ریال هستند (نه تومان).',
|
||||||
'جمع سود معاملات = سود اختلاف قیمت + کارمزد معاملات'
|
'جمع سود معاملات = سود اختلاف قیمت + کارمزد معاملات'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -400,7 +400,13 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
support_messenger_text:
|
support_messenger_text:
|
||||||
'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیشفرض اپ استفاده میشود.',
|
'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیشفرض اپ استفاده میشود.',
|
||||||
deposit_support_message_text:
|
deposit_support_message_text:
|
||||||
'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیشفرض: «شماره حساب».',
|
`متن پیام هنگام کلیک «پیام به پشتیبانی» در مودال واریز (SMS و پیامرسان).
|
||||||
|
|
||||||
|
متغیر:
|
||||||
|
• %نام = نام صاحب حساب
|
||||||
|
|
||||||
|
پیشفرض اپ:
|
||||||
|
باسلام، %نام هستم. کدپیگیری و مبلغ واریزی من:`,
|
||||||
deposit_account_holder:
|
deposit_account_holder:
|
||||||
'نام صاحب حساب واریز — در متن راهنما و پیام پشتیبانی با ٪نام یا %نام جایگزین میشود.',
|
'نام صاحب حساب واریز — در متن راهنما و پیام پشتیبانی با ٪نام یا %نام جایگزین میشود.',
|
||||||
deposit_card_number:
|
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>
|
<template>
|
||||||
<a-form ref="formRef" :model="form" layout="vertical" autocomplete="off">
|
<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
|
<BasicSelect
|
||||||
v-model:value="form.customer_id"
|
v-model:value="form.customer_id"
|
||||||
show-search
|
:items="customerItems"
|
||||||
:options="customerOptions"
|
key-name="id"
|
||||||
|
value-name="displayName"
|
||||||
placeholder="انتخاب مشتری"
|
placeholder="انتخاب مشتری"
|
||||||
|
:has-tooltip="false"
|
||||||
@change="onCustomerChange"
|
@change="onCustomerChange"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</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
|
<BasicSelect
|
||||||
v-model:value="form.address_id"
|
v-model:value="form.address_id"
|
||||||
:options="addressOptions"
|
:items="addressItems"
|
||||||
|
key-name="id"
|
||||||
|
value-name="displayName"
|
||||||
placeholder="انتخاب آدرس"
|
placeholder="انتخاب آدرس"
|
||||||
:loading="optionsLoading"
|
:loading="optionsLoading"
|
||||||
|
:has-tooltip="false"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<div v-if="form.customer_id" class="space-y-3">
|
<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
|
<div
|
||||||
v-for="(line, index) in form.lines"
|
v-for="(line, index) in form.lines"
|
||||||
:key="`${line.wallet_id}-${index}`"
|
:key="`${line.wallet_id}-${index}`"
|
||||||
@@ -30,13 +44,16 @@
|
|||||||
class="!mb-0"
|
class="!mb-0"
|
||||||
:name="['lines', index, 'wallet_id']"
|
:name="['lines', index, 'wallet_id']"
|
||||||
:rules="inputRules()"
|
:rules="inputRules()"
|
||||||
label="محصول"
|
|
||||||
>
|
>
|
||||||
|
<template #label>محصول</template>
|
||||||
<BasicSelect
|
<BasicSelect
|
||||||
v-model:value="line.wallet_id"
|
v-model:value="line.wallet_id"
|
||||||
:options="productOptionsForLine(index)"
|
:items="productItemsForLine(index)"
|
||||||
|
key-name="wallet_id"
|
||||||
|
value-name="displayName"
|
||||||
placeholder="انتخاب محصول"
|
placeholder="انتخاب محصول"
|
||||||
:loading="optionsLoading"
|
:loading="optionsLoading"
|
||||||
|
:has-tooltip="false"
|
||||||
@change="() => onProductChange(index)"
|
@change="() => onProductChange(index)"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -44,8 +61,10 @@
|
|||||||
class="!mb-0"
|
class="!mb-0"
|
||||||
:name="['lines', index, 'value']"
|
:name="['lines', index, 'value']"
|
||||||
:rules="inputRules()"
|
:rules="inputRules()"
|
||||||
label="مقدار"
|
|
||||||
>
|
>
|
||||||
|
<template #label>
|
||||||
|
<AdminLabelWithHelp label="مقدار" :help="physicalWithdrawAmountHelp" />
|
||||||
|
</template>
|
||||||
<BasicInput v-model:value="line.value" type="number" min="0" />
|
<BasicInput v-model:value="line.value" type="number" min="0" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<div class="flex items-end">
|
<div class="flex items-end">
|
||||||
@@ -73,6 +92,14 @@
|
|||||||
import { inputRules } from '@/utils/inputRules'
|
import { inputRules } from '@/utils/inputRules'
|
||||||
import type { Customer } from '@/model/customer'
|
import type { Customer } from '@/model/customer'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
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 {
|
import {
|
||||||
getPhysicalWithdrawOptions,
|
getPhysicalWithdrawOptions,
|
||||||
type PhysicalWithdrawAddressOption,
|
type PhysicalWithdrawAddressOption,
|
||||||
@@ -100,21 +127,21 @@ const optionsLoading = ref(false)
|
|||||||
const products = ref<PhysicalWithdrawProductOption[]>([])
|
const products = ref<PhysicalWithdrawProductOption[]>([])
|
||||||
const addresses = ref<PhysicalWithdrawAddressOption[]>([])
|
const addresses = ref<PhysicalWithdrawAddressOption[]>([])
|
||||||
|
|
||||||
const customerOptions = computed(() =>
|
const customerItems = computed(() =>
|
||||||
(props.customers ?? []).map((customer) => ({
|
(props.customers ?? []).map((customer) => ({
|
||||||
label: `${customer.name ?? '-'} (${customer.mobile ?? '-'})`,
|
...customer,
|
||||||
value: customer.id
|
displayName: `${customer.name ?? '-'} (${customer.mobile ?? '-'})`
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
const addressOptions = computed(() =>
|
const addressItems = computed(() =>
|
||||||
addresses.value.map((addr) => ({
|
addresses.value.map((addr) => ({
|
||||||
label: [addr.province, addr.city, addr.address].filter(Boolean).join(' / '),
|
...addr,
|
||||||
value: addr.id
|
displayName: [addr.province, addr.city, addr.address].filter(Boolean).join(' / ')
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
function productOptionsForLine(index: number) {
|
function productItemsForLine(index: number) {
|
||||||
const usedWalletIds = props.form.lines
|
const usedWalletIds = props.form.lines
|
||||||
.map((line, i) => (i === index ? undefined : line.wallet_id))
|
.map((line, i) => (i === index ? undefined : line.wallet_id))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -122,8 +149,8 @@ function productOptionsForLine(index: number) {
|
|||||||
return products.value
|
return products.value
|
||||||
.filter((product) => !usedWalletIds.includes(product.wallet_id))
|
.filter((product) => !usedWalletIds.includes(product.wallet_id))
|
||||||
.map((product) => ({
|
.map((product) => ({
|
||||||
label: `${product.title} (موجودی: ${product.available} ${product.unit})`,
|
...product,
|
||||||
value: product.wallet_id
|
displayName: `${product.title} (موجودی: ${product.available} ${product.unit})`
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,83 +1,81 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
<div class="h-full min-h-0 min-w-0 max-w-full overflow-hidden">
|
||||||
<BasicShowContext title="لیست برداشت فیزیکی">
|
<BasicShowContext title="لیست برداشت فیزیکی">
|
||||||
|
<template #extra>
|
||||||
|
<AdminFieldHelp :text="physicalWithdrawPageIntro" />
|
||||||
|
</template>
|
||||||
<div class="flex h-full min-h-0 flex-col">
|
<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">
|
<BaseTable class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||||
<template #extraHeader>
|
<template #extraHeader>
|
||||||
<BaseButton type="primary" size="small" icon="vuesax-linear:add-square" @click="emits('add')">
|
<div class="flex items-center gap-2">
|
||||||
ثبت برداشت فیزیکی
|
<BaseButton type="primary" size="small" icon="vuesax-linear:add-square" @click="emits('add')">
|
||||||
</BaseButton>
|
ثبت برداشت فیزیکی
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<div class="flex flex-col items-center gap-2 py-1">
|
<div class="flex items-center justify-center gap-2">
|
||||||
<div
|
<a-tooltip v-if="canAdjust(record)">
|
||||||
v-for="tx in record.transaction ?? []"
|
<template #title>ویرایش مقدار</template>
|
||||||
:key="tx.id"
|
<Icon
|
||||||
class="flex w-full max-w-[220px] items-center justify-between gap-2 rounded border border-gray-200 px-2 py-1"
|
icon="vuesax-linear:edit-2"
|
||||||
>
|
color="#2563EB"
|
||||||
<span class="truncate text-xs">{{ tx.title?.trim() || 'محصول' }}</span>
|
class="cursor-pointer"
|
||||||
<span class="shrink-0 text-xs font-semibold">{{ Math.abs(Number(tx.gold ?? 0)) }}</span>
|
@click="openAdjustModal(record)"
|
||||||
<div v-if="canAdjust(record)" class="flex shrink-0 gap-1">
|
/>
|
||||||
<BaseButton
|
</a-tooltip>
|
||||||
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">
|
<a-tooltip v-if="canApprove(record)">
|
||||||
<BaseButton
|
<template #title>{{ physicalWithdrawApproveHelp }}</template>
|
||||||
v-if="canApprove(record)"
|
<Icon
|
||||||
size="small"
|
icon="vuesax-linear:tick-circle"
|
||||||
type="primary"
|
color="#16A34A"
|
||||||
:loading="isApproving(record.id)"
|
class="cursor-pointer"
|
||||||
|
:class="{ 'opacity-40 pointer-events-none': isApproving(record.id) }"
|
||||||
@click="handleApprove(record)"
|
@click="handleApprove(record)"
|
||||||
>
|
/>
|
||||||
تایید
|
</a-tooltip>
|
||||||
</BaseButton>
|
|
||||||
<ADropdown v-if="canChangeStatus(record)" :trigger="['click']">
|
<ADropdown v-if="canChangeStatus(record)" :trigger="['click']">
|
||||||
<BaseButton size="small">وضعیت</BaseButton>
|
<a-tooltip>
|
||||||
<template #overlay>
|
<template #title>{{ physicalWithdrawStatusHelp }}</template>
|
||||||
<a-menu class="flex flex-col">
|
<Icon icon="vuesax-linear:status-up" color="#CA8A04" class="cursor-pointer" />
|
||||||
<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>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</BaseTable>
|
</BaseTable>
|
||||||
</div>
|
</div>
|
||||||
</BasicShowContext>
|
</BasicShowContext>
|
||||||
|
|
||||||
|
<PhysicalWithdrawAdjustModal
|
||||||
|
v-model:visible="adjustVisible"
|
||||||
|
:withdraw-id="selectedWithdrawId"
|
||||||
|
@success="reload"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -86,21 +84,25 @@ import type { TableInput } from '@core/type/baseTable'
|
|||||||
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTableOld/hooks/useBaseTable'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import { getPhysicalWithdrawColumns } from '../hooks/physicalWithdrawColumns'
|
import { getPhysicalWithdrawColumns } from '../hooks/physicalWithdrawColumns'
|
||||||
import {
|
import { approvePhysicalWithdraw, getPhysicalWithdrawTable } from '@/api/physicalWithdraw'
|
||||||
adjustPhysicalWithdraw,
|
|
||||||
approvePhysicalWithdraw,
|
|
||||||
getPhysicalWithdrawTable
|
|
||||||
} from '@/api/physicalWithdraw'
|
|
||||||
import { updateSts } from '@/api/wallet'
|
import { updateSts } from '@/api/wallet'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import type { Withdraw } from '@/model/wallet'
|
import type { Withdraw } from '@/model/wallet'
|
||||||
import { status as physicalStatusMap } from '@/pages/menuPages/withdraw/information/hooks/status'
|
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 emits = defineEmits(['add'])
|
||||||
|
|
||||||
const [submit, reload] = useBaseTable()
|
const [submit, reload] = useBaseTable()
|
||||||
const adjustingKey = ref('')
|
|
||||||
const approvingId = ref<number | null>(null)
|
const approvingId = ref<number | null>(null)
|
||||||
|
const adjustVisible = ref(false)
|
||||||
|
const selectedWithdrawId = ref<number>()
|
||||||
|
|
||||||
const tblProps = computed(() => ({
|
const tblProps = computed(() => ({
|
||||||
columns: getPhysicalWithdrawColumns(),
|
columns: getPhysicalWithdrawColumns(),
|
||||||
@@ -108,7 +110,7 @@ const tblProps = computed(() => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
function canAdjust(record: Withdraw) {
|
function canAdjust(record: Withdraw) {
|
||||||
return Number(record.sts) === 50
|
return Number(record.sts) === 50 && (record.transaction?.length ?? 0) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function canApprove(record: Withdraw) {
|
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) {
|
function isApproving(withdrawId?: number) {
|
||||||
return approvingId.value === withdrawId
|
return approvingId.value === withdrawId
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAdjust(
|
function openAdjustModal(record: Withdraw) {
|
||||||
record: Withdraw,
|
if (!record.id) return
|
||||||
tx: { id?: number },
|
selectedWithdrawId.value = record.id
|
||||||
direction: 'increase' | 'decrease'
|
adjustVisible.value = true
|
||||||
) {
|
|
||||||
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) {
|
async function handleApprove(record: Withdraw) {
|
||||||
if (!record.id) return
|
if (!record.id || isApproving(record.id)) return
|
||||||
approvingId.value = record.id
|
approvingId.value = record.id
|
||||||
try {
|
try {
|
||||||
await approvePhysicalWithdraw(record.id, 51)
|
await approvePhysicalWithdraw(record.id, 51)
|
||||||
|
|||||||
@@ -3,15 +3,25 @@ import { toJalali } from '@core/utils/useDataTime'
|
|||||||
import CustomTag from '@/components/CustomTag.vue'
|
import CustomTag from '@/components/CustomTag.vue'
|
||||||
import type { Withdraw } from '@/model/wallet'
|
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 {
|
function formatProductsSummary(record: Withdraw): string {
|
||||||
const lines = record.transaction ?? []
|
const lines = record.transaction ?? []
|
||||||
if (!lines.length) return '-'
|
if (!lines.length) return '-'
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
.map((tx) => {
|
.map((tx) => {
|
||||||
const qty = Math.abs(Number(tx.gold ?? 0))
|
const title = String(tx.title ?? '').trim() || tx.product?.title || 'محصول'
|
||||||
const title = String(tx.title ?? '').trim() || 'محصول'
|
return formatLineQty(tx, title)
|
||||||
return `${title}: ${qty}`
|
|
||||||
})
|
})
|
||||||
.join(' | ')
|
.join(' | ')
|
||||||
}
|
}
|
||||||
@@ -87,7 +97,7 @@ export function getPhysicalWithdrawColumns(): BaseTableColumn[] {
|
|||||||
title: 'عملیات',
|
title: 'عملیات',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 260,
|
width: 140,
|
||||||
slot: 'action',
|
slot: 'action',
|
||||||
filterable: false,
|
filterable: false,
|
||||||
fixed: 'right'
|
fixed: 'right'
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ async function loadCustomers() {
|
|||||||
customers.value = (await getCustomersList()) ?? []
|
customers.value = (await getCustomersList()) ?? []
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
message.error('خطا در بارگذاری لیست مشتریان')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +76,7 @@ function resetForm() {
|
|||||||
|
|
||||||
function openCreate() {
|
function openCreate() {
|
||||||
resetForm()
|
resetForm()
|
||||||
|
void loadCustomers()
|
||||||
createVisible.value = true
|
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">
|
<BaseTable v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||||
<template #extraHeader>
|
<template #extraHeader>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<AdminFieldHelp :text="pricingPageIntro" />
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
/** توضیحات ساده فارسی برای فرمهای ادمین — قابل فهم بدون دانش فنی */
|
/** توضیحات ساده فارسی برای فرمهای ادمین — قابل فهم بدون دانش فنی */
|
||||||
|
|
||||||
|
import { adminHelp } from '@/utils/adminHelpFormat'
|
||||||
|
|
||||||
export const bannersPageIntro =
|
export const bannersPageIntro =
|
||||||
'بنرها همان تصاویر اسلایدر بالای صفحهٔ اصلی اپ مشتری هستند. هر کارت = یک اسلاید. بعد از هر تغییر حتماً «ذخیره تغییرات» را بزنید؛ تا آن لحظه در اپ دیده نمیشود.'
|
'بنرها همان تصاویر اسلایدر بالای صفحهٔ اصلی اپ مشتری هستند. هر کارت = یک اسلاید. بعد از هر تغییر حتماً «ذخیره تغییرات» را بزنید؛ تا آن لحظه در اپ دیده نمیشود.'
|
||||||
|
|
||||||
@@ -65,3 +67,37 @@ export const discountExpiryHelp =
|
|||||||
|
|
||||||
export const priceTriggersPageIntro =
|
export const priceTriggersPageIntro =
|
||||||
'لیست هشدارهای قیمت مشتریان. میتوانید هشدار جدید بسازید، ویرایش کنید، فعال/غیرفعال کنید یا حذف کنید. وقتی قیمت به هدف برسد، به مشتری پیامک یا ایمیل میرود و هشدار خودکار غیرفعال میشود.'
|
'لیست هشدارهای قیمت مشتریان. میتوانید هشدار جدید بسازید، ویرایش کنید، فعال/غیرفعال کنید یا حذف کنید. وقتی قیمت به هدف برسد، به مشتری پیامک یا ایمیل میرود و هشدار خودکار غیرفعال میشود.'
|
||||||
|
|
||||||
|
export const physicalWithdrawPageIntro =
|
||||||
|
'درخواست تحویل فیزیکی طلا یا محصول از کیف مشتری. درخواستهای ثبتشده از اپ مشتری و همینجا توسط ادمین در این لیست دیده میشوند. با ثبت درخواست، مقدار از کیف مشتری کسر میشود. در وضعیت «در انتظار ارسال» با آیکون ویرایش میتوانید مقدار را (حتی اعشاری برای آبشده) تنظیم کنید؛ با «تایید» به «ارسال شده» میرود. برای برداشت ریالی از منوی «برداشت و واریز» استفاده کنید.'
|
||||||
|
|
||||||
|
export const physicalWithdrawCustomerHelp =
|
||||||
|
'مشتریای که میخواهید برایش تحویل فیزیکی ثبت کنید. بعد از انتخاب، لیست محصولات و آدرسهای همان مشتری بارگذاری میشود.'
|
||||||
|
|
||||||
|
export const physicalWithdrawAddressHelp =
|
||||||
|
'آدرسی که محصول به آن ارسال میشود. مشتری آدرسها را در اپ خود ثبت میکند؛ اگر آدرسی ندارد، ابتدا از پنل مشتریان یا از خود مشتری بخواهید آدرس اضافه کند.'
|
||||||
|
|
||||||
|
export const physicalWithdrawProductHelp =
|
||||||
|
'محصولی که مشتری میخواهد فیزیکی تحویل بگیرد. فقط محصولاتی با موجودی بیشتر از صفر نشان داده میشوند.'
|
||||||
|
|
||||||
|
export const physicalWithdrawAmountHelp = adminHelp(
|
||||||
|
'تعداد یا مقدار درخواستی همان واحدی است که در اپ مشتری نمایش داده میشود (مثلاً تعداد سکه یا گرم).',
|
||||||
|
'مقدار درخواستی ≤ موجودی کیف مشتری'
|
||||||
|
)
|
||||||
|
|
||||||
|
export const physicalWithdrawDecreaseHelp =
|
||||||
|
'یک واحد از مقدار این خط کم میکند و همان مقدار به کیف مشتری برمیگردد. فقط وقتی وضعیت «در انتظار ارسال» است.'
|
||||||
|
|
||||||
|
export const physicalWithdrawIncreaseHelp =
|
||||||
|
'یک واحد به مقدار این خط اضافه میکند و از کیف مشتری کسر میشود. فقط وقتی وضعیت «در انتظار ارسال» است و موجودی کافی باشد.'
|
||||||
|
|
||||||
|
export const physicalWithdrawAdjustValueHelp = adminHelp(
|
||||||
|
'مقدار نهایی برداشت را وارد کنید. برای آبشده و محصولات وزنی میتوانید اعشار بزنید (مثلاً ۱.۲۵۰ گرم). برای سکه و محصولات تعدادی فقط عدد صحیح مجاز است.',
|
||||||
|
'مقدار نهایی ≤ موجودی کیف مشتری'
|
||||||
|
)
|
||||||
|
|
||||||
|
export const physicalWithdrawApproveHelp =
|
||||||
|
'درخواست را تایید میکند و وضعیت به «ارسال شده» تغییر میکند؛ یعنی آمادهٔ ارسال یا تحویل است.'
|
||||||
|
|
||||||
|
export const physicalWithdrawStatusHelp =
|
||||||
|
'پیگیری مراحل ارسال: در انتظار ارسال، ارسال شده، لغو شده، یا در انتظار تعیین مجدد قیمت (برای محصولاتی که وزن نهایی مشخص نیست).'
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/** ساختار متن راهنما — توضیح + فرمول اختیاری در کادر جدا */
|
||||||
|
|
||||||
|
export type AdminHelpContent = {
|
||||||
|
description: string
|
||||||
|
formula?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const FORMULA_MARKER = '\n\n__FORMULA__\n'
|
||||||
|
|
||||||
|
export function adminHelp(description: string, formula?: string): string {
|
||||||
|
const body = description.trim()
|
||||||
|
if (!formula?.trim()) return body
|
||||||
|
return `${body}${FORMULA_MARKER}${formula.trim()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseAdminHelp(input: string | AdminHelpContent | undefined | null): AdminHelpContent {
|
||||||
|
if (!input) return { description: '' }
|
||||||
|
if (typeof input !== 'string') {
|
||||||
|
return {
|
||||||
|
description: input.description?.trim() ?? '',
|
||||||
|
formula: input.formula?.trim() || undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const marked = input.split(FORMULA_MARKER)
|
||||||
|
if (marked.length === 2) {
|
||||||
|
return {
|
||||||
|
description: marked[0].trim(),
|
||||||
|
formula: marked[1].trim() || undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { description: input.trim() }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function adminHelpPlainText(content: AdminHelpContent): string {
|
||||||
|
const parts = [content.description].filter(Boolean)
|
||||||
|
if (content.formula) parts.push(content.formula)
|
||||||
|
return parts.join('\n\n')
|
||||||
|
}
|
||||||
@@ -1,20 +1,69 @@
|
|||||||
|
const PERSIAN_DIGITS = '۰۱۲۳۴۵۶۷۸۹'
|
||||||
|
const ARABIC_DIGITS = '٠١٢٣٤٥٦٧٨٩'
|
||||||
|
|
||||||
|
function toAsciiDigits(value: string): string {
|
||||||
|
return value
|
||||||
|
.replace(/[۰-۹]/g, (d) => String(PERSIAN_DIGITS.indexOf(d)))
|
||||||
|
.replace(/[٠-٩]/g, (d) => String(ARABIC_DIGITS.indexOf(d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** پارس مبلغ محدودیت سفارش — ارقام فارسی/عربی و جداکننده هزارگان */
|
||||||
|
export function parseOrderLimitCashValue(value: unknown): number | undefined {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = toAsciiDigits(String(value).trim().replace(/[,\s٬]/g, ''))
|
||||||
|
if (raw === '') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const num = Number(raw)
|
||||||
|
if (!Number.isFinite(num) || num < 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.trunc(num)
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderLimitInputsPresent(form: {
|
||||||
|
min_order_cash?: number | string | null
|
||||||
|
max_order_cash?: number | string | null
|
||||||
|
}): boolean {
|
||||||
|
return (
|
||||||
|
(form.min_order_cash != null && form.min_order_cash !== '') ||
|
||||||
|
(form.max_order_cash != null && form.max_order_cash !== '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function validateProductOrderLimitForm(form: {
|
export function validateProductOrderLimitForm(form: {
|
||||||
order_limit_enabled?: boolean
|
order_limit_enabled?: boolean
|
||||||
min_order_cash?: number | string | null
|
min_order_cash?: number | string | null
|
||||||
max_order_cash?: number | string | null
|
max_order_cash?: number | string | null
|
||||||
}): string | null {
|
}): string | null {
|
||||||
if (!form.order_limit_enabled) return null
|
const enabled = !!form.order_limit_enabled || orderLimitInputsPresent(form)
|
||||||
|
if (!enabled) return null
|
||||||
|
|
||||||
const min = form.min_order_cash
|
const rawMin = form.min_order_cash
|
||||||
const max = form.max_order_cash
|
const rawMax = form.max_order_cash
|
||||||
const hasMin = min != null && min !== ''
|
const hasMinInput = rawMin != null && rawMin !== ''
|
||||||
const hasMax = max != null && max !== ''
|
const hasMaxInput = rawMax != null && rawMax !== ''
|
||||||
|
const min = parseOrderLimitCashValue(rawMin)
|
||||||
|
const max = parseOrderLimitCashValue(rawMax)
|
||||||
|
|
||||||
if (!hasMin && !hasMax) {
|
if (hasMinInput && min == null) {
|
||||||
|
return 'حداقل مبلغ سفارش نامعتبر است'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMaxInput && max == null) {
|
||||||
|
return 'حداکثر مبلغ سفارش نامعتبر است'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min == null && max == null) {
|
||||||
return 'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود'
|
return 'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasMin && hasMax && Number(min) > Number(max)) {
|
if (min != null && max != null && min > max) {
|
||||||
return 'حداقل مبلغ نمیتواند بیشتر از حداکثر باشد'
|
return 'حداقل مبلغ نمیتواند بیشتر از حداکثر باشد'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,13 +71,19 @@ export function validateProductOrderLimitForm(form: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeProductOrderLimitForm<T extends Record<string, any>>(data: T): T {
|
export function normalizeProductOrderLimitForm<T extends Record<string, any>>(data: T): T {
|
||||||
data.order_limit_enabled = !!data.order_limit_enabled
|
const min = parseOrderLimitCashValue(data.min_order_cash)
|
||||||
if (!data.order_limit_enabled) {
|
const max = parseOrderLimitCashValue(data.max_order_cash)
|
||||||
|
const enabled = !!data.order_limit_enabled || min != null || max != null
|
||||||
|
|
||||||
|
data.order_limit_enabled = enabled
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
data.min_order_cash = undefined
|
data.min_order_cash = undefined
|
||||||
data.max_order_cash = undefined
|
data.max_order_cash = undefined
|
||||||
} else {
|
} else {
|
||||||
data.min_order_cash = data.min_order_cash != null && data.min_order_cash !== '' ? data.min_order_cash : undefined
|
data.min_order_cash = min
|
||||||
data.max_order_cash = data.max_order_cash != null && data.max_order_cash !== '' ? data.max_order_cash : undefined
|
data.max_order_cash = max
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user