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> </header>
<div className="space-y-4 rounded-xl glass-card p-6 border border-white/10"> <div className="space-y-4 rounded-xl glass-card p-6 border border-white/10">
{/* Export Section */} {/* Import and Export Buttons Side by Side */}
<div className="space-y-3"> <div className="grid grid-cols-2 gap-4 items-stretch">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground"> {/* Import Button */}
<Save className="h-4 w-4" /> <div className="relative">
<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">
<Button <Button
type="button" type="button"
variant="outline" 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"}`}
className="flex-1 min-w-[180px] hover:bg-black/5 dark:hover:bg-white/5 border-white/10" onClick={!selectedFile ? onSelectFile : onImport}
onClick={onSelectFile} disabled={isImporting}
> >
<FolderOpen className="mr-2 h-4 w-4" /> <div className="flex items-center gap-2 w-full justify-center">
{t("settings.selectConfigFile")} {isImporting ? (
</Button> <Loader2 className="h-4 w-4 animate-spin flex-shrink-0" />
<Button ) : selectedFile ? (
type="button" <CheckCircle2 className="h-4 w-4 flex-shrink-0" />
disabled={!selectedFile || isImporting} ) : (
className="bg-primary hover:bg-primary/90" <FolderOpen className="h-4 w-4 flex-shrink-0" />
onClick={onImport} )}
> <span className="font-medium">
{isImporting ? ( {isImporting
<span className="inline-flex items-center gap-2"> ? t("settings.importing")
<Loader2 className="h-4 w-4 animate-spin" /> : selectedFile
{t("settings.importing")} ? t("settings.import")
: t("settings.selectConfigFile")}
</span> </span>
) : ( </div>
<> {selectedFile && !isImporting && (
<CheckCircle2 className="mr-2 h-4 w-4" /> <div className="mt-2 w-full text-left">
{t("settings.import")} <p className="text-xs font-mono text-white/80 truncate">
</> 📄 {selectedFileName}
</p>
</div>
)} )}
</Button> </Button>
{selectedFile ? ( {selectedFile && (
<Button <button
type="button" type="button"
variant="ghost"
onClick={onClear} 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" /> <XCircle className="h-4 w-4" />
{t("common.clear")} </button>
</Button> )}
) : null}
</div> </div>
{selectedFile ? ( {/* Export Button */}
<div className="glass rounded-lg border border-white/10 p-3"> <div>
<p className="text-xs font-mono text-foreground/80 truncate"> <Button
📄 {selectedFileName} type="button"
</p> 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"
</div> onClick={onExport}
) : ( >
<div className="glass rounded-lg border border-white/10 p-3"> <Save className="mr-2 h-4 w-4" />
<p className="text-xs text-muted-foreground italic"> {t("settings.exportConfig")}
{t("settings.noFileSelected")} </Button>
</p> </div>
</div>
)}
</div> </div>
<ImportStatusMessage <ImportStatusMessage

View File

@@ -49,7 +49,7 @@ export function SettingsPage({
resetDirectory, resetDirectory,
resetAppConfigDir, resetAppConfigDir,
saveSettings, saveSettings,
resetSettings, autoSaveSettings,
requiresRestart, requiresRestart,
acknowledgeRestart, acknowledgeRestart,
} = useSettings(); } = useSettings();
@@ -83,21 +83,6 @@ export function SettingsPage({
} }
}, [requiresRestart]); }, [requiresRestart]);
const closeDialog = useCallback(() => {
// 取消/直接关闭:恢复到初始设置(包括语言回滚)
resetSettings();
acknowledgeRestart();
clearSelection();
resetStatus();
onOpenChange(false);
}, [
acknowledgeRestart,
clearSelection,
onOpenChange,
resetSettings,
resetStatus,
]);
const closeAfterSave = useCallback(() => { const closeAfterSave = useCallback(() => {
// 保存成功后关闭:不再重置语言,避免需要“保存两次”才生效 // 保存成功后关闭:不再重置语言,避免需要“保存两次”才生效
acknowledgeRestart(); acknowledgeRestart();
@@ -118,7 +103,7 @@ export function SettingsPage({
} catch (error) { } catch (error) {
console.error("[SettingsPage] Failed to save settings", error); console.error("[SettingsPage] Failed to save settings", error);
} }
}, [closeDialog, saveSettings]); }, [closeAfterSave, saveSettings]);
const handleRestartLater = useCallback(() => { const handleRestartLater = useCallback(() => {
setShowRestartPrompt(false); setShowRestartPrompt(false);
@@ -144,15 +129,13 @@ export function SettingsPage({
}, [closeAfterSave, t]); }, [closeAfterSave, t]);
// 通用设置即时保存(无需手动点击) // 通用设置即时保存(无需手动点击)
// 使用 autoSaveSettings 避免误触发系统 API开机自启、Claude 插件等)
const handleAutoSave = useCallback( const handleAutoSave = useCallback(
async (updates: Partial<SettingsFormState>) => { async (updates: Partial<SettingsFormState>) => {
if (!settings) return; if (!settings) return;
updateSettings(updates); updateSettings(updates);
try { try {
const result = await saveSettings(updates, { silent: true }); await autoSaveSettings(updates);
if (result?.requiresRestart) {
setShowRestartPrompt(true);
}
} catch (error) { } catch (error) {
console.error("[SettingsPage] Failed to autosave settings", error); console.error("[SettingsPage] Failed to autosave settings", error);
toast.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]); const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]);
@@ -206,7 +189,7 @@ export function SettingsPage({
) : null} ) : null}
</TabsContent> </TabsContent>
<TabsContent value="advanced" className="space-y-6 mt-0"> <TabsContent value="advanced" className="space-y-6 mt-0 pb-6">
{settings ? ( {settings ? (
<> <>
<DirectorySettings <DirectorySettings
@@ -233,6 +216,25 @@ export function SettingsPage({
onExport={exportConfig} onExport={exportConfig}
onClear={clearSelection} 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} ) : null}
</TabsContent> </TabsContent>
@@ -241,28 +243,6 @@ export function SettingsPage({
<AboutSection isPortable={isPortable} /> <AboutSection isPortable={isPortable} />
</TabsContent> </TabsContent>
</div> </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> </Tabs>
)} )}