Files
nl-blogs/client/src/pages/admin/Settings.vue

239 lines
8.8 KiB
Vue
Raw Normal View History

2026-01-15 13:51:44 +08:00
<template>
2026-01-16 11:06:59 +08:00
<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>
2026-01-20 08:31:42 +08:00
<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>
2026-01-15 13:51:44 +08:00
</div>
2026-01-19 16:14:08 +08:00
2026-01-20 08:31:42 +08:00
<!-- 配置表单 -->
<div class="admin-card p-6">
2026-01-16 11:06:59 +08:00
<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>
2026-01-20 08:31:42 +08:00
<button @click="showAddModal = true" class="admin-btn-primary mt-4">添加第一个配置</button>
2026-01-16 11:06:59 +08:00
</div>
2026-01-20 08:31:42 +08:00
<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>
2026-01-15 13:51:44 +08:00
</div>
2026-01-20 08:31:42 +08:00
<!-- 新增配置 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>
2026-01-16 11:06:59 +08:00
<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">
2026-01-20 08:31:42 +08:00
<h2 class="text-lg font-medium text-white">新增配置</h2>
<button @click="closeAddModal" class="text-white/40 hover:text-white transition-colors">
2026-01-16 11:06:59 +08:00
<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>
2026-01-15 13:51:44 +08:00
</div>
2026-01-16 11:06:59 +08:00
<div class="p-6 overflow-y-auto">
2026-01-20 08:31:42 +08:00
<form @submit.prevent="saveNewSetting" class="space-y-4">
2026-01-16 11:06:59 +08:00
<div class="space-y-2">
<label for="keyName" class="block text-xs font-medium text-art-muted uppercase tracking-wider">键名</label>
2026-01-15 13:51:44 +08:00
<input
type="text"
id="keyName"
2026-01-20 08:31:42 +08:00
v-model="newForm.keyName"
2026-01-15 13:51:44 +08:00
required
2026-01-20 08:31:42 +08:00
class="admin-input font-mono"
2026-01-16 11:06:59 +08:00
placeholder="例如: site_title"
2026-01-15 13:51:44 +08:00
>
</div>
2026-01-16 11:06:59 +08:00
<div class="space-y-2">
<label for="value" class="block text-xs font-medium text-art-muted uppercase tracking-wider">值</label>
2026-01-15 13:51:44 +08:00
<input
type="text"
id="value"
2026-01-20 08:31:42 +08:00
v-model="newForm.value"
2026-01-15 13:51:44 +08:00
required
2026-01-16 11:06:59 +08:00
class="admin-input"
placeholder="配置项的值"
2026-01-15 13:51:44 +08:00
>
</div>
2026-01-16 11:06:59 +08:00
<div class="space-y-2">
<label for="description" class="block text-xs font-medium text-art-muted uppercase tracking-wider">描述</label>
2026-01-15 13:51:44 +08:00
<textarea
id="description"
2026-01-20 08:31:42 +08:00
v-model="newForm.description"
2026-01-15 13:51:44 +08:00
rows="3"
2026-01-16 11:06:59 +08:00
class="admin-input resize-none"
placeholder="该配置项的用途说明"
2026-01-15 13:51:44 +08:00
></textarea>
</div>
2026-01-16 11:06:59 +08:00
<div class="pt-4 flex justify-end gap-3">
2026-01-20 08:31:42 +08:00
<button type="button" @click="closeAddModal" class="admin-btn-secondary">取消</button>
2026-01-16 11:06:59 +08:00
<button type="submit" class="admin-btn-primary">保存</button>
2026-01-15 13:51:44 +08:00
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
2026-01-19 20:21:09 +08:00
import { ref, onMounted } from 'vue'
2026-01-15 13:51:44 +08:00
import { getSettings, createSetting, updateSetting, deleteSetting as deleteSettingApi, Setting } from '../../services/api'
import { useToast } from '../../composables/useToast'
const toast = useToast()
const settings = ref<Setting[]>([])
2026-01-20 08:31:42 +08:00
const showAddModal = ref(false)
const isSaving = ref(false)
2026-01-15 13:51:44 +08:00
2026-01-20 08:31:42 +08:00
const newForm = ref({
2026-01-15 13:51:44 +08:00
keyName: '',
value: '',
description: ''
})
const fetchSettings = async () => {
try {
settings.value = await getSettings()
} catch (error) {
console.error('Error fetching settings:', error)
2026-01-16 11:06:59 +08:00
toast.showToast('获取系统配置失败', 'error')
2026-01-15 13:51:44 +08:00
}
}
2026-01-20 08:31:42 +08:00
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
2026-01-15 13:51:44 +08:00
}
}
2026-01-20 08:31:42 +08:00
const saveNewSetting = async () => {
2026-01-15 13:51:44 +08:00
try {
2026-01-20 08:31:42 +08:00
await createSetting({
keyName: newForm.value.keyName,
value: newForm.value.value,
description: newForm.value.description
})
toast.showToast('配置创建成功', 'success')
closeAddModal()
2026-01-15 13:51:44 +08:00
fetchSettings()
} catch (error) {
2026-01-20 08:31:42 +08:00
console.error('Error creating setting:', error)
toast.showToast('创建配置失败', 'error')
2026-01-15 13:51:44 +08:00
}
}
const deleteSetting = async (keyName: string) => {
if (confirm(`确定要删除配置项 "${keyName}" 吗?`)) {
try {
await deleteSettingApi(keyName)
2026-01-16 11:06:59 +08:00
toast.showToast('配置删除成功', 'success')
2026-01-15 13:51:44 +08:00
fetchSettings()
} catch (error) {
console.error('Error deleting setting:', error)
2026-01-16 11:06:59 +08:00
toast.showToast('删除配置失败', 'error')
2026-01-15 13:51:44 +08:00
}
}
}
2026-01-20 08:31:42 +08:00
const closeAddModal = () => {
showAddModal.value = false
newForm.value = {
2026-01-15 13:51:44 +08:00
keyName: '',
value: '',
description: ''
}
}
onMounted(() => {
fetchSettings()
})
</script>
<style scoped>
2026-01-16 11:06:59 +08:00
/* Scoped styles removed */
</style>