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 = 'متنی که داخل خود اپ به مشتری نشان داده می‌شود. می‌توانید بولد، لیست و لینک هم بگذارید.'