优化更新表单逻辑

This commit is contained in:
limil 2026-02-07 23:49:24 +08:00
parent 098259177b
commit 1edb3474c5
5 changed files with 332 additions and 304 deletions

View File

@ -13,9 +13,6 @@ export default function CustomNode({
<span className="node-label">{data.label}</span> <span className="node-label">{data.label}</span>
</div> </div>
<div className="node-info">
</div>
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => ( {[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
(["target", "source"] as const).map((type) => ( (["target", "source"] as const).map((type) => (
<Handle type={type} position={position} id={position} key={`${type}-${position}`} className="node-handle"/> <Handle type={type} position={position} id={position} key={`${type}-${position}`} className="node-handle"/>

View File

@ -1,10 +1,11 @@
import { useState, ReactNode } from 'react'; import { useState, ReactNode } from 'react';
import { AppNode, AppEdge, EdgeData } from '../types/graph'; import { AppNode, AppEdge, EdgeData, EdgeDataUpdate } from '../types/graph';
import { useReactFlow } from '@xyflow/react'; import { useReactFlow } from '@xyflow/react';
import './FormEditor.css';
interface EdgeEditorProps { interface EdgeEditorProps {
edge: EdgeData; edge: EdgeData;
onUpdate: (data: EdgeData) => void; onUpdate: (data: EdgeDataUpdate) => void;
onClose: () => void; onClose: () => void;
} }
@ -13,18 +14,12 @@ export default function NodeEditor({
onUpdate, onUpdate,
onClose onClose
}: EdgeEditorProps): ReactNode { }: EdgeEditorProps): ReactNode {
const [formData, setFormData] = useState<EdgeData>(edge);
const [keepalive, setKeepalive] = useState(edge.persistentKeepalive);
const { getNode, getEdge } = useReactFlow<AppNode, AppEdge>(); const { getNode, getEdge } = useReactFlow<AppNode, AppEdge>();
const handleInputChange = (field: keyof EdgeData, value: string): void => {
setFormData(prev => ({
...prev,
[field]: value
}));
};
const handleSave = (): void => { const handleSave = (): void => {
onUpdate(formData); onUpdate({persistentKeepalive : keepalive});
onClose(); onClose();
}; };
@ -52,8 +47,13 @@ export default function NodeEditor({
type="number" type="number"
min="0" min="0"
step="1" step="1"
value={formData.persistentKeepalive || ''} value={keepalive || ''}
onChange={(e) => handleInputChange('persistentKeepalive', e.target.value)} onChange={
(e) => {
const value = e.target.valueAsNumber;
setKeepalive(isNaN(value) ? undefined : value);
}
}
placeholder={`留空或0代表不保活`} placeholder={`留空或0代表不保活`}
/> />
</div> </div>

View File

@ -1,7 +1,7 @@
import { useState, ReactNode } from 'react'; import { useState, ReactNode } from 'react';
import { NodeData, Settings } from '../types/graph'; import { NodeData, Settings, NodeDataUpdate } from '../types/graph';
import { generateWireGuardPrivateKey } from '../utils/wireguardConfig' import { generateWireGuardPrivateKey } from '../utils/wireguardConfig'
import './NodeEditor.css'; import './FormEditor.css';
import Folder from './Folder' import Folder from './Folder'
@ -10,15 +10,10 @@ interface Validation {
errors: string[] errors: string[]
} }
function validateNodeConfig(formData : NodeData) : Validation {
// todo
return {isValid : true, errors: []}
}
interface NodeEditorProps { interface NodeEditorProps {
node: NodeData; node: NodeData;
settings: Settings; settings: Settings;
onUpdate: (data: NodeData) => void; onUpdate: (data: NodeDataUpdate) => void;
onClose: () => void; onClose: () => void;
} }
@ -29,36 +24,48 @@ export default function NodeEditor({
onClose onClose
}: NodeEditorProps): ReactNode { }: NodeEditorProps): ReactNode {
const [formData, setFormData] = useState<NodeData>(node);
const [errors, setErrors] = useState<string[]>([]); const [errors, setErrors] = useState<string[]>([]);
const handleInputChange = (field: keyof NodeData, value: string): void => { const [label, setLabel] = useState<string>(node.label);
setFormData(prev => ({ const [privateKey, setPrivateKey] = useState<string>(node.privateKey);
...prev, const [ipv4Address, setIpv4Address] = useState(node.ipv4Address);
[field]: value const [ipv6Address, setIpv6Address] = useState(node.ipv6Address);
})); const [disallowIPs, setDisallowIPs] = useState(node.disallowIPs);
}; const [listenPort, setListenPort] = useState(node.listenPort);
const [mtu, setmtu] = useState(node.mtu);
const [dnsServers, setdnsServers] = useState(node.dnsServers)
const [postUp, setPostUp] = useState(node.postUp)
const [postDown, setPostDown] = useState(node.postDown)
const [notes, setNotes] = useState(node.notes)
const handleSave = (): void => { const handleSave = (): void => {
const validation = validateNodeConfig(formData); // const validation = validateNodeConfig(formData);
if (!validation.isValid) { // if (!validation.isValid) {
setErrors(validation.errors); // setErrors(validation.errors);
return; // return;
} // }
setErrors([]); setErrors([]);
onUpdate(formData); onUpdate({
label: label,
privateKey: privateKey,
ipv4Address: ipv4Address,
ipv6Address: ipv6Address,
disallowIPs: disallowIPs,
postUp: postUp,
postDown: postDown,
mtu: mtu,
listenPort: listenPort,
dnsServers: dnsServers,
notes: notes
});
onClose(); onClose();
}; };
const handleGenerateKey = (): void => {
handleInputChange('privateKey', generateWireGuardPrivateKey())
}
return ( return (
<div className="node-editor-overlay"> <div className="node-editor-overlay">
<div className="node-editor"> <div className="node-editor">
<div className="editor-header"> <div className="editor-header">
<h2>: {formData.label || '新节点'}</h2> <h2>: {label}</h2>
<button className="close-btn" onClick={onClose}>×</button> <button className="close-btn" onClick={onClose}>×</button>
</div> </div>
@ -80,8 +87,8 @@ export default function NodeEditor({
<label></label> <label></label>
<input <input
type="text" type="text"
value={formData.label || ''} value={label}
onChange={(e) => handleInputChange('label', e.target.value)} onChange={e => setLabel(e.target.value)}
placeholder="例如: Node-A" placeholder="例如: Node-A"
/> />
</div> </div>
@ -90,10 +97,11 @@ export default function NodeEditor({
<label></label> <label></label>
<div className="item-group"> <div className="item-group">
<input <input
value={formData.privateKey || ''} value={privateKey}
readOnly readOnly
/> />
<button className="btn-interect" onClick={handleGenerateKey}></button> <button className="btn-interect"
onClick={_ => setPrivateKey(generateWireGuardPrivateKey())}></button>
</div> </div>
</div> </div>
@ -102,8 +110,8 @@ export default function NodeEditor({
<label>IPv4地址</label> <label>IPv4地址</label>
<input <input
type="text" type="text"
value={formData.ipv4Address || ''} value={ipv4Address || ''}
onChange={(e) => handleInputChange('ipv4Address', e.target.value)} onChange={e => setIpv4Address(e.target.value)}
/> />
</div> </div>
)} )}
@ -113,8 +121,8 @@ export default function NodeEditor({
<label>IPv6地址</label> <label>IPv6地址</label>
<input <input
type="text" type="text"
value={formData.ipv6Address || ''} value={ipv6Address || ''}
onChange={(e) => handleInputChange('ipv6Address', e.target.value)} onChange={e => setIpv6Address(e.target.value)}
/> />
</div> </div>
)} )}
@ -124,8 +132,8 @@ export default function NodeEditor({
<label></label> <label></label>
<input <input
type="text" type="text"
value={formData.disallowIPs || ''} value={disallowIPs || ''}
onChange={(e) => handleInputChange('disallowIPs', e.target.value)} onChange={e => setDisallowIPs(e.target.value)}
/> />
</div> </div>
@ -136,8 +144,11 @@ export default function NodeEditor({
min="1024" min="1024"
max="49151" max="49151"
step="1" step="1"
value={formData.listenPort || ''} value={listenPort || ''}
onChange={(e) => handleInputChange('listenPort', e.target.value)} onChange={e => {
const value = e.target.valueAsNumber;
setListenPort(isNaN(value) ? undefined : value);
}}
placeholder={`默认值:${settings.listenPort}`} placeholder={`默认值:${settings.listenPort}`}
/> />
</div> </div>
@ -148,8 +159,11 @@ export default function NodeEditor({
type="number" type="number"
min="1" min="1"
step="1" step="1"
value={formData.mtu || ''} value={mtu || ''}
onChange={(e) => handleInputChange('mtu', e.target.value)} onChange={e => {
const value = e.target.valueAsNumber;
setmtu(isNaN(value) ? undefined : value);
}}
placeholder={settings.mtu ? `默认值:${settings.mtu}` : ''} placeholder={settings.mtu ? `默认值:${settings.mtu}` : ''}
/> />
</div> </div>
@ -158,8 +172,8 @@ export default function NodeEditor({
<label>DNS服务器</label> <label>DNS服务器</label>
<input <input
type="text" type="text"
value={formData.dnsServers || ''} value={dnsServers || ''}
onChange={(e) => handleInputChange('dnsServers', e.target.value)} onChange={(e) => setdnsServers(e.target.value)}
placeholder="例如: 8.8.8.8,1.1.1.1" placeholder="例如: 8.8.8.8,1.1.1.1"
/> />
</div> </div>
@ -168,8 +182,8 @@ export default function NodeEditor({
<label>PostUp</label> <label>PostUp</label>
<textarea <textarea
rows={2} rows={2}
value={formData.postUp || ''} value={postUp || ''}
onChange={(e) => handleInputChange('postUp', e.target.value)} onChange={(e) => setPostUp(e.target.value)}
/> />
</div> </div>
@ -177,8 +191,8 @@ export default function NodeEditor({
<label>PostDown</label> <label>PostDown</label>
<textarea <textarea
rows={2} rows={2}
value={formData.postDown || ''} value={postDown || ''}
onChange={(e) => handleInputChange('postDown', e.target.value)} onChange={(e) => setPostDown(e.target.value)}
/> />
</div> </div>
@ -186,8 +200,8 @@ export default function NodeEditor({
<label></label> <label></label>
<textarea <textarea
rows={4} rows={4}
value={formData.notes || ''} value={notes || ''}
onChange={(e) => handleInputChange('notes', e.target.value)} onChange={(e) => setNotes(e.target.value)}
/> />
</div> </div>
</Folder> </Folder>

View File

@ -11,7 +11,20 @@ export type NodeData = {
ipv4Address?: string; ipv4Address?: string;
ipv6Address?: string; ipv6Address?: string;
disallowIPs?: string; disallowIPs?: string;
postUp?: string;
postDown?: string;
mtu?: number;
listenPort?: number;
dnsServers?: string;
notes?: string;
}
export interface NodeDataUpdate {
label: string;
privateKey: string;
ipv4Address?: string;
ipv6Address?: string;
disallowIPs?: string;
postUp?: string; postUp?: string;
postDown?: string; postDown?: string;
mtu?: number; mtu?: number;
@ -23,7 +36,11 @@ export type NodeData = {
export type EdgeData = { export type EdgeData = {
readonly id: string; readonly id: string;
isTwoWayEdge: boolean; isTwoWayEdge: boolean;
persistentKeepalive?: string; persistentKeepalive?: number;
}
export interface EdgeDataUpdate {
persistentKeepalive?: number;
} }
export class SubNetRouter { export class SubNetRouter {