Files
web/src/components/SettingsCatalogPanel.vue
T

206 lines
6.0 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { reactive, ref } from 'vue'
import { message } from 'ant-design-vue'
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
import api from '@/api/http'
import { SETTING_TYPE_OPTIONS } from '@/config/khanegiSettingCatalog'
const props = defineProps<{ canWrite: boolean }>()
const addOpen = ref(false)
const deleteOpen = ref(false)
const saving = ref(false)
const deleting = ref(false)
const activeTab = ref('form')
const jsonText = ref(
'[\n {\n "key": "name_fa",\n "value": "",\n "title_fa": "نام فارسی",\n "description": "",\n "type": "text",\n "can_edit": 0,\n "is_active": 1\n }\n]',
)
const deleteKey = ref('')
const form = reactive({
key: '',
title_fa: '',
value: '',
description: '',
type: 'text',
type_data: '',
can_edit: 0,
is_active: 1,
})
const typeOptions = SETTING_TYPE_OPTIONS.map((t) => ({ value: t.key, label: t.value }))
function parseJsonItems(raw: string): Array<Record<string, unknown>> {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed)) return parsed
if (parsed && typeof parsed === 'object' && Array.isArray(parsed.items)) return parsed.items
throw new Error('JSON باید آرایه باشد: [{"key":"name_fa",...}]')
}
function errText(e: any) {
const msg = e?.response?.data?.message
return Array.isArray(msg) ? msg.join('، ') : msg || 'خطا'
}
async function submitAdd() {
if (!props.canWrite) {
message.warning('دسترسی نوشتن ندارید')
return Promise.reject()
}
let items: Array<Record<string, unknown>> = []
try {
if (activeTab.value === 'json') {
items = parseJsonItems(jsonText.value)
} else {
if (!form.key.trim()) {
message.warning('اسم کلید لازم است (مثلاً name_fa)')
return Promise.reject()
}
items = [{ ...form, key: form.key.trim() }]
}
} catch (e: any) {
message.error(e?.message || 'JSON نامعتبر است')
return Promise.reject()
}
saving.value = true
try {
const { data } = await api.post('/setting-templates/keys', { items })
message.success(`${data.count ?? items.length} کلید ثبت شد`)
form.key = ''
form.title_fa = ''
form.value = ''
form.description = ''
addOpen.value = false
} catch (e: any) {
message.error(errText(e) || 'ثبت ناموفق')
return Promise.reject()
} finally {
saving.value = false
}
}
async function submitDelete() {
const key = deleteKey.value.trim()
if (!key) {
message.warning('اسم کلید را بنویس (مثلاً name_fa)')
return Promise.reject()
}
deleting.value = true
try {
await api.delete('/setting-templates/keys', { params: { key } })
message.success('کلید حذف شد')
deleteKey.value = ''
deleteOpen.value = false
} catch (e: any) {
message.error(errText(e) || 'حذف ناموفق')
return Promise.reject()
} finally {
deleting.value = false
}
}
</script>
<template>
<div class="catalog-actions">
<a-button v-if="canWrite" @click="addOpen = true">
<PlusOutlined /> افزودن کلید
</a-button>
<a-button v-if="canWrite" danger ghost @click="deleteOpen = true">
<DeleteOutlined /> حذف کلید
</a-button>
</div>
<a-modal
v-model:open="addOpen"
title="افزودن کلید تنظیمات"
ok-text="ثبت کلید"
cancel-text="انصراف"
:confirm-loading="saving"
:width="720"
destroy-on-close
@ok="submitAdd"
>
<p class="field-hint">
با ثبت، کلید به قالب نسخه‌های بعدی می‌رود و روی پروژه‌هایی که این کلید را ندارند اعمال می‌شود.
اسم کلید مثل <code dir="ltr">name_fa</code> است نه عنوان فارسی.
</p>
<a-tabs v-model:activeKey="activeTab">
<a-tab-pane key="form" tab="فرم">
<a-form layout="vertical">
<div class="catalog-grid">
<a-form-item label="کلید (key)" required>
<a-input v-model:value="form.key" dir="ltr" placeholder="name_fa" size="large" />
</a-form-item>
<a-form-item label="عنوان فارسی">
<a-input v-model:value="form.title_fa" size="large" />
</a-form-item>
<a-form-item label="مقدار پیش‌فرض">
<a-input v-model:value="form.value" dir="ltr" size="large" />
</a-form-item>
<a-form-item label="نوع">
<a-select v-model:value="form.type" :options="typeOptions" size="large" />
</a-form-item>
</div>
<a-form-item label="توضیح">
<a-textarea v-model:value="form.description" :rows="2" />
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="json" tab="JSON">
<a-textarea v-model:value="jsonText" :rows="10" dir="ltr" class="json-box" />
</a-tab-pane>
</a-tabs>
</a-modal>
<a-modal
v-model:open="deleteOpen"
title="حذف کلید از همهٔ نسخه‌ها"
ok-text="حذف از همه"
ok-type="danger"
cancel-text="انصراف"
:confirm-loading="deleting"
:width="520"
destroy-on-close
@ok="submitDelete"
>
<p class="field-hint">
این کلید از قالب و تنظیمات همهٔ پروژه‌های خانگی پاک می‌شود. اسم کلید را بنویس، نه عنوان فارسی.
</p>
<a-input
v-model:value="deleteKey"
dir="ltr"
size="large"
placeholder="مثلاً name_fa"
/>
</a-modal>
</template>
<style scoped>
.catalog-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.field-hint {
margin: 0 0 12px;
color: #64748b;
font-size: 13px;
line-height: 1.6;
}
.catalog-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 12px;
}
@media (max-width: 720px) {
.catalog-grid {
grid-template-columns: 1fr;
}
}
.json-box {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
}
</style>