fixed ui and some bugs

This commit is contained in:
2026-06-28 21:48:17 +03:30
parent 34be7a66dc
commit 218d3e9f5e
25 changed files with 1374 additions and 371 deletions
@@ -39,7 +39,12 @@
<script setup lang="ts">
import SettingValueField from './SettingValueField.vue'
import { helpText, resolveSettingFieldType, type SettingFieldType } from './settingTypes'
import {
helpText,
KEY_LABEL_FALLBACK,
resolveSettingFieldType,
type SettingFieldType
} from './settingTypes'
import type { Const } from '@/model/configuration'
const props = defineProps<{
@@ -58,7 +63,9 @@ 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 || '—')
const title = computed(
() => props.row.title_fa || (props.row.key && KEY_LABEL_FALLBACK[props.row.key]) || props.row.key || '—'
)
const fieldType = computed<SettingFieldType>(() =>
resolveSettingFieldType({ key: props.row.key, type: props.row.type })
@@ -31,6 +31,18 @@
</a-select-option>
</a-select>
<a-time-picker
v-else-if="fieldType === 'time'"
:value="modelValue || undefined"
:disabled="disabled"
class="w-full"
format="HH:mm"
value-format="HH:mm:ss"
:minute-step="5"
placeholder="انتخاب ساعت"
@update:value="onTimeChange"
/>
<a-input-number
v-else-if="fieldType === 'price'"
:value="priceValue"
@@ -130,6 +142,12 @@ function onSelectChange(val: string | undefined) {
emit('update:value', val ?? '')
}
function onTimeChange(val: string | string[] | undefined) {
const v = Array.isArray(val) ? val[0] : val
emit('update:value', v ?? '')
emit('blur')
}
const priceValue = computed(() => {
const n = parseFloat(String(props.modelValue || '').replace(/,/g, ''))
return Number.isFinite(n) ? n : undefined
@@ -16,12 +16,18 @@ export const EDITABLE_SETTING_GROUPS: SettingGroupDef[] = [
k
)
},
{
id: 'trading_hours',
title: 'ساعت کاری و نمایش قیمت',
icon: 'vuesax-linear:clock',
match: (k) => /^(close_price|startDay|endDay|show_last_price)$/.test(k)
},
{
id: 'pricing',
title: 'کارمزد و محدودیت‌ها',
icon: 'vuesax-linear:dollar-circle',
match: (k) =>
/^(fee_|buy_fee|sell_fee|min_|minimum_|show_last_price|close_price|startDay|endDay|up_price|down_price|price_extreme_date|balance_report_exclude)/.test(
/^(fee_|buy_fee|sell_fee|min_|minimum_|up_price|down_price|price_extreme_date|balance_report_exclude)/.test(
k
) || k.includes('charge_percentage')
},
@@ -1,7 +1,7 @@
import type { Const } from '@/model/configuration'
import { isStoreApp } from '@/utils/config'
export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select' | 'longtext'
export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select' | 'longtext' | 'time'
/** کلیدهای legacy — در پنل نمایش داده نمی‌شوند */
export const HIDDEN_LEGACY_SETTING_KEYS = new Set([
@@ -42,7 +42,8 @@ export const SETTING_TYPE_OPTIONS = [
{ key: 'price', value: 'مبلغ (ریال)' },
{ key: 'switch', value: 'روشن/خاموش' },
{ key: 'select', value: 'انتخابی' },
{ key: 'longtext', value: 'متن بلند' }
{ key: 'longtext', value: 'متن بلند' },
{ key: 'time', value: 'ساعت' }
]
const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
@@ -73,6 +74,8 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
open_register: 'switch',
auto_active_customer: 'switch',
close_price: 'switch',
startDay: 'time',
endDay: 'time',
show_last_price: 'switch',
send_to_tahesab: 'switch',
profit_gold_api_enabled: 'switch',
@@ -136,7 +139,7 @@ export function referralRequiredToString(checked: boolean): string {
export function resolveSettingFieldType(row: Pick<Const, 'key' | 'type'>): SettingFieldType {
const raw = (row.type || '').trim().toLowerCase()
if (['text', 'number', 'price', 'switch', 'select', 'longtext'].includes(raw)) {
if (['text', 'number', 'price', 'switch', 'select', 'longtext', 'time'].includes(raw)) {
return raw as SettingFieldType
}
if (row.key && KEY_TYPE_FALLBACK[row.key]) {
@@ -205,6 +208,9 @@ export function formatValueForSave(
if (type === 'number') {
return String(value ?? '').replace(/,/g, '')
}
if (type === 'time') {
return normalizeTimeValue(value)
}
return value ?? ''
}
@@ -218,12 +224,45 @@ export function switchLabels(key: string | undefined): { on: string; off: string
return { on: 'فعال', off: 'غیرفعال' }
}
/** توضیحات کامل و ساده برای کلیدهای مهم — اگر description در دیتابیس خالی بود استفاده می‌شود */
const KEY_HELP_FALLBACK: Record<string, string> = {
close_price:
'فیلتر اصلی فروشگاه. وقتی روشن باشد، خرید و فروش و نمایش قیمت فقط در بازهٔ «ساعت شروع» تا «ساعت پایان» انجام می‌شود و خارج از این بازه به مشتری پیام «فروشگاه بسته است» نشان داده می‌شود. اگر خاموش باشد، فروشگاه همیشه باز است (مگر اینکه برای محصولی به‌صورت اختصاصی ساعت تعریف کرده باشید).',
startDay:
'ساعت شروع فعالیت فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود. ساعت را از روی ساعت‌شمار انتخاب کنید (مثلاً ۰۹:۰۰).',
endDay:
'ساعت پایان فعالیت فروشگاه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود. بعد از این ساعت فروشگاه بسته می‌شود (مثلاً ۱۷:۰۰).',
show_last_price:
'وقتی قیمت زندهٔ لحظه‌ای در دسترس نباشد: اگر روشن باشد، آخرین قیمت ذخیره‌شده در دیتابیس به مشتری نشان داده می‌شود؛ اگر خاموش باشد، به‌جای قیمت صفر پیام «مظنه در دسترس نیست» نمایش داده می‌شود.'
}
/** برچسب فارسی ساده برای کلیدهای مهم — اگر سرور برچسبی نفرستد */
export const KEY_LABEL_FALLBACK: Record<string, string> = {
close_price: 'بستن خودکار خارج از ساعت کاری',
startDay: 'ساعت شروع',
endDay: 'ساعت پایان',
show_last_price: 'نمایش آخرین قیمت ذخیره‌شده'
}
export function helpText(row: Const): string {
const desc = (row.description || '').trim()
if (!desc) return ''
if (!desc) {
return (row.key && KEY_HELP_FALLBACK[row.key]) || ''
}
if (resolveSettingFieldType(row) === 'select' && desc.includes(',')) {
const parts = desc.split('|')
return parts.length > 1 ? parts[0].trim() : ''
}
return desc
}
/** نرمال‌سازی ساعت به فرمت HH:mm:ss برای ذخیره و سازگاری با بک‌اند */
export function normalizeTimeValue(value: string | undefined | null): string {
const raw = String(value ?? '').trim()
if (!raw) return ''
const parts = raw.split(':')
const pad = (n: string) => n.padStart(2, '0')
if (parts.length >= 3) return `${pad(parts[0])}:${pad(parts[1])}:${pad(parts[2])}`
if (parts.length === 2) return `${pad(parts[0])}:${pad(parts[1])}:00`
return raw
}