refactor(settings): simplify settings page layout and auto-save

Reorganize settings page structure and integrate autoSaveSettings.
- Move save button inline within Advanced tab content
- Remove sticky footer for cleaner layout
- Use autoSaveSettings for General tab settings
- Simplify dialog close behavior
- Refactor ImportExportSection layout
This commit is contained in:
YoVinchen
2025-11-22 15:35:08 +08:00
parent cfee4d6fcc
commit 4210b1547c
2 changed files with 71 additions and 114 deletions

View File

@@ -54,86 +54,63 @@ export function ImportExportSection({
</header>
<div className="space-y-4 rounded-xl glass-card p-6 border border-white/10">
{/* Export Section */}
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
<Save className="h-4 w-4" />
<span></span>
</div>
<Button
type="button"
className="w-full bg-primary/90 hover:bg-primary text-primary-foreground shadow-lg shadow-primary/20"
onClick={onExport}
>
<Save className="mr-2 h-4 w-4" />
{t("settings.exportConfig")}
</Button>
</div>
{/* Divider */}
<div className="border-t border-white/10" />
{/* Import Section */}
<div className="space-y-3">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
<FolderOpen className="h-4 w-4" />
<span></span>
</div>
<div className="flex flex-wrap items-center gap-2">
{/* Import and Export Buttons Side by Side */}
<div className="grid grid-cols-2 gap-4 items-stretch">
{/* Import Button */}
<div className="relative">
<Button
type="button"
variant="outline"
className="flex-1 min-w-[180px] hover:bg-black/5 dark:hover:bg-white/5 border-white/10"
onClick={onSelectFile}
className={`w-full h-auto py-3 px-4 bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white ${selectedFile && !isImporting ? "flex-col items-start" : "items-center"}`}
onClick={!selectedFile ? onSelectFile : onImport}
disabled={isImporting}
>
<FolderOpen className="mr-2 h-4 w-4" />
{t("settings.selectConfigFile")}
</Button>
<Button
type="button"
disabled={!selectedFile || isImporting}
className="bg-primary hover:bg-primary/90"
onClick={onImport}
>
{isImporting ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t("settings.importing")}
<div className="flex items-center gap-2 w-full justify-center">
{isImporting ? (
<Loader2 className="h-4 w-4 animate-spin flex-shrink-0" />
) : selectedFile ? (
<CheckCircle2 className="h-4 w-4 flex-shrink-0" />
) : (
<FolderOpen className="h-4 w-4 flex-shrink-0" />
)}
<span className="font-medium">
{isImporting
? t("settings.importing")
: selectedFile
? t("settings.import")
: t("settings.selectConfigFile")}
</span>
) : (
<>
<CheckCircle2 className="mr-2 h-4 w-4" />
{t("settings.import")}
</>
</div>
{selectedFile && !isImporting && (
<div className="mt-2 w-full text-left">
<p className="text-xs font-mono text-white/80 truncate">
📄 {selectedFileName}
</p>
</div>
)}
</Button>
{selectedFile ? (
<Button
{selectedFile && (
<button
type="button"
variant="ghost"
onClick={onClear}
className="hover:bg-black/5 dark:hover:bg-white/5"
className="absolute -top-2 -right-2 h-6 w-6 rounded-full bg-red-500 hover:bg-red-600 text-white flex items-center justify-center shadow-lg transition-colors z-10"
aria-label="Clear selection"
>
<XCircle className="mr-2 h-4 w-4" />
{t("common.clear")}
</Button>
) : null}
<XCircle className="h-4 w-4" />
</button>
)}
</div>
{selectedFile ? (
<div className="glass rounded-lg border border-white/10 p-3">
<p className="text-xs font-mono text-foreground/80 truncate">
📄 {selectedFileName}
</p>
</div>
) : (
<div className="glass rounded-lg border border-white/10 p-3">
<p className="text-xs text-muted-foreground italic">
{t("settings.noFileSelected")}
</p>
</div>
)}
{/* Export Button */}
<div>
<Button
type="button"
className="w-full h-full py-3 px-4 bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white items-center"
onClick={onExport}
>
<Save className="mr-2 h-4 w-4" />
{t("settings.exportConfig")}
</Button>
</div>
</div>
<ImportStatusMessage

View File

@@ -49,7 +49,7 @@ export function SettingsPage({
resetDirectory,
resetAppConfigDir,
saveSettings,
resetSettings,
autoSaveSettings,
requiresRestart,
acknowledgeRestart,
} = useSettings();
@@ -83,21 +83,6 @@ export function SettingsPage({
}
}, [requiresRestart]);
const closeDialog = useCallback(() => {
// 取消/直接关闭:恢复到初始设置(包括语言回滚)
resetSettings();
acknowledgeRestart();
clearSelection();
resetStatus();
onOpenChange(false);
}, [
acknowledgeRestart,
clearSelection,
onOpenChange,
resetSettings,
resetStatus,
]);
const closeAfterSave = useCallback(() => {
// 保存成功后关闭:不再重置语言,避免需要“保存两次”才生效
acknowledgeRestart();
@@ -118,7 +103,7 @@ export function SettingsPage({
} catch (error) {
console.error("[SettingsPage] Failed to save settings", error);
}
}, [closeDialog, saveSettings]);
}, [closeAfterSave, saveSettings]);
const handleRestartLater = useCallback(() => {
setShowRestartPrompt(false);
@@ -144,15 +129,13 @@ export function SettingsPage({
}, [closeAfterSave, t]);
// 通用设置即时保存(无需手动点击)
// 使用 autoSaveSettings 避免误触发系统 API开机自启、Claude 插件等)
const handleAutoSave = useCallback(
async (updates: Partial<SettingsFormState>) => {
if (!settings) return;
updateSettings(updates);
try {
const result = await saveSettings(updates, { silent: true });
if (result?.requiresRestart) {
setShowRestartPrompt(true);
}
await autoSaveSettings(updates);
} catch (error) {
console.error("[SettingsPage] Failed to autosave settings", error);
toast.error(
@@ -162,7 +145,7 @@ export function SettingsPage({
);
}
},
[saveSettings, settings, t, updateSettings],
[autoSaveSettings, settings, t, updateSettings],
);
const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]);
@@ -206,7 +189,7 @@ export function SettingsPage({
) : null}
</TabsContent>
<TabsContent value="advanced" className="space-y-6 mt-0">
<TabsContent value="advanced" className="space-y-6 mt-0 pb-6">
{settings ? (
<>
<DirectorySettings
@@ -233,6 +216,25 @@ export function SettingsPage({
onExport={exportConfig}
onClear={clearSelection}
/>
<div className="pt-6 border-t border-gray-200 dark:border-white/10">
<Button
onClick={handleSave}
className="w-full"
disabled={isSaving}
>
{isSaving ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t("settings.saving")}
</span>
) : (
<>
<Save className="mr-2 h-4 w-4" />
{t("common.save")}
</>
)}
</Button>
</div>
</>
) : null}
</TabsContent>
@@ -241,28 +243,6 @@ export function SettingsPage({
<AboutSection isPortable={isPortable} />
</TabsContent>
</div>
{activeTab === "advanced" ? (
<div className="flex-shrink-0 pt-6 border-t border-white/5 sticky bottom-0 bg-background/95 backdrop-blur-sm">
<Button
onClick={handleSave}
className="w-full bg-primary hover:bg-primary/90"
disabled={isSaving}
>
{isSaving ? (
<span className="inline-flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
{t("settings.saving")}
</span>
) : (
<>
<Save className="mr-2 h-4 w-4" />
{t("common.save")}
</>
)}
</Button>
</div>
) : null}
</Tabs>
)}