数据结构优化
This commit is contained in:
@@ -101,6 +101,14 @@
|
||||
|
||||
<!-- Right: Actions -->
|
||||
<div class="flex items-center gap-3">
|
||||
<button
|
||||
@click="refreshCurrentPage"
|
||||
class="p-2 text-art-muted hover:text-white hover:bg-white/5 rounded-full transition-colors"
|
||||
title="刷新当前页面"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>
|
||||
</button>
|
||||
|
||||
<button class="p-2 text-art-muted hover:text-white hover:bg-white/5 rounded-full transition-colors relative">
|
||||
<span class="absolute top-2 right-2 w-2 h-2 bg-art-accent rounded-full border-2 border-art-admin-bg"></span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path><path d="M13.73 21a2 2 0 0 1-3.46 0"></path></svg>
|
||||
@@ -174,6 +182,11 @@ const handleChangePassword = () => {
|
||||
isUserDropdownOpen.value = false
|
||||
}
|
||||
|
||||
const refreshCurrentPage = () => {
|
||||
// 刷新当前路由页面,不刷新整个浏览器
|
||||
router.replace(router.currentRoute.value.fullPath)
|
||||
}
|
||||
|
||||
interface MenuItem {
|
||||
title: string
|
||||
icon: string
|
||||
|
||||
@@ -2,69 +2,106 @@
|
||||
<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 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1/4">键名</th>
|
||||
<th class="w-1/3">值</th>
|
||||
<th>描述</th>
|
||||
<th class="text-right w-32">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="setting in settings" :key="setting.id" class="group transition-colors duration-200">
|
||||
<td class="font-mono text-xs text-art-accent">{{ setting.keyName }}</td>
|
||||
<td class="max-w-xs truncate text-white/80" :title="setting.value">{{ setting.value }}</td>
|
||||
<td class="text-art-muted text-sm">{{ setting.description }}</td>
|
||||
<td class="text-right">
|
||||
<div class="flex items-center justify-end gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button @click="editSetting(setting)" class="admin-btn-secondary py-1 px-3 text-xs">
|
||||
编辑
|
||||
</button>
|
||||
<button @click="deleteSetting(setting.keyName)" class="admin-btn-danger py-1 px-3 text-xs">
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</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>
|
||||
|
||||
<!-- Add/Edit Modal -->
|
||||
<div v-if="showModal" 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="closeModal"></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">{{ editingSetting ? '编辑配置' : '新增配置' }}</h2>
|
||||
<button @click="closeModal" class="text-white/40 hover:text-white transition-colors">
|
||||
<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="saveSetting" class="space-y-4">
|
||||
<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="form.keyName"
|
||||
:disabled="editingSetting"
|
||||
v-model="newForm.keyName"
|
||||
required
|
||||
class="admin-input font-mono disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="admin-input font-mono"
|
||||
placeholder="例如: site_title"
|
||||
>
|
||||
</div>
|
||||
@@ -74,7 +111,7 @@
|
||||
<input
|
||||
type="text"
|
||||
id="value"
|
||||
v-model="form.value"
|
||||
v-model="newForm.value"
|
||||
required
|
||||
class="admin-input"
|
||||
placeholder="配置项的值"
|
||||
@@ -85,7 +122,7 @@
|
||||
<label for="description" class="block text-xs font-medium text-art-muted uppercase tracking-wider">描述</label>
|
||||
<textarea
|
||||
id="description"
|
||||
v-model="form.description"
|
||||
v-model="newForm.description"
|
||||
rows="3"
|
||||
class="admin-input resize-none"
|
||||
placeholder="该配置项的用途说明"
|
||||
@@ -93,7 +130,7 @@
|
||||
</div>
|
||||
|
||||
<div class="pt-4 flex justify-end gap-3">
|
||||
<button type="button" @click="closeModal" class="admin-btn-secondary">取消</button>
|
||||
<button type="button" @click="closeAddModal" class="admin-btn-secondary">取消</button>
|
||||
<button type="submit" class="admin-btn-primary">保存</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -110,11 +147,10 @@ import { useToast } from '../../composables/useToast'
|
||||
|
||||
const toast = useToast()
|
||||
const settings = ref<Setting[]>([])
|
||||
const showModal = ref(false)
|
||||
const editingSetting = ref(false)
|
||||
const showAddModal = ref(false)
|
||||
const isSaving = ref(false)
|
||||
|
||||
const form = ref({
|
||||
id: 0,
|
||||
const newForm = ref({
|
||||
keyName: '',
|
||||
value: '',
|
||||
description: ''
|
||||
@@ -129,35 +165,44 @@ const fetchSettings = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const editSetting = (setting: Setting) => {
|
||||
editingSetting.value = true
|
||||
form.value = {
|
||||
id: setting.id,
|
||||
keyName: setting.keyName,
|
||||
value: setting.value,
|
||||
description: setting.description
|
||||
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
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const saveSetting = async () => {
|
||||
const saveNewSetting = async () => {
|
||||
try {
|
||||
if (editingSetting.value) {
|
||||
await updateSetting(form.value)
|
||||
toast.showToast('配置更新成功', 'success')
|
||||
} else {
|
||||
await createSetting({
|
||||
keyName: form.value.keyName,
|
||||
value: form.value.value,
|
||||
description: form.value.description
|
||||
})
|
||||
toast.showToast('配置创建成功', 'success')
|
||||
}
|
||||
closeModal()
|
||||
await createSetting({
|
||||
keyName: newForm.value.keyName,
|
||||
value: newForm.value.value,
|
||||
description: newForm.value.description
|
||||
})
|
||||
toast.showToast('配置创建成功', 'success')
|
||||
closeAddModal()
|
||||
fetchSettings()
|
||||
} catch (error) {
|
||||
console.error('Error saving setting:', error)
|
||||
toast.showToast(editingSetting.value ? '更新配置失败' : '创建配置失败', 'error')
|
||||
console.error('Error creating setting:', error)
|
||||
toast.showToast('创建配置失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,11 +219,9 @@ const deleteSetting = async (keyName: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
showModal.value = false
|
||||
editingSetting.value = false
|
||||
form.value = {
|
||||
id: 0,
|
||||
const closeAddModal = () => {
|
||||
showAddModal.value = false
|
||||
newForm.value = {
|
||||
keyName: '',
|
||||
value: '',
|
||||
description: ''
|
||||
|
||||
@@ -15,6 +15,7 @@ func GetWorks() ([]models.Work, error) {
|
||||
var works []models.Work
|
||||
err := config.DB.Model(&models.Work{}).
|
||||
Where("deleted_at = ?", 0).
|
||||
Order("id DESC").
|
||||
Find(&works).Error
|
||||
if err != nil {
|
||||
log.Printf("Error querying works: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user