diff --git a/src/pages/menuPages/configuration/SettingValueField.vue b/src/pages/menuPages/configuration/SettingValueField.vue
index bd2774d..cf831c7 100644
--- a/src/pages/menuPages/configuration/SettingValueField.vue
+++ b/src/pages/menuPages/configuration/SettingValueField.vue
@@ -38,6 +38,21 @@
+
+
+ {{ opt.value }}
+
+
+
(() =>
+ String(props.modelValue || '')
+ .split(',')
+ .map((s) => s.trim())
+ .filter((s) => s !== '')
+)
+
+function onMultiSelectChange(val: string[] | undefined) {
+ const list = Array.isArray(val) ? val.map((s) => s.trim()).filter((s) => s !== '') : []
+ emit('update:value', list.join(','))
+}
+
function onTimeChange(val: string | string[] | undefined) {
const v = Array.isArray(val) ? val[0] : val
emit('update:value', v ?? '')
diff --git a/src/pages/menuPages/configuration/settingGroups.ts b/src/pages/menuPages/configuration/settingGroups.ts
index 9f92503..7103137 100644
--- a/src/pages/menuPages/configuration/settingGroups.ts
+++ b/src/pages/menuPages/configuration/settingGroups.ts
@@ -76,7 +76,13 @@ export const EDITABLE_SETTING_GROUPS: SettingGroupDef[] = [
id: 'bots',
title: 'باتها',
icon: 'vuesax-linear:messages-2',
- match: (k) => /^(telegram_bot|bale_bot)/.test(k)
+ match: (k) => /^(telegram_bot|bale_bot|rubika_bot)/.test(k)
+ },
+ {
+ id: 'price_channel',
+ title: 'ارسال قیمت به کانال',
+ icon: 'vuesax-linear:send-2',
+ match: (k) => /^price_channel_/.test(k) || k === 'change_mesghal'
},
{
id: 'notifications',
@@ -152,6 +158,12 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|| k === 'send_to_tahesab'
|| k === 'send_to_pargar'
},
+ {
+ id: 'price_channel',
+ title: 'ارسال قیمت به کانال',
+ icon: 'vuesax-linear:send-2',
+ match: (k) => /^price_channel_/.test(k) || /^(telegram_bot|bale_bot|rubika_bot)/.test(k) || k === 'change_mesghal'
+ },
{
id: 'infra',
title: 'زیرساخت',
diff --git a/src/pages/menuPages/configuration/settingTypes.ts b/src/pages/menuPages/configuration/settingTypes.ts
index 4597080..fe9c48b 100644
--- a/src/pages/menuPages/configuration/settingTypes.ts
+++ b/src/pages/menuPages/configuration/settingTypes.ts
@@ -1,7 +1,15 @@
import type { Const } from '@/model/configuration'
import { isStoreApp } from '@/utils/config'
-export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select' | 'longtext' | 'time'
+export type SettingFieldType =
+ | 'text'
+ | 'number'
+ | 'price'
+ | 'switch'
+ | 'select'
+ | 'multiselect'
+ | 'longtext'
+ | 'time'
export const MAIN_WALLET_PRODUCTS_DEFAULT = '13'
@@ -78,6 +86,7 @@ export const SETTING_TYPE_OPTIONS = [
{ key: 'price', value: 'مبلغ (ریال)' },
{ key: 'switch', value: 'روشن/خاموش' },
{ key: 'select', value: 'انتخابی' },
+ { key: 'multiselect', value: 'انتخاب چندتایی' },
{ key: 'longtext', value: 'متن بلند' },
{ key: 'time', value: 'ساعت' }
]
@@ -145,7 +154,19 @@ const KEY_TYPE_FALLBACK: Record = {
invite_link_text: 'longtext',
support_messenger: 'select',
deposit_sms_support_number: 'number',
- zaarbox_price_key: 'select'
+ zaarbox_price_key: 'select',
+ price_channel_enabled: 'switch',
+ price_channel_messengers: 'multiselect',
+ price_channel_diff: 'price',
+ price_channel_min_seconds: 'number',
+ price_channel_footer_id: 'text',
+ price_channel_footer_phone: 'text',
+ price_channel_footer_link: 'text',
+ price_channel_bale_to: 'text',
+ price_channel_telegram_to: 'text',
+ price_channel_rubika_to: 'text',
+ rubika_bot_token: 'text',
+ change_mesghal: 'number'
}
const SELECT_LABELS_BY_KEY: Record> = {
@@ -161,6 +182,11 @@ const SELECT_LABELS_BY_KEY: Record> = {
rubika: 'روبیکا',
eitaa: 'ایتا',
soroush: 'سروش'
+ },
+ price_channel_messengers: {
+ bale: 'بله',
+ telegram: 'تلگرام',
+ rubika: 'روبیکا'
}
}
@@ -169,7 +195,8 @@ const SELECT_OPTIONS_BY_KEY: Record = {
price_provider: 'montzar,zaarbox,main_product',
payping_gateway_name: 'sepal,',
unit: 'گرم,کیلوگرم',
- support_messenger: 'bale,telegram,whatsapp,rubika,eitaa,soroush'
+ support_messenger: 'bale,telegram,whatsapp,rubika,eitaa,soroush',
+ price_channel_messengers: 'bale,telegram,rubika'
}
/** سوییچهایی که مقدار required/خالی دارند (نه true/false) */
@@ -184,13 +211,14 @@ export function referralRequiredToString(checked: boolean): string {
}
export function resolveSettingFieldType(row: Pick): SettingFieldType {
- const raw = (row.type || '').trim().toLowerCase()
- if (['text', 'number', 'price', 'switch', 'select', 'longtext', 'time'].includes(raw)) {
- return raw as SettingFieldType
- }
+ // کاتالوگ پنل بر نوعِ دیتابیس اولویت دارد (همراستا با عنوان/توضیح/can_edit)
if (row.key && KEY_TYPE_FALLBACK[row.key]) {
return KEY_TYPE_FALLBACK[row.key]
}
+ const raw = (row.type || '').trim().toLowerCase()
+ if (['text', 'number', 'price', 'switch', 'select', 'multiselect', 'longtext', 'time'].includes(raw)) {
+ return raw as SettingFieldType
+ }
return 'text'
}
@@ -368,7 +396,7 @@ const KEY_HELP_FALLBACK: Record = {
support_messenger_text:
'متن راهنمای پشتیبانی در مودال شارژ حساب (واریز).',
deposit_sms_support_number:
- 'شماره پشتیبانی؛ با کلیک روی «تماس با پشتیبانی» در اپ، تماس تلفنی برقرار میشود. در صفحه واریز هم برای دکمه پیامک با متن شماره حساب استفاده میشود.',
+ 'شماره تماس پشتیبانی؛ با کلیک روی «تماس با پشتیبانی» در اپ، تماس تلفنی برقرار میشود. در صفحه واریز، درباره ما و قوانین هم نمایش داده میشود.',
charge_notif:
'روشن: پس از شارژ کیف توسط ادمین، پیامک به مشتری ارسال میشود.',
group_sms:
@@ -440,7 +468,31 @@ const KEY_HELP_FALLBACK: Record = {
admin_url:
'آدرس کامل پنل مدیریت (با https).',
main_product_price:
- 'تنظیم قدیمی — از «منبع قیمت» استفاده کنید. روشن نگه داشتن توصیه نمیشود.'
+ 'تنظیم قدیمی — از «منبع قیمت» استفاده کنید. روشن نگه داشتن توصیه نمیشود.',
+ price_channel_enabled:
+ 'روشن: بعد از هر تغییر قیمت، مظنه بهصورت خودکار به کانالهای پیامرسان انتخابشده ارسال میشود.',
+ price_channel_messengers:
+ 'کدام پیامرسانها قیمت را دریافت کنند؟ میتوانید چند مورد را انتخاب کنید (بله، تلگرام، روبیکا). برای هر پیامرسان باید توکن بات و «مقصد ارسال» هم پر شده باشد.',
+ price_channel_diff:
+ 'اسپرد اضافه فقط برای کانال (ریال): به قیمت خرید کانال اضافه و از قیمت فروش کم میشود. صفر یعنی همان قیمت اپ.',
+ price_channel_min_seconds:
+ 'حداقل فاصله (ثانیه) بین دو ارسال متوالی به کانال. جلوی سیل پیام هنگام نوسان قیمت را میگیرد. صفر = هر تغییر قیمت ارسال شود.',
+ price_channel_footer_id:
+ 'اگر پر باشد، در انتهای پیام کانال نمایش داده میشود (مثلاً یوزرنیم کانال). خالی = این خط نمایش داده نمیشود.',
+ price_channel_footer_phone:
+ 'شماره تماسی که در انتهای پیام کانال نشان داده میشود. خالی = نمایش داده نمیشود.',
+ price_channel_footer_link:
+ 'لینکی که در انتهای پیام کانال نشان داده میشود. خالی = نمایش داده نمیشود.',
+ price_channel_bale_to:
+ 'آیدی چنل یا گروه بله که قیمت برایش ارسال میشود. چند مقصد را با ویرگول جدا کنید.',
+ price_channel_telegram_to:
+ 'آیدی چنل یا گروه تلگرام که قیمت برایش ارسال میشود. چند مقصد را با ویرگول جدا کنید.',
+ price_channel_rubika_to:
+ 'آیدی چنل یا گروه روبیکا که قیمت برایش ارسال میشود. چند مقصد را با ویرگول جدا کنید.',
+ rubika_bot_token:
+ 'توکن ربات روبیکا برای ارسال قیمت به کانال. محرمانه نگه دارید.',
+ change_mesghal:
+ 'وزن هر مثقال به گرم؛ برای نمایش خط «هر مثقال» در پیام کانال (مثقال = قیمت هر گرم × این عدد، پیشفرض ۴٫۳۳۱۸).'
}
/** برچسب فارسی — وقتی در کاتالوگ تعریف شده، بر title_fa دیتابیس اولویت دارد */
@@ -478,7 +530,19 @@ export const KEY_LABEL_FALLBACK: Record = {
support_messenger: 'پیامرسان پشتیبانی',
support_messenger_id: 'شناسه پشتیبانی',
support_messenger_text: 'متن راهنمای واریز',
- deposit_sms_support_number: 'شماره پشتیبانی'
+ deposit_sms_support_number: 'شماره تماس پشتیبانی',
+ price_channel_enabled: 'ارسال قیمت به کانال',
+ price_channel_messengers: 'پیامرسانهای ارسال قیمت',
+ price_channel_diff: 'اختلاف قیمت کانال',
+ price_channel_min_seconds: 'حداقل فاصله ارسال (ثانیه)',
+ price_channel_footer_id: 'آیدی کانال در پیام',
+ price_channel_footer_phone: 'شماره تماس در پیام',
+ price_channel_footer_link: 'لینک در پیام',
+ price_channel_bale_to: 'مقصد ارسال بله',
+ price_channel_telegram_to: 'مقصد ارسال تلگرام',
+ price_channel_rubika_to: 'مقصد ارسال روبیکا',
+ rubika_bot_token: 'توکن بات روبیکا',
+ change_mesghal: 'وزن مثقال (گرم)'
}
/** غیرقابل ویرایش در پنل — همتراز deploy_settings_const.sql */
@@ -507,7 +571,8 @@ export const SETTING_LOCKED_KEYS = new Set([
'sse_path',
'tahesab_bank_account', 'tahesab_db', 'tahesab_host', 'tahesab_port', 'tahesab_token',
'pargar_api_token', 'pargar_password', 'pargar_username',
- 'telegram_bot_token',
+ 'telegram_bot_token', 'rubika_bot_token',
+ 'price_channel_bale_to', 'price_channel_telegram_to', 'price_channel_rubika_to',
'unit',
'up_price', 'down_price',
'wss_enabled',
@@ -534,7 +599,10 @@ export const SETTING_EDITABLE_KEYS = new Set([
'sell_fee', 'show_last_price', 'startDay',
'support_messenger', 'support_messenger_id', 'support_messenger_text',
'text_about', 'text_rule',
- 'zaarbox_price_key'
+ 'zaarbox_price_key',
+ 'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
+ 'price_channel_min_seconds', 'price_channel_footer_id', 'price_channel_footer_phone',
+ 'price_channel_footer_link', 'change_mesghal'
])
/** عنوان فارسی — کاتالوگ پنل بر title_fa دیتابیس اولویت دارد */
diff --git a/src/utils/adminHelpCopy.ts b/src/utils/adminHelpCopy.ts
index cd179e1..d072ebf 100644
--- a/src/utils/adminHelpCopy.ts
+++ b/src/utils/adminHelpCopy.ts
@@ -19,7 +19,7 @@ export const bannersEventHelp =
'وقتی مشتری روی بنر ضربه میزند چه اتفاقی بیفتد؟ اگر مطمئن نیستید «بدون رویداد» را بگذارید.'
export const bannersLinkHelp =
- 'آدرس کامل صفحهای که باید باز شود. مثال: https://bazretala.ir یا tel:02112345678'
+ 'آدرس کامل صفحهای که باید باز شود. مثال: https://example.com یا tel:02112345678'
export const bannersModalHelp =
'متنی که داخل خود اپ به مشتری نشان داده میشود. میتوانید بولد، لیست و لینک هم بگذارید.'