Files
boursecu_admin/src/pages/menuPages/configuration/SettingValueField.vue
T
mohmdsdgandCursor 46c397804f feat(settings): montzar_product_id as select from partner product API
Load products from /admin/montzar/products with searchable select UI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 14:02:48 +03:30

272 lines
7.4 KiB
Vue

<template>
<div class="setting-value-field">
<SettingToggle
v-if="fieldType === 'switch'"
:checked="switchChecked"
:labels="labels"
:disabled="disabled"
@change="onSwitchChange"
/>
<SupportMessengersField
v-else-if="isSupportMessengers"
:model-value="modelValue"
:disabled="disabled"
@update:value="emit('update:value', $event)"
/>
<PartnerServerUrlsField
v-else-if="isPartnerServerUrls"
:model-value="modelValue"
:disabled="disabled"
@update:value="emit('update:value', $event)"
/>
<MainWalletProductsField
v-else-if="isMainWalletProducts"
:model-value="modelValue"
:disabled="disabled"
@update:value="emit('update:value', $event)"
/>
<ZaarboxPriceKeyField
v-else-if="isZaarboxPriceKey"
:model-value="modelValue"
:disabled="disabled"
@update:value="emit('update:value', $event)"
/>
<MontzarProductIdField
v-else-if="isMontzarProductId"
:model-value="modelValue"
:disabled="disabled"
@update:value="emit('update:value', $event)"
/>
<TemplateTextField
v-else-if="isTemplateField"
:model-value="modelValue"
:setting-key="settingKey || ''"
:disabled="disabled"
@update:value="emit('update:value', $event)"
@blur="emit('blur')"
/>
<a-select
v-else-if="fieldType === 'select'"
:value="modelValue || undefined"
:disabled="disabled"
allow-clear
placeholder="انتخاب کنید"
class="w-full"
@update:value="onSelectChange"
>
<a-select-option v-for="opt in selectItems" :key="opt.key" :value="opt.key">
{{ opt.value }}
</a-select-option>
</a-select>
<a-select
v-else-if="fieldType === 'multiselect'"
:value="multiSelectValue"
:disabled="disabled"
mode="multiple"
allow-clear
placeholder="انتخاب کنید"
class="w-full"
@update:value="onMultiSelectChange"
>
<a-select-option v-for="opt in selectItems" :key="opt.key" :value="opt.key">
{{ opt.value }}
</a-select-option>
</a-select>
<a-time-picker
v-else-if="fieldType === 'time'"
:value="timePickerValue"
:disabled="disabled"
class="w-full"
format="HH:mm"
:minute-step="5"
placeholder="انتخاب ساعت"
@update:value="onTimeChange"
/>
<a-input-number
v-else-if="fieldType === 'price'"
:value="priceValue"
:disabled="disabled"
class="w-full"
:formatter="(v: string) => `${v}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="(v: string) => sanitizeNumericString(v, { allowDecimal: false })"
@update:value="onPriceChange"
/>
<a-input
v-else-if="fieldType === 'number'"
:value="modelValue"
:disabled="disabled"
inputmode="numeric"
allow-clear
class="w-full"
@update:value="onPlainNumberChange"
@blur="emit('blur')"
/>
<a-textarea
v-else-if="fieldType === 'longtext'"
:value="modelValue"
:disabled="disabled"
:rows="rows"
class="w-full font-mono text-sm"
@update:value="emit('update:value', $event)"
@blur="emit('blur')"
/>
<a-input
v-else
:value="modelValue"
:disabled="disabled"
allow-clear
@update:value="emit('update:value', $event)"
@blur="emit('blur')"
/>
</div>
</template>
<script setup lang="ts">
import MontzarProductIdField from './MontzarProductIdField.vue'
import MainWalletProductsField from './MainWalletProductsField.vue'
import PartnerServerUrlsField from './PartnerServerUrlsField.vue'
import SettingToggle from './SettingToggle.vue'
import SupportMessengersField from './SupportMessengersField.vue'
import TemplateTextField from './TemplateTextField.vue'
import ZaarboxPriceKeyField from './ZaarboxPriceKeyField.vue'
import { hasTemplateField } from './templateFields'
import {
normalizeTimeValue,
parseSelectOptions,
resolveSettingFieldType,
resolveSwitchChecked,
switchLabels,
switchValueFromChecked,
type SettingFieldType
} from './settingTypes'
import { parseNumericValue, sanitizeNumericString } from '@core/utils/convert'
import dayjs, { type Dayjs } from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
dayjs.extend(customParseFormat)
const props = withDefaults(
defineProps<{
modelValue: string
settingKey?: string
settingType?: string
description?: string
disabled?: boolean
rows?: number
}>(),
{ disabled: false, rows: 10 }
)
const emit = defineEmits<{
'update:value': [value: string]
blur: []
}>()
const fieldType = computed<SettingFieldType>(() =>
resolveSettingFieldType({
key: props.settingKey,
type: props.settingType
})
)
const isZaarboxPriceKey = computed(() => props.settingKey === 'zaarbox_price_key')
const isMontzarProductId = computed(() => props.settingKey === 'montzar_product_id')
const isMainWalletProducts = computed(() => props.settingKey === 'main_wallet_products')
const isSupportMessengers = computed(() => props.settingKey === 'support_messengers')
const isPartnerServerUrls = computed(() => props.settingKey === 'zaarbox_api_url')
const isTemplateField = computed(() => hasTemplateField(props.settingKey))
const selectItems = computed(() =>
parseSelectOptions({
key: props.settingKey,
description: props.description,
type: props.settingType
})
)
const labels = computed(() => switchLabels(props.settingKey))
const switchChecked = computed(() => resolveSwitchChecked(props.settingKey, props.modelValue))
function onSwitchChange(checked: boolean) {
emit('update:value', switchValueFromChecked(props.settingKey, checked))
}
function onSelectChange(val: string | undefined) {
emit('update:value', val ?? '')
}
const multiSelectValue = computed<string[]>(() =>
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 parseTimePickerValue(raw?: string | null): Dayjs | undefined {
const normalized = normalizeTimeValue(raw)
if (!normalized) return undefined
const parsed = dayjs(normalized, 'HH:mm:ss', true)
return parsed.isValid() ? parsed : undefined
}
const timePickerValue = computed(() => parseTimePickerValue(props.modelValue))
function onTimeChange(val: Dayjs | string | null | undefined) {
if (val == null || val === '') {
emit('update:value', '')
emit('blur')
return
}
const parsed = dayjs.isDayjs(val) ? val : dayjs(String(val), 'HH:mm:ss', true)
emit('update:value', parsed.isValid() ? normalizeTimeValue(parsed.format('HH:mm:ss')) : '')
emit('blur')
}
const priceValue = computed(() => {
const n = parseNumericValue(props.modelValue || '')
return Number.isFinite(n) ? n : undefined
})
function onPriceChange(val: number | string | null) {
if (val === null || val === undefined || val === '') {
emit('update:value', '')
return
}
emit('update:value', sanitizeNumericString(val, { allowDecimal: false }))
}
function onPlainNumberChange(val: string) {
emit('update:value', sanitizeNumericString(val ?? '', { allowDecimal: true }))
}
</script>
<style scoped>
.setting-value-field {
width: 100%;
}
.setting-value-field :deep(.ant-input-number) {
width: 100%;
}
</style>