Load products from /admin/montzar/products with searchable select UI. Co-authored-by: Cursor <cursoragent@cursor.com>
272 lines
7.4 KiB
Vue
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>
|