Add customer multi-level referral profit editor and related settings UI.
Expose enable_customer_level_profit in config and let admins edit per-customer level percents from the customers table/mobile sheets. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -14,4 +14,6 @@ export const postAdminChatUnfocus = async (room_id: string) =>
|
|||||||
await useAxios.post('/admin/chat/unfocus', { room_id })
|
await useAxios.post('/admin/chat/unfocus', { room_id })
|
||||||
|
|
||||||
export const postAdminChatEnsureWebhook = async () =>
|
export const postAdminChatEnsureWebhook = async () =>
|
||||||
await useAxios.post('/admin/chat/ensure-webhook')
|
await useAxios.post('/admin/chat/ensure-webhook', undefined, {
|
||||||
|
meta: { silent: true },
|
||||||
|
})
|
||||||
|
|||||||
+20
-14
@@ -87,29 +87,35 @@ _axios.interceptors.response.use(
|
|||||||
await router.push({ name: 'login' })
|
await router.push({ name: 'login' })
|
||||||
} else if (error.response && error.response.status == 403) {
|
} else if (error.response && error.response.status == 403) {
|
||||||
if (!error.config?.meta?.silent) {
|
if (!error.config?.meta?.silent) {
|
||||||
message.error('شما دسترسی لازم برای انجام این عملیات را ندارید')
|
message.error('شما دسترسی لازم برای انجام این عملیات را ندارید (403)')
|
||||||
}
|
}
|
||||||
} else if (error.response && error.response.status == 422) {
|
} else if (error.response && error.response.status == 422) {
|
||||||
if (!error.config?.meta?.silent) {
|
|
||||||
message.error(error.response.data?.message)
|
|
||||||
}
|
|
||||||
} else if (error.response) {
|
|
||||||
if (!error.config?.meta?.silent) {
|
if (!error.config?.meta?.silent) {
|
||||||
const apiMessage = error.response.data?.message
|
const apiMessage = error.response.data?.message
|
||||||
message.error(
|
message.error(
|
||||||
typeof apiMessage === 'string' && apiMessage.trim()
|
typeof apiMessage === 'string' && apiMessage.trim()
|
||||||
? formatApiErrorMessage(apiMessage)
|
? `${apiMessage} (422)`
|
||||||
: 'خطا در بارگذاری اطلاعات',
|
: 'خطا در اعتبارسنجی (422)',
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (error.response) {
|
||||||
|
if (!error.config?.meta?.silent) {
|
||||||
|
const status = Number(error.response.status)
|
||||||
|
if (status >= 500) {
|
||||||
|
message.error(`خطا در برقراری ارتباط با سرور (${status})`)
|
||||||
|
} else {
|
||||||
|
const apiMessage = error.response.data?.message
|
||||||
|
const base =
|
||||||
|
typeof apiMessage === 'string' && apiMessage.trim()
|
||||||
|
? formatApiErrorMessage(apiMessage)
|
||||||
|
: 'خطا در بارگذاری اطلاعات'
|
||||||
|
message.error(`${base} (${status})`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!error.config?.meta?.silent) {
|
||||||
|
// بدون status (نه 4xx نه 5xx): قطع شبکه، timeout، DNS، …
|
||||||
|
message.error('لطفا اینترنت خود را بررسی کنید و مجددا تلاش کنید')
|
||||||
}
|
}
|
||||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
|
||||||
// Do something with response error
|
|
||||||
|
|
||||||
//const { response, code, message, config } = error || {}
|
|
||||||
//const errorMessageMode = config?.requestOptions?.errorMessageMode || 'none'
|
|
||||||
//const msg: string = response?.data?.error?.message ?? ''
|
|
||||||
//checkStatus(error?.response?.status, msg)
|
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -41,5 +41,12 @@ export interface Customer {
|
|||||||
gold: number
|
gold: number
|
||||||
moshtari_code?: string | number | null
|
moshtari_code?: string | number | null
|
||||||
pargar_account_id?: string | number | null
|
pargar_account_id?: string | number | null
|
||||||
|
/** درصد سود چندسطحی از کارمزد زیرمجموعهها — `{ level, present }` */
|
||||||
|
profit_amount_by_lvl?: ProfitByLevel[]
|
||||||
// print_name: string
|
// print_name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProfitByLevel {
|
||||||
|
level: number
|
||||||
|
present: number
|
||||||
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|
|||||||
title: 'دسترسی مشتری به قابلیتها',
|
title: 'دسترسی مشتری به قابلیتها',
|
||||||
icon: 'vuesax-linear:people',
|
icon: 'vuesax-linear:people',
|
||||||
match: (k) =>
|
match: (k) =>
|
||||||
/^(enable_conditional_order|enable_product_funds|has_page_price_trigger|has_page_notification|banner_enabled|jibit_enabled|customer_direct_sms|has_page_earn_money|group_sms|tracking_systems_enabled|permision__enabled|show_to_users_chat)$/.test(
|
/^(enable_conditional_order|enable_product_funds|has_page_price_trigger|has_page_notification|has_page_gift_card|enable_customer_level_profit|banner_enabled|jibit_enabled|customer_direct_sms|has_page_earn_money|group_sms|tracking_systems_enabled|permision__enabled|show_to_users_chat)$/.test(
|
||||||
k
|
k
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -263,6 +263,8 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
|
|||||||
enable_product_funds: 'switch',
|
enable_product_funds: 'switch',
|
||||||
has_page_price_trigger: 'switch',
|
has_page_price_trigger: 'switch',
|
||||||
has_page_notification: 'switch',
|
has_page_notification: 'switch',
|
||||||
|
has_page_gift_card: 'switch',
|
||||||
|
enable_customer_level_profit: 'switch',
|
||||||
min_withdraw: 'price',
|
min_withdraw: 'price',
|
||||||
min_deposit: 'price',
|
min_deposit: 'price',
|
||||||
min_first_buy: 'price',
|
min_first_buy: 'price',
|
||||||
@@ -363,6 +365,7 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
|
|||||||
deposit_support_message_text: 'longtext',
|
deposit_support_message_text: 'longtext',
|
||||||
deposit_account_holder: 'text',
|
deposit_account_holder: 'text',
|
||||||
deposit_card_number: 'text',
|
deposit_card_number: 'text',
|
||||||
|
deposit_account_number: 'text',
|
||||||
deposit_sheba: 'text',
|
deposit_sheba: 'text',
|
||||||
invite_link_text: 'longtext',
|
invite_link_text: 'longtext',
|
||||||
support_messenger: 'select',
|
support_messenger: 'select',
|
||||||
@@ -649,6 +652,10 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
'روشن: قابلیت سفارش شرطی سراسری فعال است. خاموش: منو و ارزیابی اجرا نمیشود. فقط از صفحه دیپلوی قابل تغییر است.',
|
'روشن: قابلیت سفارش شرطی سراسری فعال است. خاموش: منو و ارزیابی اجرا نمیشود. فقط از صفحه دیپلوی قابل تغییر است.',
|
||||||
enable_product_funds:
|
enable_product_funds:
|
||||||
'روشن: ساخت و مدیریت قلک per-product در اپ مشتری فعال است (یک محصول در هر قلک؛ انتقال بدون سند حسابداری). خاموش: منو و API ساخت قلک جدید در دسترس نیست. فقط از دیپلوی قابل تغییر است.',
|
'روشن: ساخت و مدیریت قلک per-product در اپ مشتری فعال است (یک محصول در هر قلک؛ انتقال بدون سند حسابداری). خاموش: منو و API ساخت قلک جدید در دسترس نیست. فقط از دیپلوی قابل تغییر است.',
|
||||||
|
has_page_gift_card:
|
||||||
|
'روشن: منوی «ثبت کارت هدیه» و صفحه ثبت کد در اپ کاربر فعال است. خاموش: منو و صفحه مخفی است و API استفاده از کد هدیه در دسترس نیست. فقط از دیپلوی قابل تغییر است.',
|
||||||
|
enable_customer_level_profit:
|
||||||
|
'روشن: در لیست مشتریان آیکون تنظیم درصد سود هر سطح زیرمجموعه فعال است و پس از سفارش، سهم کارمزد طبق درصدهای هر معرف به کیف طلا واریز میشود. خاموش: آیکون و واریز سود چندسطحی غیرفعال است. فقط از دیپلوی قابل تغییر است.',
|
||||||
has_page_price_trigger:
|
has_page_price_trigger:
|
||||||
'روشن: منوی هشدار قیمت در اپ مشتری نمایش داده میشود. خاموش: منو و صفحه مخفی است. فقط از صفحه دیپلوی قابل تغییر است.',
|
'روشن: منوی هشدار قیمت در اپ مشتری نمایش داده میشود. خاموش: منو و صفحه مخفی است. فقط از صفحه دیپلوی قابل تغییر است.',
|
||||||
has_page_notification:
|
has_page_notification:
|
||||||
@@ -756,13 +763,15 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
|||||||
support_messenger_id:
|
support_messenger_id:
|
||||||
'شناسه تماس در پیامرسان انتخابشده: یوزرنیم، آیدی یا لینک کامل. فقط روی آیکون پیامرسان و دکمه شناور اعمال میشود؛ تماس تلفنی از فیلد «شماره پشتیبانی» خوانده میشود.',
|
'شناسه تماس در پیامرسان انتخابشده: یوزرنیم، آیدی یا لینک کامل. فقط روی آیکون پیامرسان و دکمه شناور اعمال میشود؛ تماس تلفنی از فیلد «شماره پشتیبانی» خوانده میشود.',
|
||||||
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:
|
||||||
'شماره کارت واریز — در متنها با ٪کارت یا %کارت جایگزین میشود.',
|
'شماره کارت واریز — در متنها با ٪کارت یا %کارت جایگزین میشود.',
|
||||||
|
deposit_account_number:
|
||||||
|
'شماره حساب بانکی واریز — در مودال راهنمای شارژ کنار کارت و شبا نمایش داده میشود. در متنها با ٪حساب یا %حساب جایگزین میشود.',
|
||||||
deposit_sheba:
|
deposit_sheba:
|
||||||
'شماره شبا واریز — در متنها با ٪شبا یا %شبا جایگزین میشود.',
|
'شماره شبا واریز — در متنها با ٪شبا یا %شبا جایگزین میشود.',
|
||||||
deposit_sms_support_number:
|
deposit_sms_support_number:
|
||||||
@@ -939,6 +948,8 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
|
|||||||
fee_conditional_order: 'کارمزد سفارش شرطی',
|
fee_conditional_order: 'کارمزد سفارش شرطی',
|
||||||
enable_conditional_order: 'سفارش شرطی',
|
enable_conditional_order: 'سفارش شرطی',
|
||||||
enable_product_funds: 'قلک محصول',
|
enable_product_funds: 'قلک محصول',
|
||||||
|
has_page_gift_card: 'کارت هدیه',
|
||||||
|
enable_customer_level_profit: 'سود چندسطحی معرف',
|
||||||
has_page_price_trigger: 'هشدار قیمت',
|
has_page_price_trigger: 'هشدار قیمت',
|
||||||
has_page_notification: 'اعلان داخل اپ',
|
has_page_notification: 'اعلان داخل اپ',
|
||||||
jibit_enabled: 'احراز هویت جیبیت',
|
jibit_enabled: 'احراز هویت جیبیت',
|
||||||
@@ -1016,6 +1027,7 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
|
|||||||
deposit_support_message_text: 'متن پیام به پشتیبانی (واریز)',
|
deposit_support_message_text: 'متن پیام به پشتیبانی (واریز)',
|
||||||
deposit_account_holder: 'نام صاحب حساب واریز',
|
deposit_account_holder: 'نام صاحب حساب واریز',
|
||||||
deposit_card_number: 'شماره کارت واریز',
|
deposit_card_number: 'شماره کارت واریز',
|
||||||
|
deposit_account_number: 'شماره حساب واریز',
|
||||||
deposit_sheba: 'شبا واریز',
|
deposit_sheba: 'شبا واریز',
|
||||||
deposit_sms_support_number: 'شماره تماس پشتیبانی',
|
deposit_sms_support_number: 'شماره تماس پشتیبانی',
|
||||||
price_channel_enabled: 'ارسال قیمت به کانال',
|
price_channel_enabled: 'ارسال قیمت به کانال',
|
||||||
@@ -1056,6 +1068,8 @@ export const SETTING_LOCKED_KEYS = new Set([
|
|||||||
'enable_product_funds',
|
'enable_product_funds',
|
||||||
'has_page_price_trigger',
|
'has_page_price_trigger',
|
||||||
'has_page_notification',
|
'has_page_notification',
|
||||||
|
'has_page_gift_card',
|
||||||
|
'enable_customer_level_profit',
|
||||||
'jibit_enabled',
|
'jibit_enabled',
|
||||||
'has_page_earn_money',
|
'has_page_earn_money',
|
||||||
'is_store',
|
'is_store',
|
||||||
@@ -1103,7 +1117,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
|
|||||||
'sms_notif_fund_withdraw_admin', 'sms_notif_fund_withdraw_customer',
|
'sms_notif_fund_withdraw_admin', 'sms_notif_fund_withdraw_customer',
|
||||||
'sms_notif_price_trigger', 'sms_notif_wallet_alert', 'sms_notif_support_admin',
|
'sms_notif_price_trigger', 'sms_notif_wallet_alert', 'sms_notif_support_admin',
|
||||||
'deposit_sms_support_number', 'deposit_support_message_text',
|
'deposit_sms_support_number', 'deposit_support_message_text',
|
||||||
'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
'deposit_account_holder', 'deposit_card_number', 'deposit_account_number', 'deposit_sheba',
|
||||||
'endDay',
|
'endDay',
|
||||||
'fee_free', 'fee_service_charge_percentage',
|
'fee_free', 'fee_service_charge_percentage',
|
||||||
'gift_for_referral', 'gift_for_referrer',
|
'gift_for_referral', 'gift_for_referrer',
|
||||||
@@ -1122,7 +1136,7 @@ export const SETTING_EDITABLE_KEYS = new Set([
|
|||||||
'startDay',
|
'startDay',
|
||||||
'support_messengers', 'support_messenger_text',
|
'support_messengers', 'support_messenger_text',
|
||||||
'has_chat',
|
'has_chat',
|
||||||
'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
|
'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_account_number', 'deposit_sheba',
|
||||||
'text_about', 'text_rule',
|
'text_about', 'text_rule',
|
||||||
'zaarbox_price_key',
|
'zaarbox_price_key',
|
||||||
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
|
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export const TEMPLATE_FIELD_CONFIGS: Record<string, TemplateFieldConfig> = {
|
|||||||
previewTitle: 'پیشنمایش راهنما',
|
previewTitle: 'پیشنمایش راهنما',
|
||||||
placeholder: 'متن راهنمای واریز در مودال شارژ حساب را بنویسید…',
|
placeholder: 'متن راهنمای واریز در مودال شارژ حساب را بنویسید…',
|
||||||
hint: 'روی هر متغیر کلیک کنید تا در محل نشانگر اضافه شود. اگر خالی بگذارید، متن پیشفرض اپ استفاده میشود.',
|
hint: 'روی هر متغیر کلیک کنید تا در محل نشانگر اضافه شود. اگر خالی بگذارید، متن پیشفرض اپ استفاده میشود.',
|
||||||
legend: 'مقادیر بالا نمونهاند؛ در اپ، نام/کارت/شبا از فیلدهای «نام صاحب حساب واریز»، «شماره کارت واریز» و «شبا واریز» پر میشوند.',
|
legend: 'مقادیر بالا نمونهاند؛ در اپ، نام/کارت/حساب/شبا از فیلدهای مربوطه پر میشوند.',
|
||||||
rows: 8,
|
rows: 8,
|
||||||
defaultTemplate: DEPOSIT_GUIDE_DEFAULT,
|
defaultTemplate: DEPOSIT_GUIDE_DEFAULT,
|
||||||
variableGroups: [
|
variableGroups: [
|
||||||
@@ -192,6 +192,7 @@ export const TEMPLATE_FIELD_CONFIGS: Record<string, TemplateFieldConfig> = {
|
|||||||
items: [
|
items: [
|
||||||
{ token: 'نام', label: 'صاحب حساب', help: 'نام صاحب حساب (از فیلد «نام صاحب حساب واریز»)' },
|
{ token: 'نام', label: 'صاحب حساب', help: 'نام صاحب حساب (از فیلد «نام صاحب حساب واریز»)' },
|
||||||
{ token: 'کارت', label: 'شماره کارت', help: 'شماره کارت (از فیلد «شماره کارت واریز»)' },
|
{ token: 'کارت', label: 'شماره کارت', help: 'شماره کارت (از فیلد «شماره کارت واریز»)' },
|
||||||
|
{ token: 'حساب', label: 'شماره حساب', help: 'شماره حساب (از فیلد «شماره حساب واریز»)' },
|
||||||
{ token: 'شبا', label: 'شماره شبا', help: 'شماره شبا (از فیلد «شبا واریز»)' },
|
{ token: 'شبا', label: 'شماره شبا', help: 'شماره شبا (از فیلد «شبا واریز»)' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -199,6 +200,7 @@ export const TEMPLATE_FIELD_CONFIGS: Record<string, TemplateFieldConfig> = {
|
|||||||
sampleValues: {
|
sampleValues: {
|
||||||
نام: 'علی رضایی',
|
نام: 'علی رضایی',
|
||||||
کارت: '۶۰۳۷-۹۹۷۹-۱۲۳۴-۵۶۷۸',
|
کارت: '۶۰۳۷-۹۹۷۹-۱۲۳۴-۵۶۷۸',
|
||||||
|
حساب: '۰۱۰۸۱۷۹۰۹۰۰۲',
|
||||||
شبا: 'IR۸۲۰۵۴۰۱۰۲۶۸۰۰۲۰۸۱۷۹۰۹۰۰۲',
|
شبا: 'IR۸۲۰۵۴۰۱۰۲۶۸۰۰۲۰۸۱۷۹۰۹۰۰۲',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<BasicModal
|
||||||
|
v-model:visible="visibleProxy"
|
||||||
|
:width="isMobile ? '94vw' : '32rem'"
|
||||||
|
title="سود چندسطحی معرف"
|
||||||
|
ok-text="ذخیره"
|
||||||
|
:loading="loading"
|
||||||
|
@ok="submit"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-3">
|
||||||
|
<p class="m-0 text-sm" style="color: var(--admin-text-secondary)">
|
||||||
|
مشتری:
|
||||||
|
<strong class="text-[var(--admin-text)]">{{ customer?.name || '—' }}</strong>
|
||||||
|
</p>
|
||||||
|
<p class="m-0 text-xs" style="color: var(--admin-text-secondary)">
|
||||||
|
درصد از کارمزد سفارش زیرمجموعهها. سطح ۱ یعنی معرف مستقیم، سطح ۲ معرفِ معرف، و همینطور الی آخر.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in levels"
|
||||||
|
:key="index"
|
||||||
|
class="flex flex-wrap items-end gap-2"
|
||||||
|
>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="item.level"
|
||||||
|
type="number"
|
||||||
|
class="min-w-28 flex-1"
|
||||||
|
:addon-after="Number(item.level) === 0 ? 'سطح (جدید)' : 'سطح'"
|
||||||
|
/>
|
||||||
|
<BasicInput
|
||||||
|
v-model:value="item.present"
|
||||||
|
type="number"
|
||||||
|
class="min-w-28 flex-1"
|
||||||
|
addon-after="درصد سود"
|
||||||
|
/>
|
||||||
|
<BaseButton size="small" type="default" danger @click="removeLevel(index)">
|
||||||
|
حذف
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<BaseButton type="dashed" class="!w-full" icon="vuesax-linear:add" @click="addLevel">
|
||||||
|
افزودن سطح
|
||||||
|
</BaseButton>
|
||||||
|
</div>
|
||||||
|
</BasicModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import BasicModal from '@/components/BasicModal.vue'
|
||||||
|
import BasicInput from '@/components/BasicInput.vue'
|
||||||
|
import { updateCustomer } from '@/api/customer'
|
||||||
|
import type { Customer, ProfitByLevel } from '@/model/customer'
|
||||||
|
import { isMobile } from '@core/utils/is'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean
|
||||||
|
customer: Customer | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:visible', value: boolean): void
|
||||||
|
(e: 'success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const levels = ref<ProfitByLevel[]>([])
|
||||||
|
|
||||||
|
const visibleProxy = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: (value: boolean) => emit('update:visible', value),
|
||||||
|
})
|
||||||
|
|
||||||
|
function normalizeLevels(raw: unknown): ProfitByLevel[] {
|
||||||
|
if (!Array.isArray(raw)) return []
|
||||||
|
return raw
|
||||||
|
.map((row) => {
|
||||||
|
if (Array.isArray(row)) {
|
||||||
|
return { level: Number(row[0] ?? 0), present: Number(row[1] ?? 0) }
|
||||||
|
}
|
||||||
|
const item = row as ProfitByLevel
|
||||||
|
return {
|
||||||
|
level: Number(item?.level ?? 0),
|
||||||
|
present: Number(item?.present ?? 0),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((row) => Number.isFinite(row.level) && Number.isFinite(row.present))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.visible, props.customer] as const,
|
||||||
|
([visible]) => {
|
||||||
|
if (!visible) return
|
||||||
|
const fromCustomer = normalizeLevels(props.customer?.profit_amount_by_lvl)
|
||||||
|
levels.value = fromCustomer.length
|
||||||
|
? cloneDeep(fromCustomer)
|
||||||
|
: [{ level: 1, present: 0 }]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
function addLevel() {
|
||||||
|
const nextLevel =
|
||||||
|
levels.value.reduce((max, row) => Math.max(max, Number(row.level) || 0), 0) + 1
|
||||||
|
levels.value.push({ level: nextLevel, present: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeLevel(index: number) {
|
||||||
|
levels.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
visibleProxy.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
if (!props.customer?.id) return
|
||||||
|
|
||||||
|
const payload = levels.value
|
||||||
|
.map((row) => ({
|
||||||
|
level: Number(row.level) || 0,
|
||||||
|
present: Number(row.present) || 0,
|
||||||
|
}))
|
||||||
|
.filter((row) => row.level > 0 && row.present > 0)
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
await updateCustomer(props.customer.id, {
|
||||||
|
profit_amount_by_lvl: payload,
|
||||||
|
} as Customer)
|
||||||
|
message.success('درصدهای سود سطح ذخیره شد.')
|
||||||
|
emit('success')
|
||||||
|
close()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
@confirmUser="forwardAndClose('confirmUser', $event)"
|
@confirmUser="forwardAndClose('confirmUser', $event)"
|
||||||
@sendSms="forwardAndClose('sendSms', $event)"
|
@sendSms="forwardAndClose('sendSms', $event)"
|
||||||
@editAccounting="forwardAndClose('editAccounting', $event)"
|
@editAccounting="forwardAndClose('editAccounting', $event)"
|
||||||
|
@levelProfit="forwardAndClose('levelProfit', $event)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ const emit = defineEmits<{
|
|||||||
(e: 'confirmUser', item: Customer): void
|
(e: 'confirmUser', item: Customer): void
|
||||||
(e: 'sendSms', item: Customer): void
|
(e: 'sendSms', item: Customer): void
|
||||||
(e: 'editAccounting', item: Customer): void
|
(e: 'editAccounting', item: Customer): void
|
||||||
|
(e: 'levelProfit', item: Customer): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Customer>()
|
const { sheetVisible, selectedItem, openSheet, syncSelectedItem } = useMobileDetailSheet<Customer>()
|
||||||
@@ -75,6 +77,7 @@ type SheetAction =
|
|||||||
| 'confirmUser'
|
| 'confirmUser'
|
||||||
| 'sendSms'
|
| 'sendSms'
|
||||||
| 'editAccounting'
|
| 'editAccounting'
|
||||||
|
| 'levelProfit'
|
||||||
|
|
||||||
function forward(action: SheetAction, item: Customer) {
|
function forward(action: SheetAction, item: Customer) {
|
||||||
emit(action, item)
|
emit(action, item)
|
||||||
|
|||||||
@@ -53,6 +53,15 @@
|
|||||||
<BaseButton size="small" type="default" icon="vuesax-linear:edit" @click="closeAndEmit('edit', row)">
|
<BaseButton size="small" type="default" icon="vuesax-linear:edit" @click="closeAndEmit('edit', row)">
|
||||||
ویرایش
|
ویرایش
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
<BaseButton
|
||||||
|
v-if="customerLevelProfitEnabled"
|
||||||
|
size="small"
|
||||||
|
type="default"
|
||||||
|
icon="vuesax-linear:hierarchy-2"
|
||||||
|
@click="closeAndEmit('levelProfit', row)"
|
||||||
|
>
|
||||||
|
سود چندسطحی
|
||||||
|
</BaseButton>
|
||||||
<BaseButton size="small" type="default" icon="vuesax-linear:archive-tick" @click="closeAndEmit('archive', row)">
|
<BaseButton size="small" type="default" icon="vuesax-linear:archive-tick" @click="closeAndEmit('archive', row)">
|
||||||
بایگانی
|
بایگانی
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@@ -121,6 +130,7 @@ import CustomerWalletBalancePanel from '@/components/customer/CustomerWalletBala
|
|||||||
import {
|
import {
|
||||||
accountingProviderSetting,
|
accountingProviderSetting,
|
||||||
customerDirectSmsEnabledSetting,
|
customerDirectSmsEnabledSetting,
|
||||||
|
customerLevelProfitEnabledSetting,
|
||||||
initAccountingSettingsFromApi
|
initAccountingSettingsFromApi
|
||||||
} from '@/utils/config'
|
} from '@/utils/config'
|
||||||
import { displayAccountingCode } from '@/utils/accountingCode'
|
import { displayAccountingCode } from '@/utils/accountingCode'
|
||||||
@@ -142,10 +152,12 @@ const emit = defineEmits<{
|
|||||||
(e: 'confirmUser', item: Customer): void
|
(e: 'confirmUser', item: Customer): void
|
||||||
(e: 'sendSms', item: Customer): void
|
(e: 'sendSms', item: Customer): void
|
||||||
(e: 'editAccounting', item: Customer): void
|
(e: 'editAccounting', item: Customer): void
|
||||||
|
(e: 'levelProfit', item: Customer): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const chargeTargets = ref<ChargeTarget[]>([])
|
const chargeTargets = ref<ChargeTarget[]>([])
|
||||||
const customerDirectSmsEnabled = customerDirectSmsEnabledSetting
|
const customerDirectSmsEnabled = customerDirectSmsEnabledSetting
|
||||||
|
const customerLevelProfitEnabled = customerLevelProfitEnabledSetting
|
||||||
|
|
||||||
const accountingCodeLabel = computed(() => {
|
const accountingCodeLabel = computed(() => {
|
||||||
const provider = accountingProviderSetting.value
|
const provider = accountingProviderSetting.value
|
||||||
|
|||||||
@@ -108,6 +108,15 @@
|
|||||||
<template #title><span>ویرایش</span></template>
|
<template #title><span>ویرایش</span></template>
|
||||||
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
<Icon icon="vuesax-linear:edit" color="#4572E9" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<a-tooltip
|
||||||
|
v-if="customerLevelProfitEnabled"
|
||||||
|
placement="bottom"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="openLevelProfit(record)"
|
||||||
|
>
|
||||||
|
<template #title><span>سود چندسطحی معرف</span></template>
|
||||||
|
<Icon icon="vuesax-linear:hierarchy-2" color="#ca8a04" />
|
||||||
|
</a-tooltip>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
:key="record.trade_active"
|
:key="record.trade_active"
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
@@ -157,11 +166,18 @@
|
|||||||
@confirmUser="openConfirmUser"
|
@confirmUser="openConfirmUser"
|
||||||
@sendSms="openDirectSms"
|
@sendSms="openDirectSms"
|
||||||
@editAccounting="openAccountingCodeEdit"
|
@editAccounting="openAccountingCodeEdit"
|
||||||
|
@levelProfit="openLevelProfit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CustomerDirectSmsModal v-model:visible="directSmsVisible" :customer="directSmsCustomer" />
|
<CustomerDirectSmsModal v-model:visible="directSmsVisible" :customer="directSmsCustomer" />
|
||||||
|
|
||||||
|
<CustomerLevelProfitModal
|
||||||
|
v-model:visible="levelProfitVisible"
|
||||||
|
:customer="levelProfitCustomer"
|
||||||
|
@success="fetch"
|
||||||
|
/>
|
||||||
|
|
||||||
<CustomerAccountingCodeModal
|
<CustomerAccountingCodeModal
|
||||||
v-model:visible="accountingCodeVisible"
|
v-model:visible="accountingCodeVisible"
|
||||||
:customer="accountingCodeCustomer"
|
:customer="accountingCodeCustomer"
|
||||||
@@ -234,6 +250,7 @@ import {
|
|||||||
import { buildCustomerColumns } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
import { buildCustomerColumns } from '@/pages/menuPages/customers/base/hooks/customerColumns'
|
||||||
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
import CustomerMobileCardList from '@/pages/menuPages/customers/base/components/CustomerMobileCardList.vue'
|
||||||
import CustomerDirectSmsModal from '@/pages/menuPages/customers/base/components/CustomerDirectSmsModal.vue'
|
import CustomerDirectSmsModal from '@/pages/menuPages/customers/base/components/CustomerDirectSmsModal.vue'
|
||||||
|
import CustomerLevelProfitModal from '@/pages/menuPages/customers/base/components/CustomerLevelProfitModal.vue'
|
||||||
import CustomerAccountingCodeModal from '@/pages/menuPages/customers/base/components/CustomerAccountingCodeModal.vue'
|
import CustomerAccountingCodeModal from '@/pages/menuPages/customers/base/components/CustomerAccountingCodeModal.vue'
|
||||||
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
@@ -251,6 +268,7 @@ import {
|
|||||||
pargarEnabledSetting,
|
pargarEnabledSetting,
|
||||||
accountingProviderSetting,
|
accountingProviderSetting,
|
||||||
customerDirectSmsEnabledSetting,
|
customerDirectSmsEnabledSetting,
|
||||||
|
customerLevelProfitEnabledSetting,
|
||||||
initAccountingSettingsFromApi,
|
initAccountingSettingsFromApi,
|
||||||
initWalletSettingsFromApi
|
initWalletSettingsFromApi
|
||||||
} from '@/utils/config'
|
} from '@/utils/config'
|
||||||
@@ -272,6 +290,9 @@ const pargarRegisterVisible = ref(false)
|
|||||||
const pargarCustomer = ref<Customer>()
|
const pargarCustomer = ref<Customer>()
|
||||||
const pargarEnabled = pargarEnabledSetting
|
const pargarEnabled = pargarEnabledSetting
|
||||||
const customerDirectSmsEnabled = customerDirectSmsEnabledSetting
|
const customerDirectSmsEnabled = customerDirectSmsEnabledSetting
|
||||||
|
const customerLevelProfitEnabled = customerLevelProfitEnabledSetting
|
||||||
|
const levelProfitVisible = ref(false)
|
||||||
|
const levelProfitCustomer = ref<Customer | null>(null)
|
||||||
const accountingCodeVisible = ref(false)
|
const accountingCodeVisible = ref(false)
|
||||||
const accountingCodeCustomer = ref<Customer | null>(null)
|
const accountingCodeCustomer = ref<Customer | null>(null)
|
||||||
const accountingEnabled = computed(() => {
|
const accountingEnabled = computed(() => {
|
||||||
@@ -329,6 +350,11 @@ function openDirectSms(user: Customer) {
|
|||||||
directSmsVisible.value = true
|
directSmsVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openLevelProfit(user: Customer) {
|
||||||
|
levelProfitCustomer.value = user
|
||||||
|
levelProfitVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
async function changeCustomerActivationFunction(id: number) {
|
async function changeCustomerActivationFunction(id: number) {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|||||||
@@ -1707,7 +1707,7 @@ export const bootstrapAdminChat = async (
|
|||||||
bootstrapPromise = (async () => {
|
bootstrapPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
void postAdminChatEnsureWebhook().catch((error) => {
|
void postAdminChatEnsureWebhook().catch((error) => {
|
||||||
console.error('admin chat ensure webhook failed:', error)
|
console.warn('admin chat ensure webhook failed:', error)
|
||||||
})
|
})
|
||||||
|
|
||||||
await fetchAdminRooms()
|
await fetchAdminRooms()
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ export const conditionalOrderEnabledSetting = ref(false)
|
|||||||
/** reactive — قلک per-product در اپ مشتری */
|
/** reactive — قلک per-product در اپ مشتری */
|
||||||
export const productFundsEnabledSetting = ref(false)
|
export const productFundsEnabledSetting = ref(false)
|
||||||
|
|
||||||
|
/** reactive — سود چندسطحی معرف در لیست مشتریان */
|
||||||
|
export const customerLevelProfitEnabledSetting = ref(false)
|
||||||
|
|
||||||
/** reactive — منو و UI هشدار قیمت */
|
/** reactive — منو و UI هشدار قیمت */
|
||||||
export const priceTriggerEnabledSetting = ref(false)
|
export const priceTriggerEnabledSetting = ref(false)
|
||||||
|
|
||||||
@@ -154,6 +157,10 @@ export function setProductFundsEnabledFromSettings(value: boolean): void {
|
|||||||
productFundsEnabledSetting.value = value
|
productFundsEnabledSetting.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setCustomerLevelProfitEnabledFromSettings(value: boolean): void {
|
||||||
|
customerLevelProfitEnabledSetting.value = value
|
||||||
|
}
|
||||||
|
|
||||||
export function setPriceTriggerEnabledFromSettings(value: boolean): void {
|
export function setPriceTriggerEnabledFromSettings(value: boolean): void {
|
||||||
priceTriggerEnabledSetting.value = value
|
priceTriggerEnabledSetting.value = value
|
||||||
}
|
}
|
||||||
@@ -257,6 +264,13 @@ export function applyProductFundsEnabledFromRows(
|
|||||||
if (row) setProductFundsEnabledFromSettings(isSwitchOn(row.value))
|
if (row) setProductFundsEnabledFromSettings(isSwitchOn(row.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyCustomerLevelProfitEnabledFromRows(
|
||||||
|
rows: Array<{ key?: string; value?: string | null }>
|
||||||
|
) {
|
||||||
|
const row = rows.find((item) => item.key === 'enable_customer_level_profit')
|
||||||
|
if (row) setCustomerLevelProfitEnabledFromSettings(isSwitchOn(row.value))
|
||||||
|
}
|
||||||
|
|
||||||
export function applyPriceTriggerEnabledFromRows(
|
export function applyPriceTriggerEnabledFromRows(
|
||||||
rows: Array<{ key?: string; value?: string | null }>
|
rows: Array<{ key?: string; value?: string | null }>
|
||||||
) {
|
) {
|
||||||
@@ -305,6 +319,7 @@ async function bootstrapSettingsFromApi(): Promise<void> {
|
|||||||
applyCustomerDirectSmsFromRows(list)
|
applyCustomerDirectSmsFromRows(list)
|
||||||
applyConditionalOrderEnabledFromRows(list)
|
applyConditionalOrderEnabledFromRows(list)
|
||||||
applyProductFundsEnabledFromRows(list)
|
applyProductFundsEnabledFromRows(list)
|
||||||
|
applyCustomerLevelProfitEnabledFromRows(list)
|
||||||
applyPriceTriggerEnabledFromRows(list)
|
applyPriceTriggerEnabledFromRows(list)
|
||||||
applyAppNotificationEnabledFromRows(list)
|
applyAppNotificationEnabledFromRows(list)
|
||||||
applySupportChatMenuEnabledFromRows(list)
|
applySupportChatMenuEnabledFromRows(list)
|
||||||
|
|||||||
Reference in New Issue
Block a user