feat: add system settings panel

This commit is contained in:
Coldsmile_7
2026-06-08 22:50:18 +08:00
parent f7a3501262
commit d5f7e06e4e
5 changed files with 245 additions and 15 deletions
+132
View File
@@ -0,0 +1,132 @@
<script setup lang="ts">
import { computed, reactive, watch } from "vue";
import type { SystemSetting } from "../types";
const props = defineProps<{
settings: SystemSetting[];
saveState: "idle" | "saving" | "saved" | "error";
}>();
defineEmits<{
saveSettings: [settings: SystemSetting[]];
}>();
const draft = reactive<Record<string, SystemSetting>>({});
const groups = [
{
title: "基础",
eyebrow: "general",
keys: ["site.title"],
},
{
title: "认证",
eyebrow: "auth",
keys: ["auth.allow_email_login", "auth.allow_github_login", "auth.allow_registration"],
},
{
title: "SMTP",
eyebrow: "mail",
keys: ["smtp.host", "smtp.port", "smtp.username", "smtp.password", "smtp.sender_email"],
},
{
title: "GitHub",
eyebrow: "github",
keys: ["github.api_base_url", "github.web_base_url", "github.oauth_client_id", "github.oauth_client_secret"],
},
];
const labels: Record<string, string> = {
"site.title": "CMS 标题",
"auth.allow_email_login": "允许邮箱登录",
"auth.allow_github_login": "允许 GitHub 登录",
"auth.allow_registration": "开放注册",
"smtp.host": "SMTP 地址",
"smtp.port": "SMTP 端口",
"smtp.username": "SMTP 用户名",
"smtp.password": "SMTP 密码",
"smtp.sender_email": "发件人邮箱",
"github.api_base_url": "GitHub API 地址",
"github.web_base_url": "GitHub Web 地址",
"github.oauth_client_id": "OAuth Client ID",
"github.oauth_client_secret": "OAuth Client Secret",
};
const booleanKeys = new Set([
"auth.allow_email_login",
"auth.allow_github_login",
"auth.allow_registration",
]);
watch(
() => props.settings,
(settings) => {
Object.keys(draft).forEach((key) => delete draft[key]);
settings.forEach((setting) => {
draft[setting.key] = { ...setting };
});
},
{ immediate: true },
);
const draftSettings = computed(() => Object.values(draft));
function getSetting(key: string) {
if (!draft[key]) {
draft[key] = {
key,
value: "",
encrypted: key.includes("password") || key.includes("secret"),
updatedAt: "",
};
}
return draft[key];
}
function isBooleanKey(key: string) {
return booleanKeys.has(key);
}
</script>
<template>
<section class="panel is-visible">
<div class="settings-toolbar">
<p class="save-status" :class="`is-${saveState}`">
<template v-if="saveState === 'saved'">系统设置已保存</template>
<template v-else-if="saveState === 'error'">保存失败请查看日志</template>
<template v-else-if="saveState === 'saving'">正在保存系统设置</template>
<template v-else>系统设置保存到 SQLite 数据库</template>
</p>
<button class="primary-button" type="button" @click="$emit('saveSettings', draftSettings)">
{{ saveState === "saving" ? "保存中" : "保存设置" }}
</button>
</div>
<div class="settings-grid">
<section v-for="group in groups" :key="group.title" class="management-card">
<p class="eyebrow">{{ group.eyebrow }}</p>
<h3>{{ group.title }}</h3>
<label
v-for="key in group.keys"
:key="key"
:class="{ 'toggle-row': isBooleanKey(key) }"
>
<span>{{ labels[key] || key }}</span>
<input
v-if="isBooleanKey(key)"
:checked="getSetting(key).value === 'true'"
type="checkbox"
@change="getSetting(key).value = ($event.target as HTMLInputElement).checked ? 'true' : 'false'"
/>
<input
v-else
v-model="getSetting(key).value"
:placeholder="getSetting(key).encrypted ? '留空表示不修改敏感值' : ''"
:type="getSetting(key).encrypted ? 'password' : 'text'"
/>
</label>
</section>
</div>
</section>
</template>