239 lines
8.8 KiB
Vue
239 lines
8.8 KiB
Vue
<template>
|
||
<div class="w-full animate-reveal">
|
||
<div class="flex items-center justify-between mb-6">
|
||
<h1 class="text-2xl font-serif italic text-white">系统配置管理</h1>
|
||
<button @click="showAddModal = true" class="admin-btn-primary flex items-center gap-2">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||
新增配置
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 配置表单 -->
|
||
<div class="admin-card p-6">
|
||
<div v-if="settings.length === 0" class="p-16 text-center">
|
||
<div class="w-16 h-16 bg-white/5 rounded-full flex items-center justify-center mx-auto mb-4 text-2xl">⚙️</div>
|
||
<h3 class="text-white font-medium mb-2">暂无配置</h3>
|
||
<button @click="showAddModal = true" class="admin-btn-primary mt-4">添加第一个配置</button>
|
||
</div>
|
||
|
||
<form v-else @submit.prevent="saveAllSettings" class="space-y-6">
|
||
<div
|
||
v-for="(setting, index) in settings"
|
||
:key="setting.id"
|
||
class="p-4 border border-white/5 rounded-lg bg-white/2 hover:bg-white/5 transition-colors"
|
||
>
|
||
<div class="grid grid-cols-1 md:grid-cols-12 gap-4 items-start">
|
||
<!-- 键名(只读) -->
|
||
<div class="md:col-span-3 space-y-2">
|
||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">键名</label>
|
||
<input
|
||
type="text"
|
||
:value="setting.keyName"
|
||
disabled
|
||
class="admin-input font-mono disabled:opacity-50 disabled:cursor-not-allowed bg-white/5"
|
||
>
|
||
</div>
|
||
|
||
<!-- 值(可编辑) -->
|
||
<div class="md:col-span-4 space-y-2">
|
||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">值</label>
|
||
<input
|
||
type="text"
|
||
v-model="setting.value"
|
||
class="admin-input"
|
||
placeholder="配置项的值"
|
||
>
|
||
</div>
|
||
|
||
<!-- 描述(可编辑) -->
|
||
<div class="md:col-span-4 space-y-2">
|
||
<label class="block text-xs font-medium text-art-muted uppercase tracking-wider">描述</label>
|
||
<textarea
|
||
v-model="setting.description"
|
||
rows="2"
|
||
class="admin-input resize-none"
|
||
placeholder="该配置项的用途说明"
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- 删除按钮 -->
|
||
<div class="md:col-span-1 space-y-2 flex items-end">
|
||
<button
|
||
type="button"
|
||
@click="deleteSetting(setting.keyName)"
|
||
class="admin-btn-danger py-2 px-3 text-xs w-full"
|
||
title="删除配置"
|
||
>
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 保存按钮 -->
|
||
<div class="pt-4 flex justify-end gap-3 border-t border-white/5">
|
||
<button type="button" @click="fetchSettings" class="admin-btn-secondary">重置</button>
|
||
<button type="submit" class="admin-btn-primary" :disabled="isSaving">
|
||
{{ isSaving ? '保存中...' : '保存所有配置' }}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- 新增配置 Modal -->
|
||
<div v-if="showAddModal" class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||
<div class="absolute inset-0 bg-black/80 backdrop-blur-sm transition-opacity" @click="closeAddModal"></div>
|
||
|
||
<div class="admin-card w-full max-w-lg relative z-10 flex flex-col max-h-[90vh] shadow-2xl animate-reveal">
|
||
<div class="flex items-center justify-between p-6 border-b border-white/5">
|
||
<h2 class="text-lg font-medium text-white">新增配置</h2>
|
||
<button @click="closeAddModal" class="text-white/40 hover:text-white transition-colors">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="p-6 overflow-y-auto">
|
||
<form @submit.prevent="saveNewSetting" class="space-y-4">
|
||
<div class="space-y-2">
|
||
<label for="keyName" class="block text-xs font-medium text-art-muted uppercase tracking-wider">键名</label>
|
||
<input
|
||
type="text"
|
||
id="keyName"
|
||
v-model="newForm.keyName"
|
||
required
|
||
class="admin-input font-mono"
|
||
placeholder="例如: site_title"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="value" class="block text-xs font-medium text-art-muted uppercase tracking-wider">值</label>
|
||
<input
|
||
type="text"
|
||
id="value"
|
||
v-model="newForm.value"
|
||
required
|
||
class="admin-input"
|
||
placeholder="配置项的值"
|
||
>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<label for="description" class="block text-xs font-medium text-art-muted uppercase tracking-wider">描述</label>
|
||
<textarea
|
||
id="description"
|
||
v-model="newForm.description"
|
||
rows="3"
|
||
class="admin-input resize-none"
|
||
placeholder="该配置项的用途说明"
|
||
></textarea>
|
||
</div>
|
||
|
||
<div class="pt-4 flex justify-end gap-3">
|
||
<button type="button" @click="closeAddModal" class="admin-btn-secondary">取消</button>
|
||
<button type="submit" class="admin-btn-primary">保存</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted } from 'vue'
|
||
import { getSettings, createSetting, updateSetting, deleteSetting as deleteSettingApi, Setting } from '../../services/api'
|
||
import { useToast } from '../../composables/useToast'
|
||
|
||
const toast = useToast()
|
||
const settings = ref<Setting[]>([])
|
||
const showAddModal = ref(false)
|
||
const isSaving = ref(false)
|
||
|
||
const newForm = ref({
|
||
keyName: '',
|
||
value: '',
|
||
description: ''
|
||
})
|
||
|
||
const fetchSettings = async () => {
|
||
try {
|
||
settings.value = await getSettings()
|
||
} catch (error) {
|
||
console.error('Error fetching settings:', error)
|
||
toast.showToast('获取系统配置失败', 'error')
|
||
}
|
||
}
|
||
|
||
const saveAllSettings = async () => {
|
||
isSaving.value = true
|
||
try {
|
||
// 批量更新所有配置项
|
||
const updatePromises = settings.value.map(setting =>
|
||
updateSetting({
|
||
id: setting.id,
|
||
keyName: setting.keyName,
|
||
value: setting.value,
|
||
description: setting.description
|
||
})
|
||
)
|
||
|
||
await Promise.all(updatePromises)
|
||
toast.showToast('所有配置保存成功', 'success')
|
||
// 重新获取配置以确保数据同步
|
||
await fetchSettings()
|
||
} catch (error) {
|
||
console.error('Error saving settings:', error)
|
||
toast.showToast('保存配置失败', 'error')
|
||
} finally {
|
||
isSaving.value = false
|
||
}
|
||
}
|
||
|
||
const saveNewSetting = async () => {
|
||
try {
|
||
await createSetting({
|
||
keyName: newForm.value.keyName,
|
||
value: newForm.value.value,
|
||
description: newForm.value.description
|
||
})
|
||
toast.showToast('配置创建成功', 'success')
|
||
closeAddModal()
|
||
fetchSettings()
|
||
} catch (error) {
|
||
console.error('Error creating setting:', error)
|
||
toast.showToast('创建配置失败', 'error')
|
||
}
|
||
}
|
||
|
||
const deleteSetting = async (keyName: string) => {
|
||
if (confirm(`确定要删除配置项 "${keyName}" 吗?`)) {
|
||
try {
|
||
await deleteSettingApi(keyName)
|
||
toast.showToast('配置删除成功', 'success')
|
||
fetchSettings()
|
||
} catch (error) {
|
||
console.error('Error deleting setting:', error)
|
||
toast.showToast('删除配置失败', 'error')
|
||
}
|
||
}
|
||
}
|
||
|
||
const closeAddModal = () => {
|
||
showAddModal.value = false
|
||
newForm.value = {
|
||
keyName: '',
|
||
value: '',
|
||
description: ''
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchSettings()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* Scoped styles removed */
|
||
</style>
|