refactor(ui): unify dialog styles and improve layout consistency

Standardize dialog and panel components across the application.
- Update dialog background to use semantic color tokens
- Adjust FullScreenPanel max-width to 56rem for better alignment
- Add drag region and prevent body scroll in full-screen panels
- Optimize button sizes and spacing in panel headers
- Apply consistent styling to all dialog-based components
This commit is contained in:
YoVinchen
2025-11-22 14:03:09 +08:00
parent 2b0bc73276
commit e8d4397b3a
10 changed files with 65 additions and 64 deletions

View File

@@ -348,7 +348,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
onClose={onClose} onClose={onClose}
footer={footer} footer={footer}
> >
<div className="rounded-xl border border-border-default bg-card px-4 py-3 shadow-sm flex items-center justify-between gap-4"> <div className="glass rounded-xl border border-white/10 px-6 py-4 flex items-center justify-between gap-4">
<div className="space-y-1"> <div className="space-y-1">
<p className="text-sm font-medium leading-none text-foreground"> <p className="text-sm font-medium leading-none text-foreground">
{t("usageScript.enableUsageQuery")} {t("usageScript.enableUsageQuery")}
@@ -369,7 +369,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
{script.enabled && ( {script.enabled && (
<div className="space-y-6"> <div className="space-y-6">
{/* 预设模板选择 */} {/* 预设模板选择 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm"> <div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex flex-wrap items-center justify-between gap-2"> <div className="flex flex-wrap items-center justify-between gap-2">
<Label className="text-base font-medium"> <Label className="text-base font-medium">
{t("usageScript.presetTemplate")} {t("usageScript.presetTemplate")}
@@ -423,7 +423,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
} }
placeholder="sk-xxxxx" placeholder="sk-xxxxx"
autoComplete="off" autoComplete="off"
className="bg-card border-border-default" className="border-white/10"
/> />
{script.apiKey && ( {script.apiKey && (
<button <button
@@ -457,7 +457,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
} }
placeholder="https://api.example.com" placeholder="https://api.example.com"
autoComplete="off" autoComplete="off"
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
</> </>
@@ -476,7 +476,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
} }
placeholder="https://api.newapi.com" placeholder="https://api.newapi.com"
autoComplete="off" autoComplete="off"
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
@@ -499,7 +499,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
"usageScript.accessTokenPlaceholder", "usageScript.accessTokenPlaceholder",
)} )}
autoComplete="off" autoComplete="off"
className="bg-card border-border-default" className="border-white/10"
/> />
{script.accessToken && ( {script.accessToken && (
<button <button
@@ -537,7 +537,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
} }
placeholder={t("usageScript.userIdPlaceholder")} placeholder={t("usageScript.userIdPlaceholder")}
autoComplete="off" autoComplete="off"
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
</> </>
@@ -548,7 +548,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div> </div>
{/* 脚本配置 */} {/* 脚本配置 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm"> <div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h4 className="text-base font-medium text-foreground"> <h4 className="text-base font-medium text-foreground">
{t("usageScript.scriptConfig")} {t("usageScript.scriptConfig")}
@@ -574,7 +574,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}); });
}} }}
placeholder={t("usageScript.requestUrlPlaceholder")} placeholder={t("usageScript.requestUrlPlaceholder")}
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
@@ -597,7 +597,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}); });
}} }}
placeholder="GET / POST" placeholder="GET / POST"
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
@@ -622,7 +622,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
timeout: validateTimeout(e.target.value), timeout: validateTimeout(e.target.value),
}) })
} }
className="bg-card border-border-default" className="border-white/10"
/> />
</div> </div>
</div> </div>
@@ -706,7 +706,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
), ),
}) })
} }
className="bg-card border-border-default" className="border-white/10"
/> />
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t("usageScript.autoQueryIntervalHint")} {t("usageScript.autoQueryIntervalHint")}
@@ -716,7 +716,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div> </div>
{/* 提取器代码 */} {/* 提取器代码 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm"> <div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Label className="text-base font-medium"> <Label className="text-base font-medium">
{t("usageScript.extractorCode")} {t("usageScript.extractorCode")}
@@ -736,12 +736,12 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div> </div>
{/* 帮助信息 */} {/* 帮助信息 */}
<div className="rounded-xl border border-border-default bg-card p-4 shadow-sm text-sm text-foreground/90"> <div className="glass rounded-xl border border-white/10 p-6 text-sm text-foreground/90">
<h4 className="font-medium mb-2">{t("usageScript.scriptHelp")}</h4> <h4 className="font-medium mb-2">{t("usageScript.scriptHelp")}</h4>
<div className="space-y-3 text-xs"> <div className="space-y-3 text-xs">
<div> <div>
<strong>{t("usageScript.configFormat")}</strong> <strong>{t("usageScript.configFormat")}</strong>
<pre className="mt-1 p-2 bg-muted text-foreground rounded border border-border-default text-[10px] overflow-x-auto"> <pre className="mt-1 p-2 bg-black/20 text-foreground rounded border border-white/10 text-[10px] overflow-x-auto">
{`({ {`({
request: { request: {
url: "{{baseUrl}}/api/usage", url: "{{baseUrl}}/api/usage",

View File

@@ -23,6 +23,15 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
children, children,
footer, footer,
}) => { }) => {
React.useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
if (!isOpen) return null; if (!isOpen) return null;
return createPortal( return createPortal(
@@ -32,26 +41,21 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
> >
{/* Header */} {/* Header */}
<div <div
className="flex-shrink-0 py-4 border-b border-border-default" className="flex-shrink-0 py-3 border-b border-border-default"
style={{ backgroundColor: "hsl(var(--background))" }} style={{ backgroundColor: "hsl(var(--background))" }}
> >
<div className="mx-auto max-w-[60rem] px-6 flex items-center gap-4"> <div className="h-4 w-full" data-tauri-drag-region />
<Button <div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
type="button" <Button type="button" variant="outline" size="icon" onClick={onClose}>
variant="ghost" <ArrowLeft className="h-4 w-4" />
size="icon"
onClick={onClose}
className="hover:bg-black/5 dark:hover:bg-white/5"
>
<ArrowLeft className="h-5 w-5" />
</Button> </Button>
<h2 className="text-lg font-semibold text-foreground">{title}</h2> <h2 className="text-lg font-semibold text-foreground">{title}</h2>
</div> </div>
</div> </div>
{/* Content */} {/* Content */}
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto scroll-overlay">
<div className="mx-auto max-w-[60rem] px-6 py-6 space-y-6 w-full"> <div className="mx-auto max-w-[56rem] px-6 py-6 space-y-6 w-full">
{children} {children}
</div> </div>
</div> </div>
@@ -62,7 +66,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
className="flex-shrink-0 py-4 border-t border-border-default" className="flex-shrink-0 py-4 border-t border-border-default"
style={{ backgroundColor: "hsl(var(--background))" }} style={{ backgroundColor: "hsl(var(--background))" }}
> >
<div className="mx-auto max-w-[60rem] px-6 flex items-center justify-end gap-3"> <div className="mx-auto max-w-[56rem] px-6 flex items-center justify-end gap-3">
{footer} {footer}
</div> </div>
</div> </div>

View File

@@ -115,7 +115,7 @@ const UnifiedMcpPanel = React.forwardRef<
}; };
return ( return (
<div className="mx-auto max-w-[60rem] px-6 flex flex-col h-[calc(100vh-8rem)] overflow-hidden"> <div className="mx-auto max-w-[56rem] px-6 flex flex-col h-[calc(100vh-8rem)] overflow-hidden">
{/* Info Section */} {/* Info Section */}
<div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6"> <div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6">
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">

View File

@@ -80,7 +80,7 @@ const PromptPanel = React.forwardRef<PromptPanelHandle, PromptPanelProps>(
const enabledPrompt = promptEntries.find(([_, p]) => p.enabled); const enabledPrompt = promptEntries.find(([_, p]) => p.enabled);
return ( return (
<div className="mx-auto max-w-[60rem] flex flex-col h-[calc(100vh-8rem)] px-6"> <div className="mx-auto max-w-[56rem] flex flex-col h-[calc(100vh-8rem)] px-6">
<div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6"> <div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6">
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{t("prompts.count", { count: promptEntries.length })} ·{" "} {t("prompts.count", { count: promptEntries.length })} ·{" "}

View File

@@ -111,6 +111,16 @@ export function EditProviderDialog({
isOpen={open} isOpen={open}
title={t("provider.editProvider")} title={t("provider.editProvider")}
onClose={() => onOpenChange(false)} onClose={() => onOpenChange(false)}
footer={
<Button
type="submit"
form="provider-form"
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
<Save className="h-4 w-4 mr-2" />
{t("common.save")}
</Button>
}
> >
<ProviderForm <ProviderForm
appId={appId} appId={appId}
@@ -131,16 +141,6 @@ export function EditProviderDialog({
}} }}
showButtons={false} showButtons={false}
/> />
<div className="flex justify-end pt-6">
<Button
type="submit"
form="provider-form"
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
<Save className="h-4 w-4 mr-2" />
{t("common.save")}
</Button>
</div>
</FullScreenPanel> </FullScreenPanel>
); );
} }

View File

@@ -69,25 +69,22 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) {
className="p-0 sm:rounded-none" className="p-0 sm:rounded-none"
> >
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<div className="flex-shrink-0 px-6 py-4 flex items-center gap-4 border-b border-border-default bg-muted/40"> <div className="flex-shrink-0 py-4 border-b border-border-default bg-muted/40">
<DialogClose asChild> <div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
<Button <DialogClose asChild>
type="button" <Button type="button" variant="outline" size="icon">
variant="ghost" <ArrowLeft className="h-4 w-4" />
size="icon" </Button>
className="hover:bg-black/5 dark:hover:bg-white/5" </DialogClose>
> <p className="text-lg font-semibold leading-tight">
<ArrowLeft className="h-5 w-5" /> {t("providerIcon.selectIcon", {
</Button> defaultValue: "选择图标",
</DialogClose> })}
<p className="text-lg font-semibold leading-tight"> </p>
{t("providerIcon.selectIcon", { </div>
defaultValue: "选择图标",
})}
</p>
</div> </div>
<div className="flex-1 overflow-y-auto px-6 py-6"> <div className="flex-1 overflow-y-auto">
<div className="space-y-6 max-w-5xl mx-auto w-full"> <div className="space-y-6 mx-auto max-w-[56rem] px-6 py-6 w-full">
<IconPicker <IconPicker
value={currentIcon} value={currentIcon}
onValueChange={handleIconSelect} onValueChange={handleIconSelect}

View File

@@ -99,7 +99,7 @@ export function ProviderPresetSelector({
return `${baseClass} bg-blue-500 text-white dark:bg-blue-600`; return `${baseClass} bg-blue-500 text-white dark:bg-blue-600`;
} }
return `${baseClass} bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700`; return `${baseClass} bg-accent text-muted-foreground hover:bg-accent/80`;
}; };
// 获取预设按钮的内联样式(用于自定义背景色) // 获取预设按钮的内联样式(用于自定义背景色)
@@ -128,7 +128,7 @@ export function ProviderPresetSelector({
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${ className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedPresetId === "custom" selectedPresetId === "custom"
? "bg-blue-500 text-white dark:bg-blue-600" ? "bg-blue-500 text-white dark:bg-blue-600"
: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700" : "bg-accent text-muted-foreground hover:bg-accent/80"
}`} }`}
> >
{t("providerPreset.custom")} {t("providerPreset.custom")}

View File

@@ -168,7 +168,7 @@ export function SettingsPage({
const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]); const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]);
return ( return (
<div className="mx-auto max-w-[60rem] flex flex-col h-[calc(100vh-8rem)] px-6"> <div className="mx-auto max-w-[56rem] flex flex-col h-[calc(100vh-8rem)] px-6">
{isBusy ? ( {isBusy ? (
<div className="flex flex-1 items-center justify-center"> <div className="flex flex-1 items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" /> <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
@@ -272,7 +272,7 @@ export function SettingsPage({
> >
<DialogContent <DialogContent
zIndex="alert" zIndex="alert"
className="max-w-md glass-card border-white/10" className="max-w-md glass border-white/10"
> >
<DialogHeader> <DialogHeader>
<DialogTitle>{t("settings.restartRequired")}</DialogTitle> <DialogTitle>{t("settings.restartRequired")}</DialogTitle>

View File

@@ -168,7 +168,7 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
{/* 技能网格(可滚动详情区域) */} {/* 技能网格(可滚动详情区域) */}
<div className="flex-1 min-h-0 overflow-y-auto animate-fade-in"> <div className="flex-1 min-h-0 overflow-y-auto animate-fade-in">
<div className="mx-auto max-w-[60rem] px-6 py-4"> <div className="mx-auto max-w-[56rem] px-6 py-4">
{loading ? ( {loading ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" /> <RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />

View File

@@ -65,7 +65,7 @@ const DialogContent = React.forwardRef<
const variantClass = { const variantClass = {
default: default:
"fixed left-1/2 top-1/2 flex flex-col w-full max-w-lg max-h-[90vh] translate-x-[-50%] translate-y-[-50%] border border-border-default bg-white dark:bg-gray-900 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", "fixed left-1/2 top-1/2 flex flex-col w-full max-w-lg max-h-[90vh] translate-x-[-50%] translate-y-[-50%] border border-border-default bg-background text-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
fullscreen: fullscreen:
"fixed inset-0 flex flex-col w-screen h-screen translate-x-0 translate-y-0 bg-background text-foreground p-0 sm:rounded-none shadow-none", "fixed inset-0 flex flex-col w-screen h-screen translate-x-0 translate-y-0 bg-background text-foreground p-0 sm:rounded-none shadow-none",
}[variant]; }[variant];