mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-12 02:06:37 +08:00
[优化] 界面优化
This commit is contained in:
@@ -1,19 +1,19 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import {useEffect, useState} from 'react';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import {useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
|
|
||||||
import { EmptyState } from '@/components/feedback/empty-state';
|
import {EmptyState} from '@/components/feedback/empty-state';
|
||||||
import { ErrorState } from '@/components/feedback/error-state';
|
import {ErrorState} from '@/components/feedback/error-state';
|
||||||
import { InlineMessage } from '@/components/feedback/inline-message';
|
import {InlineMessage} from '@/components/feedback/inline-message';
|
||||||
import { LoadingState } from '@/components/feedback/loading-state';
|
import {LoadingState} from '@/components/feedback/loading-state';
|
||||||
import { PageHeader } from '@/components/layout/page-header';
|
import {PageHeader} from '@/components/layout/page-header';
|
||||||
import { useAuth } from '@/components/providers/auth-provider';
|
import {useAuth} from '@/components/providers/auth-provider';
|
||||||
import { AppCard } from '@/components/ui/app-card';
|
import {AppCard} from '@/components/ui/app-card';
|
||||||
import { StatusBadge } from '@/components/ui/status-badge';
|
import {StatusBadge} from '@/components/ui/status-badge';
|
||||||
import { getConfigVersionPreview } from '@/features/config-versions/api/config-versions';
|
import {getConfigVersionPreview} from '@/features/config-versions/api/config-versions';
|
||||||
import { getOptions, updateOption } from '@/features/settings/api/settings';
|
import {getOptions, updateOption} from '@/features/settings/api/settings';
|
||||||
import type { OptionItem } from '@/features/settings/types';
|
import type {OptionItem} from '@/features/settings/types';
|
||||||
import {
|
import {
|
||||||
CodeBlock,
|
CodeBlock,
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
@@ -170,7 +170,7 @@ function extractMainConfigLines(content: string, matcher: RegExp) {
|
|||||||
|
|
||||||
export function PerformancePage() {
|
export function PerformancePage() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { user } = useAuth();
|
const {user} = useAuth();
|
||||||
const [activeTab, setActiveTab] = useState<PerformanceTab>('settings');
|
const [activeTab, setActiveTab] = useState<PerformanceTab>('settings');
|
||||||
const [feedback, setFeedback] = useState<FeedbackState | null>(null);
|
const [feedback, setFeedback] = useState<FeedbackState | null>(null);
|
||||||
const [busyKey, setBusyKey] = useState<string | null>(null);
|
const [busyKey, setBusyKey] = useState<string | null>(null);
|
||||||
@@ -265,7 +265,7 @@ export function PerformancePage() {
|
|||||||
try {
|
try {
|
||||||
await action();
|
await action();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setFeedback({ tone: 'danger', message: getErrorMessage(error) });
|
setFeedback({tone: 'danger', message: getErrorMessage(error)});
|
||||||
} finally {
|
} finally {
|
||||||
setBusyKey(null);
|
setBusyKey(null);
|
||||||
}
|
}
|
||||||
@@ -280,11 +280,11 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
queryClient.invalidateQueries({ queryKey: settingsQueryKey }),
|
queryClient.invalidateQueries({queryKey: settingsQueryKey}),
|
||||||
queryClient.invalidateQueries({ queryKey: previewQueryKey }),
|
queryClient.invalidateQueries({queryKey: previewQueryKey}),
|
||||||
queryClient.invalidateQueries({ queryKey: ['config-versions'] }),
|
queryClient.invalidateQueries({queryKey: ['config-versions']}),
|
||||||
]);
|
]);
|
||||||
setFeedback({ tone: 'success', message: successMessage });
|
setFeedback({tone: 'success', message: successMessage});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRuntimeSave = () => {
|
const handleRuntimeSave = () => {
|
||||||
@@ -564,7 +564,7 @@ export function PerformancePage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (optionsQuery.isLoading || previewQuery.isLoading) {
|
if (optionsQuery.isLoading || previewQuery.isLoading) {
|
||||||
return <LoadingState />;
|
return <LoadingState/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optionsQuery.isError) {
|
if (optionsQuery.isError) {
|
||||||
@@ -608,11 +608,11 @@ export function PerformancePage() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="性能"
|
title="性能"
|
||||||
description="从设置页拆出的 OpenResty 专属工作台。这里统一维护结构化性能参数和主配置模板。"
|
description="统一维护Nginx结构化性能参数和主配置模板。"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{feedback ? (
|
{feedback ? (
|
||||||
<InlineMessage tone={feedback.tone} message={feedback.message} />
|
<InlineMessage tone={feedback.tone} message={feedback.message}/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
@@ -650,15 +650,17 @@ export function PerformancePage() {
|
|||||||
{activeTab === 'settings' ? (
|
{activeTab === 'settings' ? (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
主配置校验
|
主配置校验
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<StatusBadge label="发布链路受管" variant="info" />
|
<StatusBadge label="发布链路受管" variant="info"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
当前预览规则数
|
当前预览规则数
|
||||||
</p>
|
</p>
|
||||||
@@ -666,7 +668,8 @@ export function PerformancePage() {
|
|||||||
{preview.route_count} 条
|
{preview.route_count} 条
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
当前模板
|
当前模板
|
||||||
</p>
|
</p>
|
||||||
@@ -955,7 +958,6 @@ export function PerformancePage() {
|
|||||||
/>
|
/>
|
||||||
<ToggleField
|
<ToggleField
|
||||||
label="proxy_buffering"
|
label="proxy_buffering"
|
||||||
description="是否启用 proxy_buffering。"
|
|
||||||
tooltip={performanceFieldTooltips.proxy_buffering}
|
tooltip={performanceFieldTooltips.proxy_buffering}
|
||||||
checked={performanceFields.OpenRestyProxyBufferingEnabled}
|
checked={performanceFields.OpenRestyProxyBufferingEnabled}
|
||||||
onChange={(checked) =>
|
onChange={(checked) =>
|
||||||
@@ -1014,84 +1016,6 @@ export function PerformancePage() {
|
|||||||
</AppCard>
|
</AppCard>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<AppCard
|
|
||||||
title="OpenResty 压缩"
|
|
||||||
description="单独维护 gzip 相关参数,避免与缓存配置混在一起。"
|
|
||||||
action={
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={handleGzipSave}
|
|
||||||
disabled={busyKey === 'performance-gzip'}
|
|
||||||
>
|
|
||||||
{busyKey === 'performance-gzip'
|
|
||||||
? '保存中...'
|
|
||||||
: '保存压缩设置'}
|
|
||||||
</PrimaryButton>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="space-y-5">
|
|
||||||
<div className="grid gap-5 md:grid-cols-2">
|
|
||||||
<ToggleField
|
|
||||||
label="gzip"
|
|
||||||
description="是否启用 gzip on。"
|
|
||||||
tooltip={performanceFieldTooltips.gzip}
|
|
||||||
checked={performanceFields.OpenRestyGzipEnabled}
|
|
||||||
onChange={(checked) =>
|
|
||||||
setPerformanceFields((previous) => ({
|
|
||||||
...previous,
|
|
||||||
OpenRestyGzipEnabled: checked,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
|
||||||
当前压缩预览
|
|
||||||
</p>
|
|
||||||
{gzipPreviewLines.length > 0 ? (
|
|
||||||
<CodeBlock className="mt-3 text-xs leading-6 whitespace-pre-wrap">
|
|
||||||
{gzipPreviewLines.join('\n')}
|
|
||||||
</CodeBlock>
|
|
||||||
) : (
|
|
||||||
<p className="mt-3 text-sm leading-6 text-[var(--foreground-secondary)]">
|
|
||||||
当前主配置预览中没有压缩指令。
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<ResourceField
|
|
||||||
label="gzip_min_length"
|
|
||||||
tooltip={performanceFieldTooltips.gzip_min_length}
|
|
||||||
>
|
|
||||||
<ResourceInput
|
|
||||||
type="number"
|
|
||||||
value={performanceFields.OpenRestyGzipMinLength}
|
|
||||||
onChange={(event) =>
|
|
||||||
setPerformanceFields((previous) => ({
|
|
||||||
...previous,
|
|
||||||
OpenRestyGzipMinLength: event.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ResourceField>
|
|
||||||
<ResourceField
|
|
||||||
label="gzip_comp_level"
|
|
||||||
tooltip={performanceFieldTooltips.gzip_comp_level}
|
|
||||||
>
|
|
||||||
<ResourceInput
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="9"
|
|
||||||
value={performanceFields.OpenRestyGzipCompLevel}
|
|
||||||
onChange={(event) =>
|
|
||||||
setPerformanceFields((previous) => ({
|
|
||||||
...previous,
|
|
||||||
OpenRestyGzipCompLevel: event.target.value,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ResourceField>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AppCard>
|
|
||||||
|
|
||||||
<AppCard
|
<AppCard
|
||||||
title="OpenResty 缓存"
|
title="OpenResty 缓存"
|
||||||
@@ -1128,7 +1052,8 @@ export function PerformancePage() {
|
|||||||
>
|
>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
缓存状态
|
缓存状态
|
||||||
</p>
|
</p>
|
||||||
@@ -1172,7 +1097,8 @@ export function PerformancePage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
当前缓存预览
|
当前缓存预览
|
||||||
</p>
|
</p>
|
||||||
@@ -1330,7 +1256,88 @@ export function PerformancePage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
|
<AppCard
|
||||||
|
title="OpenResty 压缩"
|
||||||
|
description="单独维护 gzip 相关参数,避免与缓存配置混在一起。"
|
||||||
|
action={
|
||||||
|
<PrimaryButton
|
||||||
|
type="button"
|
||||||
|
onClick={handleGzipSave}
|
||||||
|
disabled={busyKey === 'performance-gzip'}
|
||||||
|
>
|
||||||
|
{busyKey === 'performance-gzip'
|
||||||
|
? '保存中...'
|
||||||
|
: '保存压缩设置'}
|
||||||
|
</PrimaryButton>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="space-y-5">
|
||||||
|
<div className="grid gap-5 md:grid-cols-2">
|
||||||
|
<ToggleField
|
||||||
|
label="gzip"
|
||||||
|
description="是否启用 gzip on。"
|
||||||
|
tooltip={performanceFieldTooltips.gzip}
|
||||||
|
checked={performanceFields.OpenRestyGzipEnabled}
|
||||||
|
onChange={(checked) =>
|
||||||
|
setPerformanceFields((previous) => ({
|
||||||
|
...previous,
|
||||||
|
OpenRestyGzipEnabled: checked,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="rounded-2xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-4 py-4">
|
||||||
|
<p className="text-xs tracking-[0.2em] text-[var(--foreground-muted)] uppercase">
|
||||||
|
当前压缩预览
|
||||||
|
</p>
|
||||||
|
{gzipPreviewLines.length > 0 ? (
|
||||||
|
<CodeBlock className="mt-3 text-xs leading-6 whitespace-pre-wrap">
|
||||||
|
{gzipPreviewLines.join('\n')}
|
||||||
|
</CodeBlock>
|
||||||
|
) : (
|
||||||
|
<p className="mt-3 text-sm leading-6 text-[var(--foreground-secondary)]">
|
||||||
|
当前主配置预览中没有压缩指令。
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<ResourceField
|
||||||
|
label="gzip_min_length"
|
||||||
|
tooltip={performanceFieldTooltips.gzip_min_length}
|
||||||
|
>
|
||||||
|
<ResourceInput
|
||||||
|
type="number"
|
||||||
|
value={performanceFields.OpenRestyGzipMinLength}
|
||||||
|
onChange={(event) =>
|
||||||
|
setPerformanceFields((previous) => ({
|
||||||
|
...previous,
|
||||||
|
OpenRestyGzipMinLength: event.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ResourceField>
|
||||||
|
<ResourceField
|
||||||
|
label="gzip_comp_level"
|
||||||
|
tooltip={performanceFieldTooltips.gzip_comp_level}
|
||||||
|
>
|
||||||
|
<ResourceInput
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="9"
|
||||||
|
value={performanceFields.OpenRestyGzipCompLevel}
|
||||||
|
onChange={(event) =>
|
||||||
|
setPerformanceFields((previous) => ({
|
||||||
|
...previous,
|
||||||
|
OpenRestyGzipCompLevel: event.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ResourceField>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AppCard>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -1392,7 +1399,8 @@ export function PerformancePage() {
|
|||||||
variant="info"
|
variant="info"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CodeBlock className="max-h-[560px] overflow-auto text-xs leading-6 whitespace-pre-wrap">
|
<CodeBlock
|
||||||
|
className="max-h-[560px] overflow-auto text-xs leading-6 whitespace-pre-wrap">
|
||||||
{preview.main_config}
|
{preview.main_config}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1064,23 +1064,6 @@ export function SettingsPage() {
|
|||||||
/>
|
/>
|
||||||
</ResourceField>
|
</ResourceField>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
<AppCard
|
|
||||||
title="性能页入口"
|
|
||||||
description="OpenResty 连接、缓冲、压缩、缓存以及主配置模板编辑已迁移到侧边栏“性能”页面。"
|
|
||||||
action={
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={() => window.location.assign('/performance')}
|
|
||||||
>
|
|
||||||
打开性能页
|
|
||||||
</PrimaryButton>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<p className="text-sm leading-6 text-[var(--foreground-secondary)]">
|
|
||||||
设置页保留 Agent、系统、限流和品牌配置;OpenResty
|
|
||||||
相关项已拆分为独立工作台,支持“设置 / 编辑”双 Tab 操作。
|
|
||||||
</p>
|
|
||||||
</AppCard>
|
|
||||||
<AppCard
|
<AppCard
|
||||||
title="Discovery Token 与部署命令"
|
title="Discovery Token 与部署命令"
|
||||||
description="适用于新节点首次接入。可直接复制一键安装命令。"
|
description="适用于新节点首次接入。可直接复制一键安装命令。"
|
||||||
@@ -1527,23 +1510,6 @@ export function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</AppCard>
|
</AppCard>
|
||||||
</div>
|
</div>
|
||||||
<AppCard
|
|
||||||
title="性能页说明"
|
|
||||||
description="OpenResty 反代缓冲、压缩、缓存和主配置模板编辑已迁移到独立“性能”页面。"
|
|
||||||
action={
|
|
||||||
<PrimaryButton
|
|
||||||
type="button"
|
|
||||||
onClick={() => window.location.assign('/performance')}
|
|
||||||
>
|
|
||||||
前往性能页
|
|
||||||
</PrimaryButton>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<p className="text-sm leading-6 text-[var(--foreground-secondary)]">
|
|
||||||
这样可以把 Agent 运行参数、系统开关与 OpenResty
|
|
||||||
专项配置拆开,减少设置页的混杂信息量。
|
|
||||||
</p>
|
|
||||||
</AppCard>
|
|
||||||
<AppCard
|
<AppCard
|
||||||
title="请求限流设置"
|
title="请求限流设置"
|
||||||
description="按来源 IP 生效,保存后立即影响 Web、API、上传下载及登录注册等敏感接口。时间单位均为秒。"
|
description="按来源 IP 生效,保存后立即影响 Web、API、上传下载及登录注册等敏感接口。时间单位均为秒。"
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ export function DangerButton({
|
|||||||
|
|
||||||
interface ToggleFieldProps {
|
interface ToggleFieldProps {
|
||||||
label: string;
|
label: string;
|
||||||
description: string;
|
description?: string;
|
||||||
checked: boolean;
|
checked: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
@@ -185,9 +185,11 @@ export function ToggleField({
|
|||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
{tooltip ? <FieldTooltip content={tooltip} /> : null}
|
{tooltip ? <FieldTooltip content={tooltip} /> : null}
|
||||||
</span>
|
</span>
|
||||||
|
{description ? (
|
||||||
<span className="block text-xs leading-5 text-[var(--foreground-secondary)]">
|
<span className="block text-xs leading-5 text-[var(--foreground-secondary)]">
|
||||||
{description}
|
{description}
|
||||||
</span>
|
</span>
|
||||||
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user