添加另存为配置
This commit is contained in:
parent
0d184600f8
commit
53616d4c1d
@ -69,7 +69,7 @@
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gen-btn {
|
.node-btn {
|
||||||
background: #1677ff;
|
background: #1677ff;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
@ -86,15 +86,15 @@
|
|||||||
outline: none; /* remove default focus outline */
|
outline: none; /* remove default focus outline */
|
||||||
}
|
}
|
||||||
|
|
||||||
.gen-btn:hover {
|
.node-btn:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gen-btn:focus {
|
.node-btn:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gen-btn:active {
|
.node-btn:active {
|
||||||
transform: translateY(1px) scale(0.995);
|
transform: translateY(1px) scale(0.995);
|
||||||
background-color: #0f62d6; /* slightly darker on press */
|
background-color: #0f62d6; /* slightly darker on press */
|
||||||
box-shadow: inset 0 1px 2px rgba(0,0,0,0.12);
|
box-shadow: inset 0 1px 2px rgba(0,0,0,0.12);
|
||||||
|
|||||||
@ -235,6 +235,73 @@ export default function CustomNode({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSaveAs = async (node: NodeData) => {
|
||||||
|
const graph = new AppGraph(getNodes, getEdges);
|
||||||
|
const nodes = getNodes();
|
||||||
|
let subnets: SubnetInfo[] = [];
|
||||||
|
|
||||||
|
const v4 = getSubnet(nodes, 'ipv4');
|
||||||
|
const v6 = getSubnet(nodes, 'ipv6');
|
||||||
|
if(!v4.isValid()) {
|
||||||
|
toast.error(`ipv4子网配置有误:${v4.errorInfo()}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!v6.isValid()) {
|
||||||
|
toast.error(`ipv6子网配置有误:${v6.errorInfo()}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v4.result) subnets.push(v4.result);
|
||||||
|
if(v6.result) subnets.push(v6.result);
|
||||||
|
subnets = subnets.concat(settings.subnets);
|
||||||
|
|
||||||
|
const result = generateConfig(settings, node, graph, subnets);
|
||||||
|
if(result.success && result.config) {
|
||||||
|
const safeName = (node.label || node.id).replace(/[^a-z0-9_\-\.]/gi, '_') + '.conf';
|
||||||
|
// 优先使用浏览器的保存对话框(File System Access API),不支持时回退到 blob 下载
|
||||||
|
try {
|
||||||
|
if ((window as any).showSaveFilePicker) {
|
||||||
|
// @ts-ignore
|
||||||
|
const handle = await (window as any).showSaveFilePicker({
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
description: 'WireGuard config',
|
||||||
|
accept: { 'text/plain': ['.conf'] }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
suggestedName: safeName
|
||||||
|
});
|
||||||
|
const writable = await handle.createWritable();
|
||||||
|
await writable.write(result.config);
|
||||||
|
await writable.close();
|
||||||
|
toast.success('已保存');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 若用户取消或发生错误,回退到传统下载
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退到创建 Blob 下载
|
||||||
|
try {
|
||||||
|
const blob = new Blob([result.config], { type: 'text/plain;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = safeName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
toast.success('配置已下载');
|
||||||
|
} catch (e) {
|
||||||
|
toast.error('保存失败,请检查浏览器设置');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("配置生成失败:" + (result.error || "未知错误"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const empty = !(data.ipv4Address || data.ipv6Address);
|
const empty = !(data.ipv4Address || data.ipv6Address);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -263,10 +330,14 @@ export default function CustomNode({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="node-actions">
|
<div className="node-actions">
|
||||||
<button className="gen-btn" onClick={e => {
|
<button className="node-btn" onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleGenerate(data);
|
handleGenerate(data);
|
||||||
}} onDoubleClick={e => e.stopPropagation()}>生成配置并复制</button>
|
}} onDoubleClick={e => e.stopPropagation()}>复制配置</button>
|
||||||
|
<button className="node-btn" onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
void handleSaveAs(data);
|
||||||
|
}} onDoubleClick={e => e.stopPropagation()}>另存为</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
|
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user