This commit is contained in:
2026-07-02 00:30:42 +03:30
parent 3bf249377f
commit 48a45bf5bb
3 changed files with 120 additions and 15 deletions
@@ -41,7 +41,7 @@
import SettingValueField from './SettingValueField.vue'
import {
helpText,
KEY_LABEL_FALLBACK,
resolveSettingTitle,
resolveSettingFieldType,
type SettingFieldType
} from './settingTypes'
@@ -63,9 +63,7 @@ const emit = defineEmits<{
const settingKey = computed(() => props.row.key ?? '')
const settingType = computed(() => props.row.type)
const description = computed(() => props.row.description)
const title = computed(
() => props.row.title_fa || (props.row.key && KEY_LABEL_FALLBACK[props.row.key]) || props.row.key || '—'
)
const title = computed(() => resolveSettingTitle(props.row))
const fieldType = computed<SettingFieldType>(() =>
resolveSettingFieldType({ key: props.row.key, type: props.row.type })
+3 -1
View File
@@ -188,6 +188,7 @@ import {
isSettingLocked,
isSettingActive,
isSwitchOn,
normalizeSettingRow,
resolveSettingFieldType
} from './settingTypes'
@@ -416,10 +417,11 @@ watch(
async function initConfig() {
try {
loading.value = true
baseData.value =
const rows =
(isSystemMode.value
? await getSystemConfiguration()
: await getEditableConfiguration()) ?? []
baseData.value = rows.map(normalizeSettingRow)
if (isSystemMode.value) {
applyBannerEnabledFromRows(baseData.value)
} else {
+115 -10
View File
@@ -262,7 +262,7 @@ export function switchLabels(key: string | undefined): { on: string; off: string
return { on: 'فعال', off: 'غیرفعال' }
}
/** توضیحات ساده و فارسی برای پنل — بر توضیح دیتابیس (که ممکن است نام فنی انگلیسی داشته باشد) اولویت دارد */
/** توضیحات fallback — وقتی در کاتالوگ تعریف شده، بر description دیتابیس اولویت دارد */
const KEY_HELP_FALLBACK: Record<string, string> = {
name_fa:
'نام فارسی برند که در پیامک‌ها، عنوان صفحات و بخش‌های عمومی اپ به مشتری نشان داده می‌شود.',
@@ -287,7 +287,7 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
fee_service_charge_percentage:
'درصد کارمزد هر معامله از مبلغ سفارش (اعشاری؛ مثلاً ۰٫۰۰۵ یعنی نیم در هزار).',
fee_deduct_from_gold:
'روشن: کارمزد از مقدار طلا کسر می‌شود — در فروش مبلغ ریالی دقیق (مثلاً ۱۰۰ میلیون) واریز می‌شود و کارمزد از طلای کیف کم می‌شود. خاموش (پیش‌فرض): کارمزد از ریال فروش کم می‌شود — رفتار قبلی.',
'روشن: مشتری دقیقاً همان مبلغ یا وزنی را که وارد کرده دریافت می‌کند. در فروش، مثلاً ۱۰۰ میلیون ریال درخواست → همان ۱۰۰ میلیون واریز و کارمزد از طلای کیف کسر می‌شود. در خرید، مثلاً ۱۰ گرم درخواست → همان ۱۰ گرم واریز و کارمزد به ریال قابل پرداخت اضافه می‌شود. خاموش (پیش‌فرض): در فروش کارمزد از مبلغ ریالی واریزی کم می‌شود (مشتری مبلغ کمتری می‌گیرد). در خرید کارمزد از وزن طلای واریزی کم می‌شود (مشتری طلای کمتری می‌گیرد).',
fee_free:
'تعداد اولین سفارش‌های هر کاربر که از کارمزد معاف می‌شوند. صفر یعنی غیرفعال.',
min_withdraw:
@@ -313,7 +313,7 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
register_referral_bonus_message:
'روشن: متن پاداش معرف در پیامک خوش‌آمد ثبت‌نام گنجانده می‌شود.',
referral_gift_on_first_order:
'روشن: پاداش معرف و معرف‌شونده پس از اولین سفارش واریز می‌شود. خاموش: واریز هنگام ثبت‌نام/تأیید احراز (رفتار قبلی).',
'روشن: پاداش معرف و معرف‌شونده پس از تکمیل اولین سفارش معرف‌شونده به کیف واریز می‌شود. خاموش: پاداش بلافاصله پس از ثبت‌نام موفق یا تأیید احراز هویت واریز می‌شود.',
referral_limit:
'حداکثر تعداد دعوت مجاز برای هر کاربر. خالی = بدون محدودیت.',
referral_bonus_amount:
@@ -418,7 +418,7 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
'تنظیم قدیمی — از «منبع قیمت» استفاده کنید. روشن نگه داشتن توصیه نمی‌شود.'
}
/** برچسب فارسی ساده — اگر سرور برچسبی نفرستد یا برچسب فنی باشد */
/** برچسب فارسی — وقتی در کاتالوگ تعریف شده، بر title_fa دیتابیس اولویت دارد */
export const KEY_LABEL_FALLBACK: Record<string, string> = {
close_price: 'بستن خودکار خارج از ساعت کاری',
startDay: 'ساعت شروع',
@@ -428,7 +428,7 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
buy_fee: 'اضافه قیمت خرید',
sell_fee: 'کسر قیمت فروش',
fee_service_charge_percentage: 'درصد کارمزد معامله',
fee_deduct_from_gold: 'کارمزد از مقدار طلا',
fee_deduct_from_gold: 'اعمال مبلغ دقیق در معامله مشتری',
fee_free: 'معافیت کارمزد سفارش اول',
min_withdraw: 'حداقل مبلغ برداشت',
min_first_buy: 'حداقل اولین خرید',
@@ -451,20 +451,125 @@ export const KEY_LABEL_FALLBACK: Record<string, string> = {
deposit_sms_support_number: 'شماره پشتیبانی واریز'
}
/** غیرقابل ویرایش در پنل — هم‌تراز deploy_settings_const.sql */
export const SETTING_LOCKED_KEYS = new Set([
'admin_url', 'apk_download_link', 'app_url',
'bale_bot_token', 'banner_enabled',
'chart_id', 'chart_use_local_data',
'external_order_api_enabled',
'group_sms',
'has_page_earn_money',
'is_store',
'logo_login', 'logo_register', 'title_pwa', 'title_fav',
'main_product', 'main_wallet_products', 'metal_type', 'main_metal',
'montify_name',
'montzar_base_url', 'montzar_closed',
'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh',
'montzar_order_response', 'montzar_token',
'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh',
'node_client_id', 'node_shared_secret',
'payping_fin_customer_id', 'payping_fin_sync_enabled', 'payping_fin_token', 'payping_fin_url',
'payping_gateway_items', 'payping_gateway_name',
'payping_round_toman', 'payping_start_url', 'payping_strict_failed_withdraw', 'payping_verify_url',
'price_extreme_date', 'price_provider', 'profit_gold_api_enabled',
'send_to_tahesab', 'service_charge_percentage',
'sse_path',
'tahesab_bank_account', 'tahesab_db', 'tahesab_host', 'tahesab_port', 'tahesab_token',
'telegram_bot_token',
'unit',
'up_price', 'down_price',
'wss_enabled',
'zaarbox_api_url', 'zaarbox_price_key'
])
/** قابل ویرایش در پنل — هم‌تراز deploy_settings_const.sql */
export const SETTING_EDITABLE_KEYS = new Set([
'name_fa',
'auto_active_customer', 'balance_report_exclude_customer_id',
'buy_fee', 'charge_notif', 'close_price',
'deposit_sms_support_number', 'endDay',
'fee_deduct_from_gold', 'fee_free', 'fee_service_charge_percentage', 'fee_single',
'gift_for_referral', 'gift_for_referrer',
'invite_link_text',
'jibit_verification',
'main_product_price',
'min_first_buy', 'min_withdraw', 'minimum_sell',
'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell',
'montzar_password', 'montzar_price', 'montzar_product_id',
'open_register',
'referral_bonus_amount', 'referral_id', 'referral_limit', 'referral_gift_on_first_order',
'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer',
'sell_fee', 'show_last_price', 'startDay',
'support_messenger', 'support_messenger_id', 'support_messenger_text',
'text_about', 'text_rule'
])
/** عنوان فارسی — کاتالوگ پنل بر title_fa دیتابیس اولویت دارد */
export function resolveSettingTitle(row: Pick<Const, 'key' | 'title_fa'>): string {
const key = (row.key ?? '').trim()
if (key && KEY_LABEL_FALLBACK[key]) return KEY_LABEL_FALLBACK[key]
return row.title_fa?.trim() || key || '—'
}
/** توضیح — کاتالوگ پنل بر description دیتابیس اولویت دارد */
export function resolveSettingDescription(row: Pick<Const, 'key' | 'description' | 'type'>): string {
const key = (row.key ?? '').trim()
const help = key ? KEY_HELP_FALLBACK[key] : undefined
if (help) {
const fieldType = resolveSettingFieldType(row)
if (fieldType === 'select') {
if (SELECT_OPTIONS_BY_KEY[key]) return `${help}|${SELECT_OPTIONS_BY_KEY[key]}`
const dbDesc = (row.description ?? '').trim()
if (dbDesc.includes('|')) {
const options = dbDesc.split('|').pop()?.trim()
if (options) return `${help}|${options}`
}
}
return help
}
return row.description ?? ''
}
/** can_edit — کاتالوگ پنل بر مقدار دیتابیس اولویت دارد */
export function resolveSettingCanEdit(row: Pick<Const, 'key' | 'can_edit'>): Const['can_edit'] {
const key = (row.key ?? '').trim()
if (SETTING_LOCKED_KEYS.has(key)) return 0
if (SETTING_EDITABLE_KEYS.has(key)) return 1
return row.can_edit ?? 1
}
/** اعمال override متادیتا (عنوان، توضیح، نوع، can_edit) روی ردیف API */
export function normalizeSettingRow(row: Const): Const {
const key = (row.key ?? '').trim()
if (!key) return row
const withType = {
...row,
type: row.type || KEY_TYPE_FALLBACK[key] || row.type
}
return {
...withType,
title_fa: resolveSettingTitle(withType),
description: resolveSettingDescription(withType),
can_edit: resolveSettingCanEdit(withType),
type: resolveSettingFieldType(withType)
}
}
export function helpText(row: Const): string {
const key = (row.key ?? '').trim()
if (key && KEY_HELP_FALLBACK[key]) {
return KEY_HELP_FALLBACK[key]
}
const desc = (row.description || '').trim()
if (!desc) {
return ''
}
const desc = resolveSettingDescription(row).trim()
if (!desc) return ''
if (resolveSettingFieldType(row) === 'select' && desc.includes('|')) {
return desc.split('|')[0]?.trim() ?? ''
}
if (resolveSettingFieldType(row) === 'select' && desc.includes(',')) {
if (resolveSettingFieldType(row) === 'select' && desc.includes(',') && !/[روشن|خاموش]/.test(desc)) {
return ''
}
return desc