fix(waf): 调整编辑器返回与格式化布局按钮位置

返回置于标题上方;格式化布局移至保存按钮左侧。
This commit is contained in:
ryan
2026-07-19 12:52:12 +08:00
parent fd9348b7bd
commit 60d6e3e846
2 changed files with 77 additions and 77 deletions
@@ -25,10 +25,9 @@ import {
type NodeChange,
type ReactFlowInstance,
} from '@xyflow/react';
import { AlignHorizontalSpaceAround, Trash2 } from 'lucide-react';
import { Trash2 } from 'lucide-react';
import '@xyflow/react/dist/style.css';
import { Button } from '@/components/ui/button';
import type { WAFRuleEdge, WAFRuleGraph } from '@/lib/services/openflare';
import { cn } from '@/lib/utils';
@@ -39,7 +38,6 @@ import {
isConnectionAllowed,
isPersistentEdgeChange,
} from './editor-behavior';
import { layoutRuleGraph } from './graph-layout';
import {
type GraphIssue,
removeEdgeFromGraph,
@@ -328,18 +326,6 @@ export function RuleFlowCanvas({
[],
);
const formatLayout = useCallback(() => {
const next = layoutRuleGraph(graph);
onGraphChange(next);
setContextMenu(null);
requestAnimationFrame(() => {
void instance.current?.fitView({
...initialFitViewOptions,
duration: 280,
});
});
}, [graph, onGraphChange]);
const contextNode =
contextMenu?.kind === 'node'
? graph.nodes.find((node) => node.id === contextMenu.id)
@@ -357,18 +343,8 @@ export function RuleFlowCanvas({
className='relative min-w-0 flex-1 bg-muted/20'
onContextMenu={suppressBrowserContextMenu}
>
<div className='absolute left-4 top-4 z-10 flex flex-wrap items-center gap-2 rounded-lg border bg-background/95 p-2 shadow-sm backdrop-blur'>
<div className='absolute left-4 top-4 z-10 rounded-lg border bg-background/95 p-2 shadow-sm backdrop-blur'>
<NodeLibrary />
<Button
type='button'
variant='outline'
size='sm'
title='自动整理节点与连线布局'
onClick={formatLayout}
>
<AlignHorizontalSpaceAround data-icon='inline-start' />
格式化布局
</Button>
</div>
<ReactFlow
nodes={nodes}
+75 -51
View File
@@ -3,7 +3,12 @@
import { Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
import { ArrowLeft, GitBranch, Save } from 'lucide-react';
import {
AlignHorizontalSpaceAround,
ArrowLeft,
GitBranch,
Save,
} from 'lucide-react';
import { useRouter, useSearchParams } from 'next/navigation';
import { toast } from 'sonner';
@@ -20,14 +25,15 @@ import {
} from '@/lib/services';
import { getErrorMessage } from '../../components/helpers';
import { validateGraph } from './components/graph-validation';
import { NodeProperties } from './components/node-properties';
import { RuleFlowCanvas } from './components/rule-flow-canvas';
import { UnsavedChanges } from './components/unsaved-changes';
import {
findGraphErrorTarget,
type GraphErrorTarget,
} from './components/editor-behavior';
import { layoutRuleGraph } from './components/graph-layout';
import { validateGraph } from './components/graph-validation';
import { NodeProperties } from './components/node-properties';
import { RuleFlowCanvas } from './components/rule-flow-canvas';
import { UnsavedChanges } from './components/unsaved-changes';
export default function WAFRuleEditorPage() {
return (
@@ -160,6 +166,10 @@ function EditorContent() {
},
[changeGraph, graph],
);
const formatLayout = useCallback(() => {
if (!graph) return;
changeGraph(layoutRuleGraph(graph));
}, [changeGraph, graph]);
const leave = () => {
if (!dirty || window.confirm('存在未保存的更改,确定离开吗?'))
router.push('/waf');
@@ -186,58 +196,72 @@ function EditorContent() {
return (
<div className='flex h-[calc(100dvh-8rem)] w-full flex-col px-1 py-6'>
<UnsavedChanges dirty={dirty} />
<header className='mb-4 flex items-center justify-between gap-4'>
<div className='flex min-w-0 items-center gap-2'>
<GitBranch className='size-5 text-primary' />
<h1 className='text-2xl font-semibold tracking-tight'>
{ruleQuery.data.name}
</h1>
<Badge variant={ruleQuery.data.enabled ? 'default' : 'secondary'}>
{ruleQuery.data.enabled ? '已启用' : '已停用'}
</Badge>
<Badge variant={issues.length === 0 ? 'outline' : 'destructive'}>
{issues.length === 0 ? '图校验通过' : `${issues.length} 个问题`}
</Badge>
{dirty && <Badge variant='secondary'>未保存</Badge>}
</div>
<div className='flex shrink-0 items-center gap-2'>
<div className='flex items-center gap-2'>
<Switch
id='rule-enabled'
checked={ruleQuery.data.enabled}
disabled={
dirty ||
issues.length > 0 ||
saveMutation.isPending ||
metaMutation.isPending
}
onCheckedChange={(enabled) => metaMutation.mutate(enabled)}
/>
<Label htmlFor='rule-enabled'>启用规则</Label>
</div>
<Button variant='outline' onClick={leave}>
<header className='mb-4 flex flex-col gap-3'>
<div>
<Button variant='outline' size='sm' onClick={leave}>
<ArrowLeft data-icon='inline-start' />
返回
返回规则组
</Button>
{conflict && (
</div>
<div className='flex items-center justify-between gap-4'>
<div className='flex min-w-0 items-center gap-2'>
<GitBranch className='size-5 text-primary' />
<h1 className='text-2xl font-semibold tracking-tight'>
{ruleQuery.data.name}
</h1>
<Badge variant={ruleQuery.data.enabled ? 'default' : 'secondary'}>
{ruleQuery.data.enabled ? '已启用' : '已停用'}
</Badge>
<Badge variant={issues.length === 0 ? 'outline' : 'destructive'}>
{issues.length === 0 ? '图校验通过' : `${issues.length} 个问题`}
</Badge>
{dirty && <Badge variant='secondary'>未保存</Badge>}
</div>
<div className='flex shrink-0 items-center gap-2'>
<div className='flex items-center gap-2'>
<Switch
id='rule-enabled'
checked={ruleQuery.data.enabled}
disabled={
dirty ||
issues.length > 0 ||
saveMutation.isPending ||
metaMutation.isPending
}
onCheckedChange={(enabled) => metaMutation.mutate(enabled)}
/>
<Label htmlFor='rule-enabled'>启用规则</Label>
</div>
{conflict && (
<Button
variant='outline'
onClick={() => {
setDirty(false);
setConflict(false);
void ruleQuery.refetch();
}}
>
重新加载
</Button>
)}
<Button
type='button'
variant='outline'
onClick={() => {
setDirty(false);
setConflict(false);
void ruleQuery.refetch();
}}
title='自动整理节点与连线布局'
disabled={!graph}
onClick={formatLayout}
>
重新加载
<AlignHorizontalSpaceAround data-icon='inline-start' />
格式化布局
</Button>
)}
<Button
disabled={!dirty || issues.length > 0 || saveMutation.isPending}
onClick={() => saveMutation.mutate()}
>
<Save data-icon='inline-start' />
{saveMutation.isPending ? '保存中...' : '保存'}
</Button>
<Button
disabled={!dirty || issues.length > 0 || saveMutation.isPending}
onClick={() => saveMutation.mutate()}
>
<Save data-icon='inline-start' />
{saveMutation.isPending ? '保存中...' : '保存'}
</Button>
</div>
</div>
</header>
<div className='flex min-h-0 flex-1 overflow-hidden rounded-xl border bg-background shadow-sm'>