init
This commit is contained in:
161
frontend/src/views/admin/AdminSettings.vue
Normal file
161
frontend/src/views/admin/AdminSettings.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="mb-6 text-2xl font-bold text-gray-900">Настройки</h1>
|
||||
|
||||
<div v-if="loading" class="text-gray-500">Загрузка...</div>
|
||||
|
||||
<div v-else class="space-y-6">
|
||||
<!-- Settings groups -->
|
||||
<div v-for="group in settingsGroups" :key="group.title" class="rounded-xl border border-gray-200 bg-white">
|
||||
<div class="border-b border-gray-200 px-5 py-3">
|
||||
<h2 class="text-sm font-bold uppercase text-gray-500">{{ group.title }}</h2>
|
||||
</div>
|
||||
<div class="divide-y divide-gray-100">
|
||||
<div v-for="item in group.items" :key="item.key" class="flex items-center justify-between px-5 py-4">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900">{{ item.label }}</p>
|
||||
<p class="text-xs text-gray-400">{{ item.key }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
v-model="settingsValues[item.key]"
|
||||
class="input-field !w-64 !py-1.5 text-sm text-right"
|
||||
:placeholder="item.placeholder || ''"
|
||||
@keydown.enter="saveKey(item.key)"
|
||||
/>
|
||||
<button
|
||||
@click="saveKey(item.key)"
|
||||
:disabled="savingKey === item.key"
|
||||
class="rounded-lg bg-primary-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-primary-700 disabled:opacity-50"
|
||||
>
|
||||
{{ savingKey === item.key ? '...' : 'Сохранить' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom settings -->
|
||||
<div class="rounded-xl border border-gray-200 bg-white">
|
||||
<div class="border-b border-gray-200 px-5 py-3 flex items-center justify-between">
|
||||
<h2 class="text-sm font-bold uppercase text-gray-500">Все настройки</h2>
|
||||
<button @click="showAddModal = true" class="text-xs font-medium text-primary-600 hover:text-primary-700">
|
||||
+ Добавить
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="allSettings.length === 0" class="px-5 py-8 text-center text-sm text-gray-400">
|
||||
Настроек пока нет
|
||||
</div>
|
||||
<div v-else class="divide-y divide-gray-100">
|
||||
<div v-for="s in allSettings" :key="s.key" class="flex items-center justify-between px-5 py-3">
|
||||
<div class="flex-1 min-w-0 mr-4">
|
||||
<p class="text-sm font-mono text-gray-700 truncate">{{ s.key }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-gray-900 max-w-xs truncate">{{ s.value }}</span>
|
||||
<span class="text-xs text-gray-400">{{ formatDate(s.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add setting modal -->
|
||||
<Teleport to="body">
|
||||
<div v-if="showAddModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/30" @click.self="showAddModal = false">
|
||||
<div class="w-full max-w-sm rounded-xl bg-white p-6 shadow-2xl">
|
||||
<h3 class="text-lg font-bold text-gray-900 mb-4">Новая настройка</h3>
|
||||
<form @submit.prevent="addSetting" class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase text-gray-500">Ключ</label>
|
||||
<input v-model="newKey" required class="input-field" placeholder="time_rate_per_hour" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase text-gray-500">Значение</label>
|
||||
<input v-model="newValue" required class="input-field" placeholder="200" />
|
||||
</div>
|
||||
<div class="flex justify-end gap-2 pt-2">
|
||||
<button type="button" @click="showAddModal = false" class="rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">
|
||||
Отмена
|
||||
</button>
|
||||
<button type="submit" class="btn-primary text-sm">Создать</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import api from '../../api/client'
|
||||
|
||||
const loading = ref(true)
|
||||
const allSettings = ref([])
|
||||
const settingsValues = ref({})
|
||||
const savingKey = ref(null)
|
||||
const showAddModal = ref(false)
|
||||
const newKey = ref('')
|
||||
const newValue = ref('')
|
||||
|
||||
const settingsGroups = [
|
||||
{
|
||||
title: 'Расчёт стоимости',
|
||||
items: [
|
||||
{ key: 'time_rate_per_hour', label: 'Ставка за час печати (руб)', placeholder: '200' },
|
||||
{ key: 'sanding_cost', label: 'Стоимость шлифовки (руб/шт)', placeholder: '300' },
|
||||
{ key: 'painting_cost', label: 'Стоимость покраски (руб/шт)', placeholder: '500' },
|
||||
{ key: 'threading_cost', label: 'Стоимость резьбы (руб/шт)', placeholder: '200' },
|
||||
{ key: 'acetone_smoothing_cost', label: 'Ацетоновая обработка (руб/шт)', placeholder: '400' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Уведомления',
|
||||
items: [
|
||||
{ key: 'telegram_enabled', label: 'Telegram уведомления (true/false)', placeholder: 'true' },
|
||||
{ key: 'company_name', label: 'Название компании', placeholder: 'Filam3D' },
|
||||
{ key: 'company_phone', label: 'Телефон', placeholder: '+7 (999) 123-45-67' },
|
||||
{ key: 'company_email', label: 'Email', placeholder: 'info@filam3d.ru' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
onMounted(() => loadSettings())
|
||||
|
||||
async function loadSettings() {
|
||||
loading.value = true
|
||||
try {
|
||||
const { data } = await api.get('/admin/settings')
|
||||
allSettings.value = data
|
||||
const map = {}
|
||||
data.forEach((s) => { map[s.key] = s.value })
|
||||
settingsValues.value = map
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveKey(key) {
|
||||
savingKey.value = key
|
||||
try {
|
||||
await api.put(`/admin/settings/${key}`, { value: settingsValues.value[key] || '' })
|
||||
await loadSettings()
|
||||
} finally {
|
||||
savingKey.value = null
|
||||
}
|
||||
}
|
||||
|
||||
async function addSetting() {
|
||||
await api.put(`/admin/settings/${newKey.value}`, { value: newValue.value })
|
||||
showAddModal.value = false
|
||||
newKey.value = ''
|
||||
newValue.value = ''
|
||||
await loadSettings()
|
||||
}
|
||||
|
||||
function formatDate(iso) {
|
||||
if (!iso) return ''
|
||||
return new Date(iso).toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user