diff --git a/src/components/SettingsEditor.tsx b/src/components/SettingsEditor.tsx index c19f0ce..e323e42 100644 --- a/src/components/SettingsEditor.tsx +++ b/src/components/SettingsEditor.tsx @@ -61,30 +61,34 @@ function SubnetItem({index, subnets, setSubnets} : SubnetProps) : ReactNode { toast.error("没有选择有效的节点"); return ; } + const nodeId = selectedOption.value; - const nodecidr = (() => { - if(nodeSubnet.includes("/")) return nodeSubnet; - return `${nodeSubnet}/${nodeSubnet.includes(".") ? "32" : "128"}`; - })(); - - const result = IPUtils.parse(nodecidr); - const cidr = result.cidr; - if(!cidr) { - toast.error(`无法解析子网:${result.error}`); - return ; - } - - if(!subnetInfo.subnet.contains(cidr)) { - toast.error("不在子网范围内"); - return ; - } - if(subnetInfo.nodes.some(node => node.nodeId === nodeId)) { toast.error(`节点已添加`); return; } + let cidr = undefined; + if(nodeSubnet) { + const nodecidr = (() => { + if(nodeSubnet.includes("/")) return nodeSubnet; + return `${nodeSubnet}/${nodeSubnet.includes(".") ? "32" : "128"}`; + })(); + + const result = IPUtils.parse(nodecidr); + cidr = result.cidr; + if(!cidr) { + toast.error(`无法解析子网:${result.error}`); + return ; + } + + if(!subnetInfo.subnet.contains(cidr)) { + toast.error("不在子网范围内"); + return ; + } + } + setSubnets(subnets.map((info, idx) => { if(idx === index) { return {...info, nodes: [...info.nodes, {nodeId: nodeId, cidr: cidr}]} diff --git a/src/utils/iputils.ts b/src/utils/iputils.ts index e92438d..c9c6993 100644 --- a/src/utils/iputils.ts +++ b/src/utils/iputils.ts @@ -24,12 +24,36 @@ export class CIDR { } return `${octets.join('.')}/${this.mask}`; } else { - // Split 128 bits into 8 blocks of 16 bits - const blocks = []; + // IPv6: split into 8 hex blocks (no leading zeros) and apply :: compression + const blocks: string[] = []; for (let i = 0; i < 128; i += 16) { blocks.push(parseInt(this.binary.slice(i, i + 16), 2).toString(16)); } - return `${blocks.join(':')}/${this.mask}`; + + // find longest run of '0' blocks + let bestStart = -1, bestLen = 0; + for (let i = 0; i < blocks.length; ) { + if (blocks[i] !== '0') { i++; continue; } + let j = i + 1; + while (j < blocks.length && blocks[j] === '0') j++; + const len = j - i; + if (len > bestLen) { bestStart = i; bestLen = len; } + i = j; + } + + let compact: string; + if (bestLen >= 2) { + const left = blocks.slice(0, bestStart).join(':'); + const right = blocks.slice(bestStart + bestLen).join(':'); + if (left && right) compact = `${left}::${right}`; + else if (left) compact = `${left}::`; + else if (right) compact = `::${right}`; + else compact = '::'; + } else { + compact = blocks.join(':'); + } + + return `${compact}/${this.mask}`; } } }