change gift-card
This commit is contained in:
+17
-2
@@ -1,6 +1,16 @@
|
||||
export interface GiftcardCustomer {
|
||||
id: number
|
||||
name: string
|
||||
mobile: string
|
||||
}
|
||||
|
||||
export interface Giftcard {
|
||||
id: number
|
||||
customer_id: any
|
||||
name?: string
|
||||
mobile?: string
|
||||
customer?: GiftcardCustomer
|
||||
show_in_app?: number | boolean
|
||||
count: number
|
||||
amount: number
|
||||
total: number
|
||||
@@ -35,9 +45,14 @@ export interface AssignedTo {
|
||||
mobile: string
|
||||
}
|
||||
|
||||
export type GiftCardAssignMode = 'customer' | 'public'
|
||||
|
||||
export interface GiftcardInput {
|
||||
mobile: string
|
||||
name: string
|
||||
assignMode: GiftCardAssignMode
|
||||
customer_id?: number
|
||||
show_in_app?: number
|
||||
mobile?: string
|
||||
name?: string
|
||||
count: number
|
||||
amount: number
|
||||
unit: string
|
||||
|
||||
@@ -12,7 +12,13 @@
|
||||
@close="close"
|
||||
>
|
||||
<div class="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="min-w-0">
|
||||
<div class="min-w-0 space-y-1">
|
||||
<p v-if="giftCardCustomer" class="m-0 text-13px font-600" style="color: var(--admin-text)">
|
||||
مشتری: {{ giftCardCustomer.name }} — <span dir="ltr">{{ giftCardCustomer.mobile }}</span>
|
||||
</p>
|
||||
<p v-else class="m-0 text-13px font-600" style="color: var(--admin-text)">
|
||||
کد عمومی — بدون مشتری مشخص
|
||||
</p>
|
||||
<p class="m-0 text-13px text-[var(--admin-text-secondary)]">
|
||||
{{ codes.length }} کد — دانلود به صورت اکسل یا PDF
|
||||
</p>
|
||||
@@ -102,6 +108,7 @@ import {
|
||||
downloadGiftCardCodesPdf,
|
||||
type GiftCardCodesExportMeta
|
||||
} from '../utils/exportGiftCardCodes'
|
||||
import { resolveGiftCardCustomer } from '../utils/giftCardCustomer'
|
||||
import { SELECT_DROPDOWN_Z_INDEX } from '@/constants/overlayZIndex'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -129,20 +136,22 @@ const columns = [
|
||||
{ title: 'وضعیت', dataIndex: 'sts', key: 'sts' },
|
||||
{ title: 'تاریخ انقضا', dataIndex: 'expiry_date', key: 'expiry_date' },
|
||||
{ title: 'تاریخ استفاده', dataIndex: 'used_at', key: 'used_at' },
|
||||
{ title: 'نام کاربر اختصاص داده شده', dataIndex: 'assigned_to.name', key: 'assigned_to_name' },
|
||||
{ title: 'شماره موبایل کاربر اختصاص داده شده', dataIndex: 'assigned_to.mobile', key: 'assigned_to_mobile' }
|
||||
{ title: 'نام استفادهکننده', dataIndex: 'assigned_to.name', key: 'assigned_to_name' },
|
||||
{ title: 'موبایل استفادهکننده', dataIndex: 'assigned_to.mobile', key: 'assigned_to_mobile' }
|
||||
]
|
||||
|
||||
function close() {
|
||||
visibleProxy.value = false
|
||||
}
|
||||
|
||||
const giftCardCustomer = computed(() => resolveGiftCardCustomer(props.giftCard))
|
||||
|
||||
const exportMeta = computed<GiftCardCodesExportMeta>(() => ({
|
||||
id: props.giftCard?.id,
|
||||
unit: props.giftCard?.unit,
|
||||
count: props.giftCard?.count,
|
||||
customerName: (props.giftCard as Giftcard & { customer?: { name?: string } })?.customer?.name,
|
||||
customerMobile: (props.giftCard as Giftcard & { customer?: { mobile?: string } })?.customer?.mobile,
|
||||
customerName: giftCardCustomer.value?.name,
|
||||
customerMobile: giftCardCustomer.value?.mobile,
|
||||
createdAt: props.giftCard?.created_at
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,25 +1,48 @@
|
||||
<template>
|
||||
<a-form :model="form" ref="formRef" autocomplete="off">
|
||||
<a-form :model="props.form" ref="formRef" autocomplete="off">
|
||||
<div class="space-y-3">
|
||||
<a-form-item name="mobile" :rules="inputLenRules(11)">
|
||||
<a-form-item name="assignMode" :rules="inputRules()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="شماره موبایل مشتری" :help="giftCardMobileHelp" />
|
||||
<AdminLabelWithHelp label="نوع ثبت" :help="giftCardAssignModeHelp" />
|
||||
</template>
|
||||
<BasicInput v-model:value="form.mobile" type="tel" placeholder="09123456789" />
|
||||
<a-radio-group v-model:value="props.form.assignMode" button-style="solid" @change="onAssignModeChange">
|
||||
<a-radio-button value="public">کد عمومی (بدون مشتری)</a-radio-button>
|
||||
<a-radio-button value="customer">اختصاص به مشتری</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="name" :rules="inputRules()">
|
||||
<a-form-item v-if="isPublicMode" name="show_in_app" :rules="inputRules()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="نام مشتری" :help="giftCardNameHelp" />
|
||||
<AdminLabelWithHelp label="نمایش در اپ کاربر" :help="giftCardPublicVisibilityHelp" />
|
||||
</template>
|
||||
<BasicInput v-model:value="form.name" placeholder="نام و نام خانوادگی مشتری" />
|
||||
<a-radio-group v-model:value="props.form.show_in_app" button-style="solid" class="flex flex-col gap-2 sm:flex-row">
|
||||
<a-radio-button :value="1">نمایش برای همه</a-radio-button>
|
||||
<a-radio-button :value="0">مخفی — فقط با وارد کردن کد</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="isCustomerMode" name="customer_id" :rules="customerIdRules">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="مشتری" :help="giftCardCustomerSelectHelp" />
|
||||
</template>
|
||||
<a-select
|
||||
v-model:value="props.form.customer_id"
|
||||
show-search
|
||||
allow-clear
|
||||
placeholder="انتخاب مشتری"
|
||||
class="w-full"
|
||||
:options="customerOptions"
|
||||
:loading="customersLoading"
|
||||
:filter-option="filterCustomer"
|
||||
:dropdown-style="{ zIndex: SELECT_DROPDOWN_Z_INDEX }"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="unit" :rules="inputRules()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="نوع کارت هدیه" :help="giftCardUnitHelp" />
|
||||
</template>
|
||||
<a-radio-group v-model:value="form.unit" button-style="solid">
|
||||
<a-radio-group v-model:value="props.form.unit" button-style="solid">
|
||||
<a-radio-button value="1">ریالی</a-radio-button>
|
||||
<a-radio-button value="2">{{ metalLabel.goldWalletShort() }}</a-radio-button>
|
||||
</a-radio-group>
|
||||
@@ -29,19 +52,19 @@
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="تعداد کدها" :help="giftCardCountHelp" />
|
||||
</template>
|
||||
<BasicInput v-model:value="form.count" type="tel" placeholder="تعداد کدهای کارت هدیه" />
|
||||
<BasicInput v-model:value="props.form.count" type="tel" placeholder="تعداد کدهای کارت هدیه" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="amount" :rules="inputRulesNumber()">
|
||||
<template #label>
|
||||
<AdminLabelWithHelp
|
||||
:label="`مقدار ${form.unit === '2' ? '(گرم)' : '(ریال)'}`"
|
||||
:label="`مقدار ${props.form.unit === '2' ? '(گرم)' : '(ریال)'}`"
|
||||
:help="giftCardAmountHelp"
|
||||
/>
|
||||
</template>
|
||||
<BasicInput
|
||||
v-model:value="form.amount"
|
||||
:addon-before="form.unit === '2' ? 'گرم' : 'ریال'"
|
||||
v-model:value="props.form.amount"
|
||||
:addon-before="props.form.unit === '2' ? 'گرم' : 'ریال'"
|
||||
type="tel"
|
||||
placeholder="مقدار کارت هدیه"
|
||||
/>
|
||||
@@ -51,11 +74,11 @@
|
||||
<template #label>
|
||||
<AdminLabelWithHelp label="تاریخ انقضا" :help="giftCardExpiryHelp" />
|
||||
</template>
|
||||
<BaseDatePicker v-model:value="form.expiry_date" format="YYYY-MM-DD" />
|
||||
<BaseDatePicker v-model:value="props.form.expiry_date" format="YYYY-MM-DD" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="توضیحات" name="description" class="mb-0">
|
||||
<BasicTextArea v-model:value="form.description" place-holder="توضیحات اختیاری — فقط برای یادداشت شما در پنل" />
|
||||
<BasicTextArea v-model:value="props.form.description" place-holder="توضیحات اختیاری — فقط برای یادداشت شما در پنل" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
</a-form>
|
||||
@@ -64,24 +87,55 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance } from 'ant-design-vue'
|
||||
import type { GiftcardInput } from '@/model/giftCard'
|
||||
import { getCustomersList } from '@/api/customer'
|
||||
import type { Customer } from '@/model/customer'
|
||||
import AdminLabelWithHelp from '@/components/AdminLabelWithHelp.vue'
|
||||
import {
|
||||
giftCardAmountHelp,
|
||||
giftCardAssignModeHelp,
|
||||
giftCardCountHelp,
|
||||
giftCardCustomerSelectHelp,
|
||||
giftCardExpiryHelp,
|
||||
giftCardMobileHelp,
|
||||
giftCardNameHelp,
|
||||
giftCardPublicVisibilityHelp,
|
||||
giftCardUnitHelp,
|
||||
} from '@/utils/adminHelpCopy'
|
||||
import { inputLenRules, inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
import { SELECT_DROPDOWN_Z_INDEX } from '@/constants/overlayZIndex'
|
||||
import { inputRules, inputRulesNumber } from '@/utils/inputRules'
|
||||
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
form: GiftcardInput
|
||||
}>()
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const customers = ref<Customer[]>([])
|
||||
const customersLoading = ref(false)
|
||||
|
||||
const isCustomerMode = computed(() => props.form.assignMode === 'customer')
|
||||
const isPublicMode = computed(() => props.form.assignMode === 'public')
|
||||
|
||||
const customerOptions = computed(() =>
|
||||
customers.value.map((customer) => ({
|
||||
value: customer.id,
|
||||
label: `${customer.name ?? '—'} — ${customer.mobile ?? ''}`.trim(),
|
||||
}))
|
||||
)
|
||||
|
||||
const customerIdRules = [
|
||||
{
|
||||
validator(_rule: unknown, value: unknown) {
|
||||
if (props.form.assignMode !== 'customer') {
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return Promise.reject('انتخاب مشتری الزامی است')
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
]
|
||||
|
||||
const expiryDateRules = [
|
||||
...inputRules(),
|
||||
@@ -103,6 +157,39 @@ const expiryDateRules = [
|
||||
},
|
||||
]
|
||||
|
||||
function filterCustomer(input: string, option?: { label?: string }) {
|
||||
return String(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
|
||||
function onAssignModeChange() {
|
||||
if (props.form.assignMode === 'public') {
|
||||
props.form.customer_id = undefined
|
||||
if (props.form.show_in_app === undefined) {
|
||||
props.form.show_in_app = 0
|
||||
}
|
||||
formRef.value?.clearValidate?.(['customer_id'])
|
||||
return
|
||||
}
|
||||
|
||||
props.form.show_in_app = 0
|
||||
formRef.value?.clearValidate?.(['show_in_app'])
|
||||
}
|
||||
|
||||
async function loadCustomers() {
|
||||
customersLoading.value = true
|
||||
try {
|
||||
customers.value = (await getCustomersList()) ?? []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
customersLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadCustomers()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validateFields: () => formRef.value?.validateFields(),
|
||||
resetFields: () => formRef.value?.resetFields(),
|
||||
|
||||
@@ -3,6 +3,7 @@ import { toJalali } from '@core/utils/useDataTime'
|
||||
import CustomTag from '@/components/CustomTag.vue'
|
||||
import { usePrice } from '@core/utils/usePrice'
|
||||
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||
import { giftCardCustomerLabel, giftCardCustomerMobile } from '../utils/giftCardCustomer'
|
||||
/*import BudgetSelect from '@/components/BudgetSelect.vue'
|
||||
import { useStaticApiListsStore } from '@/store/staticApisStore'
|
||||
import type { FilterSelect } from '@/model/filterSelect'
|
||||
@@ -105,18 +106,14 @@ export function getGiftCardColumns(): BaseTableColumn[] {
|
||||
dataIndex: 'customer.name',
|
||||
key: 'customer.name',
|
||||
type: 'Text',
|
||||
customRender: ({ record }) => {
|
||||
return record.customer?.name ?? '-'
|
||||
}
|
||||
customRender: ({ record }) => giftCardCustomerLabel(record)
|
||||
},
|
||||
{
|
||||
title: 'شماره موبایل مشتری',
|
||||
dataIndex: 'customer.mobile',
|
||||
key: 'customer.mobile',
|
||||
type: 'Number',
|
||||
customRender: ({ record }) => {
|
||||
return record.customer?.mobile ?? '-'
|
||||
}
|
||||
customRender: ({ record }) => giftCardCustomerMobile(record)
|
||||
},
|
||||
{
|
||||
title: 'توضیحات',
|
||||
|
||||
@@ -135,6 +135,7 @@ import {
|
||||
discountPercentHelp,
|
||||
} from '@/utils/adminHelpCopy'
|
||||
import BaseDatePicker from '@core/components/BaseDatePicker'
|
||||
import { giftCardCustomerLabel, giftCardCustomerMobile } from './utils/giftCardCustomer'
|
||||
|
||||
//loading
|
||||
const loading = ref<boolean>(false)
|
||||
@@ -169,8 +170,9 @@ function closeGiftCardSheet() {
|
||||
|
||||
//form
|
||||
const form = ref<GiftcardInput>({
|
||||
mobile: '',
|
||||
name: '',
|
||||
assignMode: 'public',
|
||||
customer_id: undefined,
|
||||
show_in_app: 0,
|
||||
count: undefined,
|
||||
amount: undefined,
|
||||
unit: '1',
|
||||
@@ -187,8 +189,8 @@ const selectedGiftCard = ref<Giftcard | null>(null)
|
||||
function info(val: Giftcard) {
|
||||
// نمایش اطلاعات اصلی در BasicInfoModal
|
||||
infoParams.title = 'اطلاعات کارت هدیه'
|
||||
infoParams.name = val.customer?.name ?? '-'
|
||||
infoParams.type = val.customer?.mobile ?? '-'
|
||||
infoParams.name = giftCardCustomerLabel(val)
|
||||
infoParams.type = giftCardCustomerMobile(val)
|
||||
infoParams.date = toJalaliNoTime(val.created_at)
|
||||
infoParams.time = toTime(val.created_at)
|
||||
infoParams.itemsTitle = 'اطلاعات کارت هدیه'
|
||||
@@ -197,6 +199,18 @@ function info(val: Giftcard) {
|
||||
key: 'کد',
|
||||
value: val.id ?? '-'
|
||||
},
|
||||
{
|
||||
key: 'نوع ثبت',
|
||||
value: val.customer_id ? 'اختصاصی' : Number(val.show_in_app) === 1 ? 'عمومی — نمایش در اپ' : 'عمومی — مخفی (فقط با کد)'
|
||||
},
|
||||
{
|
||||
key: 'مشتری',
|
||||
value: giftCardCustomerLabel(val)
|
||||
},
|
||||
{
|
||||
key: 'موبایل مشتری',
|
||||
value: giftCardCustomerMobile(val)
|
||||
},
|
||||
{
|
||||
key: 'نوع کارت هدیه',
|
||||
value: val.unit === 3 ? 'تخفیف' : val.unit === 2 ? metalLabel.goldWalletShort() : 'ریالی'
|
||||
@@ -261,8 +275,9 @@ async function edit(record: Giftcard) {
|
||||
addGiftCardVisible.value = true
|
||||
editId = record.id
|
||||
form.value = {
|
||||
mobile: record.customer?.mobile ?? '',
|
||||
name: record.customer?.name ?? '',
|
||||
assignMode: record.customer_id ? 'customer' : 'public',
|
||||
customer_id: record.customer_id ?? undefined,
|
||||
show_in_app: record.customer_id ? 0 : Number(record.show_in_app) === 1 ? 1 : 0,
|
||||
count: record.count,
|
||||
amount: record.amount,
|
||||
unit: record.unit.toString(),
|
||||
@@ -330,8 +345,9 @@ async function addGiftOrDiscount(type: 'gift' | 'discount' = 'gift') {
|
||||
const resetData = () => {
|
||||
giftCardEditorRef.value?.resetFields?.()
|
||||
form.value = {
|
||||
mobile: '',
|
||||
name: '',
|
||||
assignMode: 'public',
|
||||
customer_id: undefined,
|
||||
show_in_app: 0,
|
||||
count: undefined,
|
||||
amount: undefined,
|
||||
unit: '1',
|
||||
@@ -353,19 +369,26 @@ async function ok() {
|
||||
await giftCardEditorRef.value?.validateFields?.().then(async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const formData = {
|
||||
...form.value,
|
||||
const formData: Record<string, unknown> = {
|
||||
count: Number(form.value.count),
|
||||
amount: Number(form.value.amount),
|
||||
unit: form.value.unit,
|
||||
expiry_date: form.value.expiry_date,
|
||||
description: form.value.description,
|
||||
}
|
||||
if (form.value.assignMode === 'customer' && form.value.customer_id) {
|
||||
formData.customer_id = Number(form.value.customer_id)
|
||||
} else if (form.value.assignMode === 'public') {
|
||||
formData.show_in_app = Number(form.value.show_in_app) === 1 ? 1 : 0
|
||||
}
|
||||
if (formData.expiry_date) {
|
||||
// تبدیل به میلادی و تنظیم به آخر روز
|
||||
formData.expiry_date = dayjs(formData.expiry_date)
|
||||
formData.expiry_date = dayjs(formData.expiry_date as string)
|
||||
.calendar('gregory')
|
||||
.endOf('day')
|
||||
.format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
await createGiftCard(formData)
|
||||
await createGiftCard(formData as GiftcardInput)
|
||||
message.success('کارت هدیه با موفقیت ثبت شد')
|
||||
addGiftCardVisible.value = false
|
||||
resetData()
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { Giftcard } from '@/model/giftCard'
|
||||
|
||||
export function resolveGiftCardCustomer(giftCard?: Giftcard | null) {
|
||||
if (!giftCard) return null
|
||||
|
||||
const name = giftCard.customer?.name ?? giftCard.name
|
||||
const mobile = giftCard.customer?.mobile ?? giftCard.mobile
|
||||
|
||||
if (name || mobile) {
|
||||
return {
|
||||
name: name || '-',
|
||||
mobile: mobile || '-',
|
||||
}
|
||||
}
|
||||
|
||||
if (giftCard.customer_id) {
|
||||
return {
|
||||
name: `مشتری #${giftCard.customer_id}`,
|
||||
mobile: '-',
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function giftCardCustomerLabel(giftCard?: Giftcard | null) {
|
||||
return resolveGiftCardCustomer(giftCard)?.name ?? '— (کد عمومی)'
|
||||
}
|
||||
|
||||
export function giftCardCustomerMobile(giftCard?: Giftcard | null) {
|
||||
return resolveGiftCardCustomer(giftCard)?.mobile ?? '-'
|
||||
}
|
||||
@@ -41,17 +41,20 @@ export const settingsProfileBannersThemeHelp =
|
||||
export const pricingPageIntro =
|
||||
'اینجا تاریخچهٔ همگامسازی قیمت را میبینید. دکمهٔ «بهروزرسانی قیمت» همان لحظه قیمت را از منبعی که در تنظیمات مشخص کردهاید میگیرد و روی محصولات اعمال میکند. اگر قیمت عوض نشده باشد پیام «تغییری نکرد» میآید.'
|
||||
|
||||
export const giftCardMobileHelp =
|
||||
'شماره موبایلی که کارت هدیه برایش ساخته میشود. باید ۱۱ رقم و با ۰۹ شروع شود.'
|
||||
export const giftCardAssignModeHelp =
|
||||
'اختصاص به مشتری: کدها فقط برای همان شخص در اپ قابل استفاده است. کد عمومی: فقط کد ساخته میشود و بعداً به هر کسی میتوانید بدهید.'
|
||||
|
||||
export const giftCardNameHelp =
|
||||
'نام مشتری فقط برای شناسایی در پنل شماست؛ روی اپ مشتری تأثیر مستقیم ندارد.'
|
||||
export const giftCardCustomerSelectHelp =
|
||||
'مشتری را از لیست انتخاب کنید؛ نام و موبایل خودکار از پروندهٔ مشتری پر میشود.'
|
||||
|
||||
export const giftCardPublicVisibilityHelp =
|
||||
'نمایش برای همه: کد(ها) در لیست اپ همه کاربران دیده میشود. مخفی: فقط کسی که کد را از مدیر گرفته و دستی وارد کند میتواند استفاده کند.'
|
||||
|
||||
export const giftCardUnitHelp =
|
||||
'ریالی = اعتبار کیف پول (ریال). مس = وزن مس که به کیف فلزی مشتری واریز میشود.'
|
||||
|
||||
export const giftCardCountHelp =
|
||||
'چند کد یکتا ساخته شود. مثلاً ۳ → سه کارت هدیه جدا، هر کدام یک کد و یک شارژ کامل با همان مبلغ.'
|
||||
'تعداد کد یکتا در همان بچ. مثلاً ۳ → یک رکورد با سه کد جداگانه و همان مبلغ برای هر کد.'
|
||||
|
||||
export const giftCardAmountHelp =
|
||||
'مقدار هر کد: اگر ریالی انتخاب کردید مبلغ به ریال؛ اگر مس انتخاب کردید وزن به واحد معامله.'
|
||||
|
||||
Reference in New Issue
Block a user