added pdf in withdraw and deposit and fix bug
This commit is contained in:
@@ -12,3 +12,23 @@ export const getPriceTable = async (params: TableInput) =>
|
||||
export const getLastPrice = async () => await useAxios.get<last>(`/api/price/last`)
|
||||
|
||||
export const methodePrices = async () => await useAxios.get<MethodePrices[]>('/admin/p')
|
||||
|
||||
export interface ZaarboxProductOption {
|
||||
key: string
|
||||
name: string
|
||||
id: string
|
||||
category: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export interface ZaarboxProductsResponse {
|
||||
code: number
|
||||
message: string
|
||||
data: {
|
||||
items: ZaarboxProductOption[]
|
||||
api_url: string
|
||||
}
|
||||
}
|
||||
|
||||
export const getZaarboxProducts = async () =>
|
||||
await useAxios.get<ZaarboxProductsResponse>('/admin/zaarbox/products')
|
||||
|
||||
+6
-1
@@ -10,9 +10,14 @@ export const chargeWallet = async (params: Charge, type: 'gold' | 'money') =>
|
||||
? await useAxios.post<boolean>('/admin/wallet/charge', params)
|
||||
: await useAxios.post<boolean>('/admin/wallet/charge_gold', params)
|
||||
|
||||
export const getWithdrawTable = async (params: TableInput) =>
|
||||
export const getWithdrawTable = async (params: TableInput & { flow_type?: string }) =>
|
||||
await useAxios.post<Array<Withdraw>>('/admin/wallet/withdraw/table', params)
|
||||
|
||||
export const exportWithdrawReport = async (params: TableInput & { flow_type?: string }) =>
|
||||
await useAxios.post<string>('/admin/wallet/withdraw/report', params, {
|
||||
responseType: 'text'
|
||||
})
|
||||
|
||||
export const completeWithdraw = async (params: Withdraw) =>
|
||||
await useAxios.post<boolean>('/admin/wallet/withdraw/complete', params)
|
||||
|
||||
|
||||
@@ -79,8 +79,13 @@
|
||||
z-index: 1100 !important;
|
||||
}
|
||||
|
||||
.ant-picker-dropdown{
|
||||
z-index: 1100 !important;
|
||||
// dropdown سلکت و date picker داخل مودال (باید بالاتر از 1100 باشد)
|
||||
.ant-select-dropdown {
|
||||
z-index: 1200 !important;
|
||||
}
|
||||
|
||||
.ant-picker-dropdown {
|
||||
z-index: 1200 !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<ASelect
|
||||
placeholder="مرتب سازی"
|
||||
class="flex-1"
|
||||
:options="sortableColumns"
|
||||
v-model:value="orderColumnKey"
|
||||
|
||||
@@ -23,6 +23,7 @@ export interface Product{
|
||||
open_buy: boolean
|
||||
open_sell: boolean
|
||||
show: boolean
|
||||
show_in_assets?: boolean
|
||||
title: string
|
||||
updated_at: string
|
||||
methodOption: number
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ export interface Withdraw {
|
||||
cash?: number
|
||||
gold?: number
|
||||
note?: string
|
||||
tracked_number?: number
|
||||
tracked_number?: string
|
||||
date_at?: string
|
||||
sts?: number
|
||||
created_at?: string
|
||||
@@ -28,7 +28,7 @@ export interface Withdraw {
|
||||
sts_str?: string
|
||||
daj?: string
|
||||
customer?: User
|
||||
bank_account?: { cart_number?: string }
|
||||
bank_account?: { cart_number?: string; shaba_number?: string }
|
||||
transaction?: WithdrawTransaction[]
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
<template #unCheckedChildren>غیرفعال</template>
|
||||
</a-switch>
|
||||
|
||||
<ZaarboxPriceKeyField
|
||||
v-else-if="isZaarboxPriceKey"
|
||||
:model-value="modelValue"
|
||||
:disabled="disabled"
|
||||
@update:value="emit('update:value', $event)"
|
||||
/>
|
||||
|
||||
<a-select
|
||||
v-else-if="fieldType === 'select'"
|
||||
:value="modelValue || undefined"
|
||||
@@ -54,6 +61,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ZaarboxPriceKeyField from './ZaarboxPriceKeyField.vue'
|
||||
import {
|
||||
isSwitchOn,
|
||||
parseSelectOptions,
|
||||
@@ -83,6 +91,8 @@ const fieldType = computed<SettingFieldType>(() =>
|
||||
})
|
||||
)
|
||||
|
||||
const isZaarboxPriceKey = computed(() => props.settingKey === 'zaarbox_price_key')
|
||||
|
||||
const selectItems = computed(() =>
|
||||
parseSelectOptions({
|
||||
key: props.settingKey,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<a-select
|
||||
:value="modelValue || undefined"
|
||||
:disabled="disabled"
|
||||
:loading="loading"
|
||||
allow-clear
|
||||
show-search
|
||||
placeholder="انتخاب محصول زرباکس"
|
||||
class="w-full"
|
||||
option-filter-prop="label"
|
||||
@update:value="onSelectChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in allOptions"
|
||||
:key="item.key"
|
||||
:value="item.key"
|
||||
:label="`${item.name} ${item.key}`"
|
||||
>
|
||||
<div class="leading-snug">
|
||||
<div>{{ item.name }}</div>
|
||||
<div class="text-xs text-gray-400 direction-ltr text-right">{{ item.key }}</div>
|
||||
</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { getZaarboxProducts, type ZaarboxProductOption } from '@/api/price'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{ disabled: false }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{ 'update:value': [value: string] }>()
|
||||
|
||||
const loading = ref(false)
|
||||
const options = ref<ZaarboxProductOption[]>([])
|
||||
|
||||
const allOptions = computed(() => {
|
||||
const current = (props.modelValue ?? '').trim()
|
||||
if (!current || options.value.some((item) => item.key === current)) {
|
||||
return options.value
|
||||
}
|
||||
|
||||
return [
|
||||
{ key: current, name: current, id: '', category: '', active: true },
|
||||
...options.value,
|
||||
]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
void loadOptions()
|
||||
})
|
||||
|
||||
async function loadOptions() {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getZaarboxProducts()
|
||||
options.value = response?.data?.items ?? []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
options.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onSelectChange(val: string | undefined) {
|
||||
emit('update:value', val ?? '')
|
||||
}
|
||||
</script>
|
||||
@@ -289,7 +289,7 @@ const SETTING_GROUPS: Array<{
|
||||
id: 'general',
|
||||
title: 'عمومی و اطلاعات اپ',
|
||||
icon: 'vuesax-linear:home-2',
|
||||
match: (k) => /^(name_|app_|admin_|server_|bace_|key$|color_|logo_|title_pwa|title_fav|metal_type)/.test(k)
|
||||
match: (k) => /^(name_|app_|admin_|server_|bace_|key$|color_|logo_|title_pwa|title_fav|metal_type|unit$)/.test(k)
|
||||
},
|
||||
{
|
||||
id: 'pricing',
|
||||
|
||||
@@ -75,6 +75,7 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
|
||||
external_order_api_enabled: 'switch',
|
||||
store_status: 'select',
|
||||
credit_rial_price_unit: 'select',
|
||||
zaarbox_price_key: 'select',
|
||||
node_client_id: 'text',
|
||||
node_shared_secret: 'text',
|
||||
support_messenger_id: 'text',
|
||||
@@ -99,6 +100,7 @@ const SELECT_OPTIONS_BY_KEY: Record<string, string> = {
|
||||
payping_gateway_name: 'sepal,',
|
||||
credit_rial_price_unit: 'gram,mesghal,unit',
|
||||
store_status: 'باز,بسته',
|
||||
unit: 'گرم,کیلوگرم',
|
||||
support_messenger: 'bale,telegram,whatsapp,rubika,eitaa,soroush'
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,57 @@
|
||||
<BaseCollapse title="اطلاعات محصول" active-key="0" :actives="activeKey">
|
||||
<template #content>
|
||||
<div
|
||||
v-if="form.group_type === '' || form.group_type !== 'آبشده'"
|
||||
v-if="isMeltedCategory"
|
||||
class="md:flex flex-col gap-3 border-1 border-solid rounded-lg border-gray-200 p-3"
|
||||
>
|
||||
<div class="items-center">
|
||||
<span class="ml-4">مرجع قیمت :</span>
|
||||
<a-radio-group :value="meltedPriceOption" name="meltedPriceGroup" @update:value="$emit('update:meltedPriceOption', $event)">
|
||||
<a-radio value="app">قیمت اپ (صفحه اصلی)</a-radio>
|
||||
<a-radio value="fixed">ثابت</a-radio>
|
||||
<a-radio value="reference">مرجع متفاوت</a-radio>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<div v-if="meltedPriceOption === 'fixed'" class="md:flex items-center gap-3">
|
||||
<span>قیمت ثابت :</span>
|
||||
<BasicInput class="md:w-[20rem]" addon-before="ریال" type="price" v-model:value="form.price" />
|
||||
</div>
|
||||
<div v-if="meltedPriceOption === 'reference'" class="flex flex-col gap-2">
|
||||
<div class="md:flex justify-between items-center">
|
||||
<div class="ml-4">مرجع قیمت :</div>
|
||||
<BasicSelect class="w-11rem method_style" allowClear :items="pricesList" valueName="name" keyName="id" v-model:value="form.reading_method" />
|
||||
</div>
|
||||
<div class="md:flex justify-between items-center">
|
||||
<div class="ml-4">تعدیل نسبت به قیمت مرجع (٪) :</div>
|
||||
<BasicInput
|
||||
class="w-11rem"
|
||||
type="number"
|
||||
v-model:value="form.price_offset_percent"
|
||||
placeholder="مثلاً 5 یا -3"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
مثبت = گرانتر، منفی = ارزانتر نسبت به قیمت مرجع.
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="meltedPriceOption === 'app'" class="flex flex-col gap-2">
|
||||
<div class="md:flex justify-between items-center">
|
||||
<div class="ml-4">تعدیل نسبت به قیمت اپ (٪) :</div>
|
||||
<BasicInput
|
||||
class="w-11rem"
|
||||
type="number"
|
||||
v-model:value="form.price_offset_percent"
|
||||
placeholder="مثلاً 5 یا -3"
|
||||
/>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">
|
||||
قیمت خرید/فروش از قیمت صفحه اصلی اپ محاسبه میشود. مثبت = گرانتر، منفی = ارزانتر.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!isMeltedCategory"
|
||||
class="md:flex justify-between border-1 border-solid rounded-lg border-gray-200 p-2"
|
||||
>
|
||||
<div class="items-center">
|
||||
@@ -46,7 +96,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="form.group_type === '' || form.group_type !== 'آبشده'"
|
||||
v-if="!isMeltedCategory"
|
||||
class="md:flex justify-between p-2 border-1 border-solid rounded-lg border-gray-200 mt-2"
|
||||
>
|
||||
<div>
|
||||
@@ -59,36 +109,30 @@
|
||||
<div v-if="weightOption === '1'" class="flex items-center">
|
||||
<span class="ml-4 ">{{ weightFieldLabel }} :</span>
|
||||
<BasicInput v-model:value="form.amount" type="number" />
|
||||
<div class="mt-1 mr-4" v-if="form.amount && form.amount < 1 && 0 < form.amount && !isBourseCuProject">معادل {{ form.amount * 1000 }} سوت</div>
|
||||
<div class="mt-1 mr-4" v-if="form.amount && form.amount < 1 && 0 < form.amount && tradeWeightUnit === 'گرم'">معادل {{ form.amount * 1000 }} سوت</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
<a-form-item label="عنوان" name="title" :rules="inputRules()"><BasicInput v-model:value="form.title" /></a-form-item>
|
||||
<a-form-item v-if="getConfigApp('name') === 'boursecu'" label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="boursemesCategoryItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item v-else-if="getConfigApp('name') === 'garoosi'" label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="garoosiCategoryItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item v-else label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="categoryItems" valueName="value" keyName="value" />
|
||||
<a-form-item label="دسته بندی" name="group_type" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.group_type" :items="categoryItems" valueName="value" keyName="key" />
|
||||
</a-form-item>
|
||||
<a-form-item label="جنس فلز" name="metal_id" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.metal_id" :items="metalItems" valueName="value" keyName="key" />
|
||||
</a-form-item>
|
||||
<a-form-item label="نوع قیمت" name="direct_price" :rules="inputRules()">
|
||||
<a-form-item v-if="!isMeltedCategory" label="نوع قیمت" name="direct_price" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.direct_price" :items="priceItems" valueName="value" keyName="key" />
|
||||
</a-form-item>
|
||||
<a-form-item label="واحد" name="unit" :rules="inputRules()">
|
||||
<BasicSelect v-model:value="form.unit" :items="unitItems" valueName="value" keyName="value" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.group_type === '' || form.group_type !== 'آبشده'" label="هزینه نقدی موقع خرید" name="cash_fee_buy" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_buy" type="number" />
|
||||
<a-form-item label="هزینه نقدی موقع خرید" name="cash_fee_buy" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_buy" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.group_type === '' || form.group_type !== 'آبشده'" label="هزینه نقدی موقع فروش" name="cash_fee_sell" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_sell" type="number" />
|
||||
<a-form-item label="هزینه نقدی موقع فروش" name="cash_fee_sell" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.cash_fee_sell" addon-before="ریال" type="price" />
|
||||
</a-form-item>
|
||||
<a-form-item label="زمان شروع فعال سازی" name="auto_open_at">
|
||||
<TimePicker v-model:value="form.auto_open_at" :locale="customLocale" placeholder="انتخاب زمان" :defaultValue="dayjs('00:00:00', 'HH:mm:ss')" />
|
||||
@@ -99,6 +143,9 @@
|
||||
<a-form-item label="محدودیت مبلغ سفارش" name="order_limit_enabled">
|
||||
<a-switch v-model:checked="form.order_limit_enabled" @change="onOrderLimitChange" />
|
||||
</a-form-item>
|
||||
<a-form-item label="نمایش در صفحه داراییها" name="show_in_assets">
|
||||
<a-switch v-model:checked="form.show_in_assets" />
|
||||
</a-form-item>
|
||||
<template v-if="form.order_limit_enabled">
|
||||
<div class="col-span-2 text-xs text-gray-500 -mt-2 mb-1">
|
||||
حداقل یکی از «حداقل» یا «حداکثر» الزامی است؛ میتوانید فقط یکی را پر کنید.
|
||||
@@ -135,19 +182,20 @@ import { inputRules } from '@/utils/inputRules'
|
||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||
import BaseCollapse from '@/components/BaseCollapse.vue'
|
||||
import IndexImageUploader from '@/components/ProductUploader/IndexImageUploader.vue'
|
||||
import { getConfigApp } from '@/utils/config'
|
||||
import { getConfiguration } from '@/api/configuration'
|
||||
import { isMeltedTradeGroup } from '@/utils/productCategories'
|
||||
import { resolveTradeWeightUnit, weightFieldLabelForUnit } from '@/utils/weightUnit'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
form: any
|
||||
formRef: any
|
||||
methodOption: string
|
||||
weightOption: string
|
||||
meltedPriceOption: string
|
||||
activeKey: string[]
|
||||
pricesList: Array<{ id: string; name: string }>
|
||||
boursemesCategoryItems: any[]
|
||||
garoosiCategoryItems: any[]
|
||||
categoryItems: any[]
|
||||
metalItems: any[]
|
||||
priceItems: any[]
|
||||
@@ -157,13 +205,47 @@ const props = defineProps<{
|
||||
editId?: number
|
||||
}>()
|
||||
|
||||
const isBourseCuProject = computed(() => getConfigApp('name') === 'boursecu')
|
||||
const weightFieldLabel = computed(() =>
|
||||
isBourseCuProject.value && (props.form.unit === 'کیلوگرم' || !props.form.unit)
|
||||
? 'وزن به کیلوگرم'
|
||||
: 'وزن به گرم'
|
||||
const tradeWeightUnit = ref('گرم')
|
||||
const isMeltedCategory = computed(() => isMeltedTradeGroup(props.form.group_type))
|
||||
|
||||
watch(
|
||||
() => props.meltedPriceOption,
|
||||
(option) => {
|
||||
if (option === 'app' || option === 'fixed') {
|
||||
props.form.reading_method = null
|
||||
}
|
||||
if (option === 'app') {
|
||||
props.form.direct_price = 0
|
||||
}
|
||||
if (option === 'fixed') {
|
||||
props.form.direct_price = 1
|
||||
props.form.price_offset_percent = 0
|
||||
}
|
||||
if (option === 'reference') {
|
||||
props.form.direct_price = 0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const rows = await getConfiguration()
|
||||
const unitRow = Array.isArray(rows) ? rows.find((row) => row.key === 'unit') : null
|
||||
tradeWeightUnit.value = resolveTradeWeightUnit(unitRow?.value)
|
||||
} catch {
|
||||
tradeWeightUnit.value = 'گرم'
|
||||
}
|
||||
})
|
||||
|
||||
const weightFieldLabel = computed(() => {
|
||||
const productUnit = props.form.unit?.trim()
|
||||
if (productUnit === 'کیلوگرم' || productUnit === 'گرم') {
|
||||
return weightFieldLabelForUnit(productUnit)
|
||||
}
|
||||
|
||||
return weightFieldLabelForUnit(tradeWeightUnit.value)
|
||||
})
|
||||
|
||||
function onOrderLimitChange(checked: boolean) {
|
||||
if (!checked) {
|
||||
props.form.min_order_cash = undefined
|
||||
@@ -176,6 +258,7 @@ defineEmits<{
|
||||
(e: 'minusPrice'): void
|
||||
(e: 'update:methodOption', value: string): void
|
||||
(e: 'update:weightOption', value: string): void
|
||||
(e: 'update:meltedPriceOption', value: string): void
|
||||
(e: 'update:activeKey', value: string[]): void
|
||||
(e: 'update:productIndexImage', value: any[]): void
|
||||
(e: 'uploadedIndexFile', id: number, response?: Record<string, any>): void
|
||||
|
||||
@@ -23,17 +23,17 @@
|
||||
:weightOption="weightOption"
|
||||
:activeKey="activeKey_"
|
||||
:pricesList="pricesList"
|
||||
:boursemesCategoryItems="BoursemesCategoryItems"
|
||||
:garoosiCategoryItems="GaroosiCategoryItems"
|
||||
:categoryItems="categoryItems"
|
||||
:categoryItems="categorySelectItems"
|
||||
:metalItems="metalItems"
|
||||
:priceItems="priceItems"
|
||||
:unitItems="unitItems"
|
||||
:customLocale="customLocale"
|
||||
:productIndexImage="ProductIndexImage"
|
||||
:editId="editId"
|
||||
:meltedPriceOption="meltedPriceOption"
|
||||
@update:methodOption="methodOption = $event"
|
||||
@update:weightOption="weightOption = $event"
|
||||
@update:meltedPriceOption="meltedPriceOption = $event"
|
||||
@update:activeKey="activeKey_ = $event"
|
||||
@update:productIndexImage="ProductIndexImage = $event"
|
||||
@addPrice="addPrice"
|
||||
@@ -73,7 +73,7 @@ import {
|
||||
unwrapApiRecord
|
||||
} from '@/utils/file'
|
||||
import { normalizeProductOrderLimitForm, validateProductOrderLimitForm } from '@/utils/productOrderLimit'
|
||||
import { getConfigApp } from '@/utils/config'
|
||||
import { buildCategorySelectItems, isMeltedTradeGroup, resolveMeltedPriceSource } from '@/utils/productCategories'
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
@@ -83,6 +83,7 @@ const refreshTable = () => table.value?.fetch()
|
||||
|
||||
const methodOption = ref('1')
|
||||
const weightOption = ref('1')
|
||||
const meltedPriceOption = ref('app')
|
||||
const activeKey_ = ref(['0'])
|
||||
const ProductIndexImage = ref<Array<FileList>>([])
|
||||
const addUserVisible = ref(false)
|
||||
@@ -104,42 +105,18 @@ const customLocale = ref({
|
||||
}
|
||||
})
|
||||
|
||||
const unitItems = computed<Array<Item>>(() => {
|
||||
if (getConfigApp('name') === 'boursecu') {
|
||||
return [{ value: 'کیلوگرم' }, { value: 'عدد' }, { value: 'گرم' }]
|
||||
}
|
||||
const unitItems = computed<Array<Item>>(() => [
|
||||
{ value: 'گرم' },
|
||||
{ value: 'کیلوگرم' },
|
||||
{ value: 'عدد' }
|
||||
])
|
||||
|
||||
return [{ value: 'عدد' }, { value: 'گرم' }]
|
||||
})
|
||||
|
||||
const GaroosiCategoryItems: Array<Item> = [
|
||||
{ value: 'طلا' },
|
||||
{ value: 'مکعب' },
|
||||
{ value: 'گوی' },
|
||||
{ value: 'سکه' },
|
||||
{ value: 'سکه پارسیان' },
|
||||
{ value: 'مس' },
|
||||
{ value: 'نقره' }
|
||||
]
|
||||
const BoursemesCategoryItems: Array<Item> = [
|
||||
{ value: 'آبشده' },
|
||||
{ value: 'کاتد مس' },
|
||||
{ value: 'مفتول مس' }
|
||||
]
|
||||
const categoryItems: Array<Item> = [
|
||||
{ value: 'آبشده' },
|
||||
{ value: 'مکعب' },
|
||||
{ value: 'گوی' },
|
||||
{ value: 'سکه' },
|
||||
{ value: 'سکه پارسیان' },
|
||||
{ value: 'مس' },
|
||||
{ value: 'نقره' }
|
||||
]
|
||||
const metalItems: Array<Item> = [
|
||||
{ key: 1, value: 'طلا' },
|
||||
{ key: 2, value: 'نقره' },
|
||||
{ key: 3, value: 'مس' }
|
||||
{ key: 3, value: 'مس' },
|
||||
]
|
||||
const categorySelectItems = computed(() => buildCategorySelectItems(metalItems))
|
||||
const priceItems: Array<Item> = [
|
||||
{ key: 0, value: 'متغیر' },
|
||||
{ key: 1, value: 'ثابت' }
|
||||
@@ -241,6 +218,10 @@ async function edit(record: Product) {
|
||||
}
|
||||
|
||||
methodOption.value = productData.reading_method ? '2' : '1'
|
||||
meltedPriceOption.value = resolveMeltedPriceSource(productData)
|
||||
if (isMeltedTradeGroup(productData.group_type)) {
|
||||
methodOption.value = '1'
|
||||
}
|
||||
weightOption.value = productData.amount && productData.amount !== 1 ? '1' : '2'
|
||||
|
||||
const start = productData.auto_open_at
|
||||
@@ -277,6 +258,7 @@ function resetData() {
|
||||
ProductIndexImage.value = []
|
||||
methodOption.value = '1'
|
||||
weightOption.value = '1'
|
||||
meltedPriceOption.value = 'app'
|
||||
form.value = {
|
||||
unit: '',
|
||||
group_type: '',
|
||||
@@ -291,6 +273,7 @@ function resetData() {
|
||||
file_id: undefined,
|
||||
file: undefined,
|
||||
order_limit_enabled: false,
|
||||
show_in_assets: true,
|
||||
min_order_cash: undefined,
|
||||
max_order_cash: undefined,
|
||||
auto_open_at: dayjs('00:00:00', 'HH:mm:ss'),
|
||||
@@ -314,8 +297,25 @@ async function ok() {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
if (methodOption.value === '1') {
|
||||
delete payload.reading_method
|
||||
if (isMeltedTradeGroup(payload.group_type)) {
|
||||
payload.amount = 1
|
||||
payload.direct_price = meltedPriceOption.value === 'fixed' ? 1 : 0
|
||||
payload.cash_fee_buy = payload.cash_fee_buy ?? 0
|
||||
payload.cash_fee_sell = payload.cash_fee_sell ?? 0
|
||||
|
||||
if (meltedPriceOption.value === 'app') {
|
||||
payload.reading_method = null
|
||||
delete payload.price
|
||||
payload.price_offset_percent = Number(payload.price_offset_percent ?? 0)
|
||||
} else if (meltedPriceOption.value === 'fixed') {
|
||||
payload.reading_method = null
|
||||
payload.price_offset_percent = 0
|
||||
} else {
|
||||
delete payload.price
|
||||
payload.price_offset_percent = Number(payload.price_offset_percent ?? 0)
|
||||
}
|
||||
} else if (methodOption.value === '1') {
|
||||
payload.reading_method = null
|
||||
payload.price_offset_percent = 0
|
||||
} else {
|
||||
delete payload.price
|
||||
|
||||
@@ -32,13 +32,24 @@
|
||||
import { message } from 'ant-design-vue'
|
||||
import { getWithdrawTable, updateSts } from '@/api/wallet'
|
||||
import type { Withdraw } from '@/model/wallet'
|
||||
import type { TableInput } from '@core/type/baseTable'
|
||||
import MobileCardList from '@/components/mobile/MobileCardList.vue'
|
||||
import { useMobileDetailSheet } from '@/components/mobile/composables/useMobileDetailSheet'
|
||||
import { useMobileTableList } from '@/components/mobile/composables/useMobileTableList'
|
||||
import { status } from '@/pages/menuPages/withdraw/information/hooks/status'
|
||||
import type { WithdrawFlowType } from '@/pages/menuPages/withdraw/types'
|
||||
import WithdrawMobileDetailSheet from './WithdrawMobileDetailSheet.vue'
|
||||
import WithdrawMobileListItem from './WithdrawMobileListItem.vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
flowType?: WithdrawFlowType
|
||||
}>(),
|
||||
{
|
||||
flowType: 'all'
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'withdraw', item: Withdraw): void
|
||||
(e: 'weight', item: Withdraw): void
|
||||
@@ -47,10 +58,17 @@ const emit = defineEmits<{
|
||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Withdraw>()
|
||||
|
||||
const { loading, search, items, hasMore, reload, loadMore, fetch } = useMobileTableList<Withdraw>({
|
||||
fetchApi: getWithdrawTable,
|
||||
fetchApi: (params: TableInput) => getWithdrawTable({ ...params, flow_type: props.flowType }),
|
||||
onFetched: (rows) => syncSelectedItem(rows)
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.flowType,
|
||||
() => {
|
||||
reload()
|
||||
}
|
||||
)
|
||||
|
||||
async function changeStatusHandler(
|
||||
item: Withdraw,
|
||||
statusEntry: [string, (typeof status)[keyof typeof status]]
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
<MobileDetailRow label="کد کاربر" :value="row.customer_id ?? '-'" />
|
||||
<MobileDetailRow label="نام و نام خانوادگی" full-width :value="row.customer?.name || '-'" />
|
||||
<MobileDetailRow label="شماره کارت" full-width :value="row.bank_account?.cart_number || '-'" />
|
||||
<MobileDetailRow label="شماره شبا" full-width :value="row.bank_account?.shaba_number || '-'" />
|
||||
<MobileDetailRow label="تاریخ ثبت" full-width :value="dateLabel(row)" />
|
||||
</MobileInfoGrid>
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<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 v-if="!isMobile" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<BaseTable v-if="!isMobile" :key="flowType" class="h-full min-h-0 min-w-0" v-bind="tblProps" @register="submit" ref="tbl">
|
||||
<template #extraHeader>
|
||||
<!-- <BaseButton type="primary" size="small" class="flex" @click="emits('add')">
|
||||
<div>افزودن مشتری عمده</div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<template #tracked="{ column, record }">
|
||||
<div v-if="record.tracked_number">{{ record.tracked_number }}</div>
|
||||
<div v-else>
|
||||
<BaseButton @click="emits('withdraw', record.id)">تایید برداشت</BaseButton>
|
||||
<BaseButton @click="emits('withdraw', record)">{{ confirmButtonLabel }}</BaseButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #action="{ column, record }">
|
||||
@@ -67,6 +67,7 @@
|
||||
v-else
|
||||
ref="mobileListRef"
|
||||
class="min-h-0 flex-1"
|
||||
:flow-type="props.flowType"
|
||||
@withdraw="(row) => emits('withdraw', row)"
|
||||
@weight="(row) => emits('weight', row)"
|
||||
/>
|
||||
@@ -88,6 +89,16 @@ import { getOrderTable } from '@/api/order'
|
||||
import {getWithdrawTable, updateSts} from '@/api/wallet'
|
||||
import WithdrawMobileCardList from './WithdrawMobileCardList.vue'
|
||||
import { isMobile } from '@core/utils/is'
|
||||
import type { WithdrawFlowType } from '../types'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
flowType?: WithdrawFlowType
|
||||
}>(),
|
||||
{
|
||||
flowType: 'all'
|
||||
}
|
||||
)
|
||||
|
||||
const [submit, reload] = useBaseTable()
|
||||
const mobileListRef = ref<null | { fetch: () => Promise<void> }>(null)
|
||||
@@ -107,14 +118,28 @@ function checkPermission(temp: string) {
|
||||
|
||||
//define table
|
||||
const table = ref(null)
|
||||
const lastTableParams = ref<TableInput & { flow_type?: string }>({})
|
||||
|
||||
const tblProps = ref<any>({
|
||||
const confirmButtonLabel = computed(() =>
|
||||
props.flowType === 'deposit' ? 'تایید واریز' : 'تایید برداشت'
|
||||
)
|
||||
|
||||
const tblProps = computed(() => ({
|
||||
columns: cols,
|
||||
api: (params: TableInput) => {
|
||||
return getWithdrawTable(params)
|
||||
api: async (params: TableInput) => {
|
||||
const payload = { ...params, flow_type: props.flowType }
|
||||
lastTableParams.value = payload
|
||||
return getWithdrawTable(payload)
|
||||
}
|
||||
//bordered: true
|
||||
})
|
||||
}))
|
||||
|
||||
watch(
|
||||
() => props.flowType,
|
||||
() => {
|
||||
reload()
|
||||
},
|
||||
{ flush: 'post' }
|
||||
)
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
async function changeCustomerActivationFunction(id: number) {
|
||||
@@ -138,8 +163,8 @@ async function fetch() {
|
||||
// table?.value?.loading.value = val
|
||||
// }
|
||||
defineExpose({
|
||||
fetch
|
||||
// spinTable
|
||||
fetch,
|
||||
getExportParams: () => ({ ...lastTableParams.value, flow_type: props.flowType })
|
||||
})
|
||||
|
||||
import { status } from '@/pages/menuPages/withdraw/information/hooks/status'
|
||||
|
||||
@@ -96,6 +96,15 @@ export const cols: BaseTableColumn[] = [
|
||||
return data.record.bank_account?.cart_number ?? '_'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'شماره شبا',
|
||||
dataIndex: 'bank_account.shaba_number',
|
||||
key: 'bank_account.shaba_number',
|
||||
type: 'Text',
|
||||
customRender: (data) => {
|
||||
return data.record.bank_account?.shaba_number ?? '_'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'وضعیت',
|
||||
dataIndex: 'sts',
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicMainCard>
|
||||
<div class="flex w-full h-full">
|
||||
<div class="flex w-full h-full flex-col">
|
||||
<div class="mb-3 flex shrink-0 flex-wrap items-center justify-between gap-3 px-1">
|
||||
<ARadioGroup v-model:value="flowType" button-style="solid">
|
||||
<ARadioButton value="all">همه</ARadioButton>
|
||||
<ARadioButton value="withdraw">برداشت</ARadioButton>
|
||||
<ARadioButton value="deposit">واریز</ARadioButton>
|
||||
</ARadioGroup>
|
||||
<BaseButton
|
||||
icon="vuesax-linear:document-download"
|
||||
:loading="exportLoading"
|
||||
@click="exportPdf"
|
||||
>
|
||||
خروجی PDF
|
||||
</BaseButton>
|
||||
</div>
|
||||
<tbl
|
||||
ref="table"
|
||||
class="w-full h-full"
|
||||
class="min-h-0 flex-1 w-full"
|
||||
:flow-type="flowType"
|
||||
@edit="edit"
|
||||
@delete="deleteItem"
|
||||
@withdraw="openWithdraw"
|
||||
@@ -28,7 +43,7 @@
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<div class="">
|
||||
<a-form-item label="شماره پیگیری" name="tracked_number" :rules="inputRules()">
|
||||
<BasicInput v-model:value="form.tracked_number" type="number" />
|
||||
<BasicInput v-model:value="form.tracked_number" />
|
||||
</a-form-item>
|
||||
<a-form-item label="تاریخ" name="date_at">
|
||||
<BaseDatePicker v-model:value="form.date_at" show-time format="YYYY-MM-DD HH:mm" />
|
||||
@@ -99,8 +114,10 @@ import type { Customer } from '@/model/customer'
|
||||
import { createCustomer, deleteCustomer, updateCustomer } from '@/api/customer'
|
||||
import type { Order } from '@/model/order'
|
||||
import type { Wallet, Withdraw } from '@/model/wallet'
|
||||
import {completeWithdraw, getWithdrawById, sendWeights} from '@/api/wallet'
|
||||
import {completeWithdraw, exportWithdrawReport, getWithdrawById, sendWeights} from '@/api/wallet'
|
||||
import Button from "@core/components/BaseButton";
|
||||
import type { WithdrawFlowType } from '@/pages/menuPages/withdraw/types'
|
||||
import { openHtmlPrintWindow } from '@/pages/menuPages/withdraw/utils/openHtmlPrintWindow'
|
||||
//import DatePicker from '@core/components/_sample/components/datePicker.vue'
|
||||
|
||||
//loading
|
||||
@@ -110,9 +127,29 @@ const loading = ref<boolean>(false)
|
||||
const store = useStaticApiListsStore()
|
||||
|
||||
//define tbl
|
||||
const table = ref(null)
|
||||
const table = ref<{ fetch: () => Promise<void>; getExportParams?: () => Record<string, unknown> } | null>(null)
|
||||
const refreshTable = () => table?.value?.fetch()
|
||||
|
||||
const flowType = ref<WithdrawFlowType>('all')
|
||||
const exportLoading = ref(false)
|
||||
|
||||
async function exportPdf() {
|
||||
try {
|
||||
exportLoading.value = true
|
||||
const params = {
|
||||
...(table.value?.getExportParams?.() ?? {}),
|
||||
flow_type: flowType.value
|
||||
}
|
||||
const html = await exportWithdrawReport(params as any)
|
||||
openHtmlPrintWindow(html)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error('خطا در تهیه خروجی PDF')
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
let updateKey = ref<number>(0)
|
||||
|
||||
//form
|
||||
@@ -215,8 +252,8 @@ async function edit(record: Customer) {
|
||||
loading.value = false
|
||||
}*/
|
||||
|
||||
function openWithdraw(val: Withdraw) {
|
||||
confirmWithdraw.value = val
|
||||
function openWithdraw(val: Withdraw | number) {
|
||||
confirmWithdraw.value = typeof val === 'number' ? { id: val } : val
|
||||
resetData()
|
||||
confirmWithdrawVisible.value = true
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type WithdrawFlowType = 'all' | 'withdraw' | 'deposit'
|
||||
@@ -0,0 +1,11 @@
|
||||
export function openHtmlPrintWindow(html: string) {
|
||||
const printWindow = window.open('', '_blank')
|
||||
if (!printWindow) {
|
||||
throw new Error('popup blocked')
|
||||
}
|
||||
|
||||
printWindow.document.open()
|
||||
printWindow.document.write(html)
|
||||
printWindow.document.close()
|
||||
printWindow.focus()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/** دستهبندی یکسان محصول در همه اپهای ماژولار */
|
||||
export const PRODUCT_CATEGORY_ABSHODE = 'آبشده'
|
||||
|
||||
export const UNIFIED_PRODUCT_CATEGORIES = [
|
||||
PRODUCT_CATEGORY_ABSHODE,
|
||||
'مکعب',
|
||||
'گوی',
|
||||
'سکه',
|
||||
'سکه پارسیان',
|
||||
'مس',
|
||||
'نقره',
|
||||
] as const
|
||||
|
||||
export const LEGACY_GROUP_KATOD = 'کاتد مس'
|
||||
export const LEGACY_GROUP_MAFTOL = 'مفتول مس'
|
||||
export const LEGACY_GROUP_GOLD = 'طلا'
|
||||
|
||||
export function normalizeProductGroupType(groupType?: string | null) {
|
||||
return (groupType ?? '').trim()
|
||||
}
|
||||
|
||||
export function isMeltedTradeGroup(groupType?: string | null) {
|
||||
const group = normalizeProductGroupType(groupType)
|
||||
return group === PRODUCT_CATEGORY_ABSHODE || group === LEGACY_GROUP_KATOD
|
||||
}
|
||||
|
||||
export type MeltedPriceSource = 'app' | 'fixed' | 'reference'
|
||||
|
||||
export function resolveMeltedPriceSource(product?: {
|
||||
reading_method?: string | null
|
||||
direct_price?: number | null
|
||||
} | null): MeltedPriceSource {
|
||||
if (product?.reading_method) {
|
||||
return 'reference'
|
||||
}
|
||||
if (Number(product?.direct_price) === 1) {
|
||||
return 'fixed'
|
||||
}
|
||||
return 'app'
|
||||
}
|
||||
|
||||
export function buildCategorySelectItems(metalItems: Array<{ key: number; value: string }>) {
|
||||
const metalHint = metalItems.map((item) => item.value).join('، ')
|
||||
|
||||
return UNIFIED_PRODUCT_CATEGORIES.map((category) => ({
|
||||
key: category,
|
||||
value: category === PRODUCT_CATEGORY_ABSHODE ? `${category} (${metalHint})` : category,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export const TRADE_WEIGHT_UNIT_GRAM = 'گرم'
|
||||
export const TRADE_WEIGHT_UNIT_KILOGRAM = 'کیلوگرم'
|
||||
|
||||
export function isKilogramUnit(unit?: string | null) {
|
||||
const value = (unit ?? '').trim()
|
||||
return value === TRADE_WEIGHT_UNIT_KILOGRAM || value === 'kilogram' || value === 'kg'
|
||||
}
|
||||
|
||||
export function resolveTradeWeightUnit(settingValue?: string | number | null) {
|
||||
return isKilogramUnit(settingValue?.toString()) ? TRADE_WEIGHT_UNIT_KILOGRAM : TRADE_WEIGHT_UNIT_GRAM
|
||||
}
|
||||
|
||||
export function weightFieldLabelForUnit(unit?: string | null) {
|
||||
return isKilogramUnit(unit) ? 'وزن به کیلوگرم' : 'وزن به گرم'
|
||||
}
|
||||
Reference in New Issue
Block a user