添加另存为配置
This commit is contained in:
parent
0d184600f8
commit
53616d4c1d
@ -69,7 +69,7 @@
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.gen-btn {
|
||||
.node-btn {
|
||||
background: #1677ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
@ -86,15 +86,15 @@
|
||||
outline: none; /* remove default focus outline */
|
||||
}
|
||||
|
||||
.gen-btn:hover {
|
||||
.node-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gen-btn:focus {
|
||||
.node-btn:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.gen-btn:active {
|
||||
.node-btn:active {
|
||||
transform: translateY(1px) scale(0.995);
|
||||
background-color: #0f62d6; /* slightly darker on press */
|
||||
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);
|
||||
|
||||
return (
|
||||
@ -263,10 +330,14 @@ export default function CustomNode({
|
||||
</div>
|
||||
|
||||
<div className="node-actions">
|
||||
<button className="gen-btn" onClick={e => {
|
||||
<button className="node-btn" onClick={e => {
|
||||
e.stopPropagation();
|
||||
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>
|
||||
|
||||
{[Position.Top, Position.Bottom, Position.Right, Position.Left].map((position) => (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user