Files
gpt-load/web/src/views/Settings.vue
2025-07-06 16:15:40 +08:00

140 lines
3.7 KiB
Vue

<script setup lang="ts">
import { settingsApi, type SettingCategory } from "@/api/settings";
import { HelpCircle, Save } from "@vicons/ionicons5";
import {
NButton,
NCard,
NForm,
NFormItem,
NGrid,
NGridItem,
NIcon,
NInput,
NInputNumber,
NSpace,
NTooltip,
useMessage,
} from "naive-ui";
import { ref } from "vue";
const settingList = ref<SettingCategory[]>([]);
const formRef = ref();
const form = ref<Record<string, string | number>>({});
const isSaving = ref(false);
const message = useMessage();
fetchSettings();
async function fetchSettings() {
try {
const data = await settingsApi.getSettings();
settingList.value = data || [];
initForm();
} catch (_error) {
message.error("获取设置失败");
}
}
function initForm() {
form.value = settingList.value.reduce((acc: Record<string, string | number>, category) => {
category.settings?.forEach(setting => {
acc[setting.key] = setting.value;
});
return acc;
}, {});
}
async function handleSubmit() {
if (isSaving.value) {
return;
}
try {
await formRef.value.validate();
isSaving.value = true;
await settingsApi.updateSettings(form.value);
await fetchSettings();
} finally {
isSaving.value = false;
}
}
</script>
<template>
<n-space vertical size="large">
<n-form ref="formRef" :model="form" label-placement="top">
<n-space vertical size="large">
<n-card
size="small"
v-for="category in settingList"
:key="category.category_name"
:title="category.category_name"
hoverable
bordered
>
<n-grid :x-gap="24" :y-gap="24" responsive="screen" cols="1 s:2 m:2 l:3 xl:4">
<n-grid-item v-for="item in category.settings" :key="item.key">
<n-form-item
:path="item.key"
:rule="{ required: true, message: `请输入 ${item.name}` }"
>
<template #label>
<n-space align="center" :size="4" :wrap-item="false">
<n-tooltip trigger="hover" placement="top">
<template #trigger>
<n-icon
:component="HelpCircle"
:size="16"
style="cursor: help; color: #9ca3af"
/>
</template>
{{ item.description }}
</n-tooltip>
<span>{{ item.name }}</span>
</n-space>
</template>
<n-input-number
v-if="item.type === 'int'"
v-model:value="form[item.key] as number"
:min="
item.min_value !== undefined && item.min_value >= 0 ? item.min_value : undefined
"
placeholder="请输入数值"
clearable
style="width: 100%"
/>
<n-input
v-else
v-model:value="form[item.key] as string"
placeholder="请输入内容"
clearable
/>
</n-form-item>
</n-grid-item>
</n-grid>
</n-card>
</n-space>
</n-form>
<div
v-if="settingList.length > 0"
style="display: flex; justify-content: center; padding-top: 12px"
>
<n-button
type="primary"
size="large"
:loading="isSaving"
:disabled="isSaving"
@click="handleSubmit"
style="min-width: 200px"
>
<template #icon>
<n-icon :component="Save" />
</template>
{{ isSaving ? "保存中..." : "保存设置" }}
</n-button>
</div>
</n-space>
</template>