fix bugs
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<a-select
|
||||
mode="multiple"
|
||||
:value="selectedIds"
|
||||
:disabled="disabled"
|
||||
:loading="loading"
|
||||
allow-clear
|
||||
show-search
|
||||
placeholder="انتخاب محصولات متصل به کیف اصلی"
|
||||
class="w-full"
|
||||
option-filter-prop="label"
|
||||
@update:value="onChange"
|
||||
>
|
||||
<a-select-option
|
||||
v-for="item in allOptions"
|
||||
:key="String(item.id)"
|
||||
:value="String(item.id)"
|
||||
:label="`${item.name} ${item.id}`"
|
||||
>
|
||||
<div class="leading-snug">
|
||||
<div>{{ item.name }}</div>
|
||||
<div class="text-xs text-gray-400">شناسه: {{ item.id }}</div>
|
||||
</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { getCustomersList } from '@/api/product'
|
||||
import type { Product } from '@/model/product'
|
||||
import {
|
||||
MAIN_WALLET_PRODUCTS_DEFAULT,
|
||||
formatMainWalletProductIds,
|
||||
parseMainWalletProductIds
|
||||
} from './settingTypes'
|
||||
|
||||
type ProductOption = { id: number; name: string }
|
||||
|
||||
function productLabel(row: Pick<Product, 'id' | 'title'> & { name?: string }): string {
|
||||
const label = (row.title || row.name || '').trim()
|
||||
return label || `محصول #${row.id}`
|
||||
}
|
||||
|
||||
function normalizeProductList(response: unknown): Product[] {
|
||||
if (Array.isArray(response)) {
|
||||
return response as Product[]
|
||||
}
|
||||
if (response && typeof response === 'object' && Array.isArray((response as { data?: unknown }).data)) {
|
||||
return (response as { data: Product[] }).data
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{ disabled: false }
|
||||
)
|
||||
|
||||
const emit = defineEmits<{ 'update:value': [value: string] }>()
|
||||
|
||||
const loading = ref(false)
|
||||
const products = ref<ProductOption[]>([])
|
||||
|
||||
const selectedIds = computed(() => {
|
||||
const parsed = parseMainWalletProductIds(props.modelValue)
|
||||
return parsed.length > 0 ? parsed : [MAIN_WALLET_PRODUCTS_DEFAULT]
|
||||
})
|
||||
|
||||
const allOptions = computed(() => {
|
||||
const known = new Set(products.value.map((p) => p.id))
|
||||
const extras: ProductOption[] = []
|
||||
|
||||
for (const idStr of selectedIds.value) {
|
||||
const id = Number(idStr)
|
||||
if (!Number.isFinite(id) || id <= 0 || known.has(id)) continue
|
||||
extras.push({ id, name: `محصول #${id}` })
|
||||
}
|
||||
|
||||
return [...extras, ...products.value]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
void loadProducts()
|
||||
})
|
||||
|
||||
async function loadProducts() {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getCustomersList()
|
||||
const rows = normalizeProductList(response)
|
||||
products.value = rows
|
||||
.map((row) => ({ id: row.id, name: productLabel(row) }))
|
||||
.filter((row) => row.id > 0)
|
||||
.sort((a, b) => a.name.localeCompare(b.name, 'fa'))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
products.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(val: string[] | undefined) {
|
||||
emit('update:value', formatMainWalletProductIds(val ?? []))
|
||||
}
|
||||
</script>
|
||||
@@ -10,6 +10,13 @@
|
||||
<template #unCheckedChildren>{{ labels.off }}</template>
|
||||
</a-switch>
|
||||
|
||||
<MainWalletProductsField
|
||||
v-else-if="isMainWalletProducts"
|
||||
:model-value="modelValue"
|
||||
:disabled="disabled"
|
||||
@update:value="emit('update:value', $event)"
|
||||
/>
|
||||
|
||||
<ZaarboxPriceKeyField
|
||||
v-else-if="isZaarboxPriceKey"
|
||||
:model-value="modelValue"
|
||||
@@ -86,6 +93,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MainWalletProductsField from './MainWalletProductsField.vue'
|
||||
import ZaarboxPriceKeyField from './ZaarboxPriceKeyField.vue'
|
||||
import {
|
||||
parseSelectOptions,
|
||||
@@ -121,6 +129,7 @@ const fieldType = computed<SettingFieldType>(() =>
|
||||
)
|
||||
|
||||
const isZaarboxPriceKey = computed(() => props.settingKey === 'zaarbox_price_key')
|
||||
const isMainWalletProducts = computed(() => props.settingKey === 'main_wallet_products')
|
||||
|
||||
const selectItems = computed(() =>
|
||||
parseSelectOptions({
|
||||
|
||||
@@ -114,7 +114,7 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|
||||
title: 'منبع قیمت و تابلو',
|
||||
icon: 'vuesax-linear:dollar-circle',
|
||||
match: (k) =>
|
||||
/^(price_provider|zaarbox_|main_product$|default_vc_number)/.test(k) ||
|
||||
/^(price_provider|zaarbox_|main_product$)/.test(k) ||
|
||||
k.includes('charge_percentage')
|
||||
},
|
||||
{
|
||||
@@ -145,7 +145,7 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|
||||
id: 'infra',
|
||||
title: 'زیرساخت',
|
||||
icon: 'vuesax-linear:cpu',
|
||||
match: (k) => /^(wss_enabled|sse_path|wss_path|wss_link|wss_trigger_path|profit_gold_api_enabled)/.test(k)
|
||||
match: (k) => /^(wss_enabled|sse_path|profit_gold_api_enabled)/.test(k)
|
||||
},
|
||||
{
|
||||
id: 'misc',
|
||||
|
||||
@@ -3,6 +3,43 @@ import { isStoreApp } from '@/utils/config'
|
||||
|
||||
export type SettingFieldType = 'text' | 'number' | 'price' | 'switch' | 'select' | 'longtext' | 'time'
|
||||
|
||||
export const MAIN_WALLET_PRODUCTS_DEFAULT = '13'
|
||||
|
||||
/** پارس setting `main_wallet_products` — id با کاما یا JSON */
|
||||
export function parseMainWalletProductIds(value: string | undefined | null): string[] {
|
||||
const raw = (value ?? '').trim()
|
||||
if (!raw) return []
|
||||
|
||||
try {
|
||||
const decoded = JSON.parse(raw)
|
||||
if (Array.isArray(decoded)) {
|
||||
return decoded
|
||||
.map((part) => String(part).trim())
|
||||
.filter((part) => /^\d+$/.test(part))
|
||||
}
|
||||
} catch {
|
||||
// not JSON — comma-separated
|
||||
}
|
||||
|
||||
return raw
|
||||
.split(',')
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => /^\d+$/.test(part))
|
||||
}
|
||||
|
||||
export function formatMainWalletProductIds(ids: Array<string | number>): string {
|
||||
const unique = [...new Set(ids.map((id) => String(id).trim()).filter((id) => /^\d+$/.test(id)))]
|
||||
if (unique.length === 0) {
|
||||
return MAIN_WALLET_PRODUCTS_DEFAULT
|
||||
}
|
||||
return unique.join(',')
|
||||
}
|
||||
|
||||
export function normalizeMainWalletProductsValue(value: string | undefined | null): string {
|
||||
const formatted = formatMainWalletProductIds(parseMainWalletProductIds(value))
|
||||
return formatted || MAIN_WALLET_PRODUCTS_DEFAULT
|
||||
}
|
||||
|
||||
/** کلیدهای legacy — در پنل نمایش داده نمیشوند */
|
||||
export const HIDDEN_LEGACY_SETTING_KEYS = new Set([
|
||||
'main_product_price',
|
||||
@@ -56,7 +93,6 @@ const KEY_TYPE_FALLBACK: Record<string, SettingFieldType> = {
|
||||
fee_single: 'price',
|
||||
buy_fee: 'price',
|
||||
sell_fee: 'price',
|
||||
fee_cash: 'price',
|
||||
fee_free: 'number',
|
||||
min_withdraw: 'price',
|
||||
min_first_buy: 'price',
|
||||
@@ -209,6 +245,9 @@ export function formatValueForSave(
|
||||
if (type === 'time') {
|
||||
return normalizeTimeValue(value)
|
||||
}
|
||||
if (key === 'main_wallet_products') {
|
||||
return normalizeMainWalletProductsValue(value)
|
||||
}
|
||||
return value ?? ''
|
||||
}
|
||||
|
||||
@@ -316,8 +355,8 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
||||
'روشن: امکان ارسال پیامک گروهی از پنل ادمین فعال است.',
|
||||
profit_gold_api_enabled:
|
||||
'روشن: محاسبه سود طلا از API خارجی فعال است. در اکثر نصبها خاموش میماند.',
|
||||
default_vc_number:
|
||||
'پیشوند اختیاری برای شمارهگذاری حوالهها در حسابداری خارجی. خالی = بدون پیشوند.',
|
||||
main_wallet_products:
|
||||
'محصولاتی که موجودیشان از کیف طلای اصلی خوانده میشود. میتوانید چند محصول انتخاب کنید. اگر خالی بماند، پیشفرض محصول ۱۳ است.',
|
||||
unit:
|
||||
'واحد نمایش وزن در داراییها، کیف پول و جزئیات معاملات (گرم یا کیلوگرم).',
|
||||
chart_use_local_data:
|
||||
@@ -342,10 +381,8 @@ const KEY_HELP_FALLBACK: Record<string, string> = {
|
||||
'شناسه مشتری در سامانه حسابداری که تراکنشها به نام آن ثبت میشوند.',
|
||||
payping_gateway_name:
|
||||
'نام درگاه پرداخت فعال که به واسط درگاه ارسال میشود.',
|
||||
payping_gateway_item:
|
||||
'کد درگاه (تکی). اگر لیست چرخشی درگاه پر باشد، اولویت با آن است.',
|
||||
payping_gateway_items:
|
||||
'لیست کدهای درگاه با ویرگول برای چرخش بین چند پذیرنده. خالی = استفاده از کد تکی.',
|
||||
'لیست کدهای درگاه با ویرگول برای چرخش بین چند پذیرنده.',
|
||||
payping_start_url:
|
||||
'آدرس API شروع پرداخت (هدایت مشتری به درگاه).',
|
||||
payping_verify_url:
|
||||
|
||||
Reference in New Issue
Block a user