133 lines
3.8 KiB
Vue
133 lines
3.8 KiB
Vue
<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>
|