165 lines
5.1 KiB
Vue
165 lines
5.1 KiB
Vue
<template>
|
||||
|
|
<BasicModal
|
|||
|
|
v-model:visible="modalVisible"
|
|||
|
|
title="تنظیمات اعتبار طلایی"
|
|||
|
|
width="48rem"
|
|||
|
|
ok-text="بستن"
|
|||
|
|
@ok="modalVisible = false"
|
|||
|
|
>
|
|||
|
|
<a-spin :spinning="loading">
|
|||
|
|
<div class="space-y-4">
|
|||
|
|
<div class="text-sm text-gray-500 mb-2">
|
|||
|
|
این تنظیمات در اپ کاربر اعمال میشوند. روشهای پرداخت غیرفعال در اپ نمایش داده نمیشوند؛
|
|||
|
|
ادمین همچنان میتواند از همه روشها استفاده کند.
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
v-for="row in rows"
|
|||
|
|
:key="row.id ?? row.key"
|
|||
|
|
class="rounded-xl border border-slate-100 bg-slate-50/60 p-3"
|
|||
|
|
>
|
|||
|
|
<div class="font-medium text-sm text-gray-800 mb-1">{{ row.title_fa || row.key }}</div>
|
|||
|
|
<div v-if="row.description" class="text-xs text-gray-500 mb-2 whitespace-pre-wrap">
|
|||
|
|
{{ displayDescription(row) }}
|
|||
|
|
</div>
|
|||
|
|
<SettingValueField
|
|||
|
|
:model-value="drafts[row.id!] ?? ''"
|
|||
|
|
:setting-key="row.key"
|
|||
|
|
:setting-type="row.type"
|
|||
|
|
:description="row.description"
|
|||
|
|
:rows="row.key === 'gold_credit_tiers' ? 6 : 4"
|
|||
|
|
:disabled="savingIds.has(row.id!)"
|
|||
|
|
@update:value="onDraftChange(row, $event)"
|
|||
|
|
/>
|
|||
|
|
<div v-if="savedFlashIds.has(row.id!)" class="text-xs text-green-600 mt-1">ذخیره شد</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</a-spin>
|
|||
|
|
</BasicModal>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { computed, ref, watch } from 'vue'
|
|||
|
|
import { message } from 'ant-design-vue'
|
|||
|
|
import { getConfiguration, setConfiguration } from '@/api/configuration'
|
|||
|
|
import type { Const } from '@/model/configuration'
|
|||
|
|
import SettingValueField from '@/pages/menuPages/configuration/SettingValueField.vue'
|
|||
|
|
import { formatValueForSave, resolveSettingFieldType } from '@/pages/menuPages/configuration/settingTypes'
|
|||
|
|
|
|||
|
|
const GOLD_CREDIT_KEYS = [
|
|||
|
|
'gold_credit_pay_gold_enabled',
|
|||
|
|
'gold_credit_pay_rial_enabled',
|
|||
|
|
'gold_credit_pay_mixed_enabled',
|
|||
|
|
'gold_credit_min_balance_gram',
|
|||
|
|
'gold_credit_default_months',
|
|||
|
|
'gold_credit_info_text',
|
|||
|
|
'gold_credit_request_success_text',
|
|||
|
|
'gold_credit_tiers',
|
|||
|
|
'gold_credit_penalty_type',
|
|||
|
|
'gold_credit_penalty_amount',
|
|||
|
|
'gold_credit_grace_hours',
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const props = defineProps<{ visible?: boolean }>()
|
|||
|
|
const emit = defineEmits<{ (e: 'update:visible', value: boolean): void }>()
|
|||
|
|
|
|||
|
|
const modalVisible = computed({
|
|||
|
|
get: () => props.visible ?? false,
|
|||
|
|
set: (v: boolean) => emit('update:visible', v),
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const rows = ref<Const[]>([])
|
|||
|
|
const drafts = ref<Record<number, string>>({})
|
|||
|
|
const savingIds = ref(new Set<number>())
|
|||
|
|
const savedFlashIds = ref(new Set<number>())
|
|||
|
|
const saveTimers = new Map<number, ReturnType<typeof setTimeout>>()
|
|||
|
|
const debounceTimers = new Map<number, ReturnType<typeof setTimeout>>()
|
|||
|
|
|
|||
|
|
function displayDescription(row: Const) {
|
|||
|
|
if (row.type === 'select' && row.description?.includes('|')) {
|
|||
|
|
return row.description.split('|')[0]
|
|||
|
|
}
|
|||
|
|
return row.description
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
watch(
|
|||
|
|
() => props.visible,
|
|||
|
|
async (open) => {
|
|||
|
|
if (!open) return
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
const all = (await getConfiguration()) ?? []
|
|||
|
|
rows.value = all
|
|||
|
|
.filter((r) => r.key && GOLD_CREDIT_KEYS.includes(r.key))
|
|||
|
|
.sort(
|
|||
|
|
(a, b) =>
|
|||
|
|
GOLD_CREDIT_KEYS.indexOf(a.key!) - GOLD_CREDIT_KEYS.indexOf(b.key!)
|
|||
|
|
)
|
|||
|
|
const next: Record<number, string> = {}
|
|||
|
|
for (const row of rows.value) {
|
|||
|
|
if (row.id != null) next[row.id] = String(row.value ?? '')
|
|||
|
|
}
|
|||
|
|
drafts.value = next
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
function onDraftChange(row: Const, value: string) {
|
|||
|
|
if (row.id == null) return
|
|||
|
|
drafts.value[row.id] = value
|
|||
|
|
scheduleSave(row)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function scheduleSave(row: Const) {
|
|||
|
|
if (row.id == null) return
|
|||
|
|
if (debounceTimers.has(row.id)) clearTimeout(debounceTimers.get(row.id))
|
|||
|
|
debounceTimers.set(
|
|||
|
|
row.id,
|
|||
|
|
setTimeout(() => {
|
|||
|
|
void saveRow(row)
|
|||
|
|
}, 600)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function saveRow(row: Const) {
|
|||
|
|
if (row.id == null) return
|
|||
|
|
const type = resolveSettingFieldType(row)
|
|||
|
|
const raw = drafts.value[row.id] ?? row.value ?? ''
|
|||
|
|
const value = formatValueForSave(raw, type)
|
|||
|
|
if (value === (row.value ?? '')) return
|
|||
|
|
|
|||
|
|
savingIds.value.add(row.id)
|
|||
|
|
savedFlashIds.value.delete(row.id)
|
|||
|
|
try {
|
|||
|
|
await setConfiguration({
|
|||
|
|
id: row.id,
|
|||
|
|
key: row.key,
|
|||
|
|
value,
|
|||
|
|
description: row.description ?? '',
|
|||
|
|
is_active: row.is_active,
|
|||
|
|
title_fa: row.title_fa ?? '',
|
|||
|
|
type,
|
|||
|
|
})
|
|||
|
|
row.value = value
|
|||
|
|
savedFlashIds.value.add(row.id)
|
|||
|
|
if (saveTimers.has(row.id)) clearTimeout(saveTimers.get(row.id))
|
|||
|
|
saveTimers.set(
|
|||
|
|
row.id,
|
|||
|
|
setTimeout(() => {
|
|||
|
|
savedFlashIds.value.delete(row.id!)
|
|||
|
|
savedFlashIds.value = new Set(savedFlashIds.value)
|
|||
|
|
}, 2000)
|
|||
|
|
)
|
|||
|
|
} catch {
|
|||
|
|
drafts.value[row.id] = String(row.value ?? '')
|
|||
|
|
message.error(`ذخیره «${row.title_fa || row.key}» ناموفق بود`)
|
|||
|
|
} finally {
|
|||
|
|
savingIds.value.delete(row.id)
|
|||
|
|
savingIds.value = new Set(savingIds.value)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|