修改配置编辑不了的问题
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { Loader2, RefreshCw, Save } from "lucide-react";
|
||||
import { selectDirtySettingsValues } from "@/lib/settings-client";
|
||||
import { crossfadeIn, pulseFeedback, revealChildren, runScopedMotion } from "@/lib/ui/motion";
|
||||
|
||||
type SettingsField = {
|
||||
@@ -23,7 +24,7 @@ type SettingsGroup = {
|
||||
};
|
||||
|
||||
type SettingsPayload = {
|
||||
restartRequired?: boolean;
|
||||
restartRequired?: boolean;
|
||||
services: {
|
||||
visual: boolean;
|
||||
evolink: boolean;
|
||||
@@ -57,6 +58,7 @@ type SettingsPayload = {
|
||||
export function SettingsPanel() {
|
||||
const [payload, setPayload] = useState<SettingsPayload | null>(null);
|
||||
const [values, setValues] = useState<Record<string, string>>({});
|
||||
const [dirtyKeys, setDirtyKeys] = useState<Set<string>>(() => new Set());
|
||||
const [activeTab, setActiveTab] = useState("status");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
@@ -92,12 +94,14 @@ export function SettingsPanel() {
|
||||
async function loadSettings() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
try {
|
||||
const response = await fetch("/api/settings", { cache: "no-store" });
|
||||
const nextPayload = await response.json();
|
||||
if (!response.ok) throw new Error(nextPayload.error || "读取设置失败");
|
||||
setPayload(nextPayload);
|
||||
setValues(valuesFromPayload(nextPayload));
|
||||
setDirtyKeys(new Set());
|
||||
if (activeTab !== "status" && !nextPayload.groups.some((group: SettingsGroup) => group.id === activeTab)) {
|
||||
setActiveTab(nextPayload.groups[0]?.id || "status");
|
||||
}
|
||||
@@ -109,6 +113,7 @@ export function SettingsPanel() {
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
if (dirtyKeys.size === 0) return;
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
@@ -116,13 +121,14 @@ export function SettingsPanel() {
|
||||
const response = await fetch("/api/settings", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ values })
|
||||
body: JSON.stringify({ values: selectDirtySettingsValues(values, dirtyKeys) })
|
||||
});
|
||||
const nextPayload = await response.json();
|
||||
if (!response.ok) throw new Error(nextPayload.error || "保存设置失败");
|
||||
setPayload(nextPayload);
|
||||
setValues(valuesFromPayload(nextPayload));
|
||||
setMessage(nextPayload.restartRequired ? "配置已保存,重启 Go 服务后生效。" : "配置已保存并应用到当前服务。");
|
||||
setDirtyKeys(new Set());
|
||||
setMessage(nextPayload.restartRequired ? "配置已保存,重启 Go 服务后生效。" : "配置已保存并应用到当前服务。");
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
@@ -130,6 +136,15 @@ export function SettingsPanel() {
|
||||
}
|
||||
}
|
||||
|
||||
function updateValue(key: string, value: string) {
|
||||
setValues((items) => ({ ...items, [key]: value }));
|
||||
setDirtyKeys((keys) => {
|
||||
const next = new Set(keys);
|
||||
next.add(key);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
if (loading && !payload) {
|
||||
return (
|
||||
<section className="panel settings-loading">
|
||||
@@ -164,7 +179,7 @@ export function SettingsPanel() {
|
||||
className="button primary"
|
||||
type="button"
|
||||
onClick={saveSettings}
|
||||
disabled={saving || (!fields.length && !payload?.engineAssignments.length)}
|
||||
disabled={saving || dirtyKeys.size === 0 || (!fields.length && !payload?.engineAssignments.length)}
|
||||
>
|
||||
{saving ? <Loader2 className="spin" size={18} /> : <Save size={18} />}
|
||||
保存
|
||||
@@ -194,7 +209,7 @@ export function SettingsPanel() {
|
||||
{field.secret ? <em>{field.configured ? "已配置" : "未配置"}</em> : null}
|
||||
</span>
|
||||
{field.type === "select" ? (
|
||||
<select value={values[field.key] ?? field.value} onChange={(event) => setValues((items) => ({ ...items, [field.key]: event.target.value }))}>
|
||||
<select value={values[field.key] ?? field.value} onChange={(event) => updateValue(field.key, event.target.value)}>
|
||||
{(field.options || []).map((option) => (
|
||||
<option value={option.value} key={option.value}>{option.label}</option>
|
||||
))}
|
||||
@@ -204,7 +219,7 @@ export function SettingsPanel() {
|
||||
type={field.secret ? "password" : field.type === "number" ? "number" : "text"}
|
||||
value={values[field.key] ?? ""}
|
||||
placeholder={field.secret && field.configured ? "留空保留当前密钥" : field.description || ""}
|
||||
onChange={(event) => setValues((items) => ({ ...items, [field.key]: event.target.value }))}
|
||||
onChange={(event) => updateValue(field.key, event.target.value)}
|
||||
/>
|
||||
)}
|
||||
<small>{field.key}</small>
|
||||
@@ -248,7 +263,7 @@ export function SettingsPanel() {
|
||||
<select
|
||||
className="settings-engine-control"
|
||||
value={values[assignment.field.key] ?? assignment.field.value}
|
||||
onChange={(event) => setValues((items) => ({ ...items, [assignment.field!.key]: event.target.value }))}
|
||||
onChange={(event) => updateValue(assignment.field!.key, event.target.value)}
|
||||
aria-label={`${assignment.label}创作引擎`}
|
||||
>
|
||||
{(assignment.field.options || []).map((option) => (
|
||||
|
||||
Reference in New Issue
Block a user