Files
EST-DSX/wiremap/故障图生成器.html
2025-09-16 16:51:55 +08:00

623 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<title>网络线序图生成器 (T568A/T568B)</title>
<style>
body {
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
color: #2c3e50;
margin-bottom: 30px;
}
#svg-container {
margin-top: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
overflow: auto;
}
.control-panel {
margin-bottom: 20px;
padding: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.control-group {
margin-bottom: 15px;
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
}
.control-group h3 {
margin-top: 0;
margin-bottom: 10px;
color: #2c3e50;
font-size: 18px;
}
select, button, input[type="checkbox"] {
padding: 8px 12px;
font-size: 16px;
border: 1px solid #ddd;
border-radius: 4px;
background-color: white;
}
select:focus, button:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52,152,219,0.2);
}
button {
background-color: #3498db;
color: white;
border: none;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #2980b9;
}
.cable-fault-row {
margin: 8px 0;
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.cable-fault-row select, .cable-fault-row input {
padding: 6px 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.cable-fault-row label {
min-width: 60px;
font-weight: 500;
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 15px;
}
.action-buttons button {
flex: 1;
}
.copy-success {
position: fixed;
top: 20px;
right: 20px;
background-color: #2ecc71;
color: white;
padding: 10px 20px;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
display: none;
animation: fadeInOut 2s ease-in-out;
}
@keyframes fadeInOut {
0% { opacity: 0; }
20% { opacity: 1; }
80% { opacity: 1; }
100% { opacity: 0; }
}
.svg-code-container {
margin-top: 20px;
background-color: #f8f9fa;
border-radius: 6px;
padding: 15px;
display: none;
}
.svg-code-container h3 {
margin-top: 0;
margin-bottom: 10px;
color: #2c3e50;
}
pre {
background-color: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
margin: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>网络线序图生成器 (T568A/T568B)</h1>
<div class="control-panel">
<div class="control-group">
<h3>基本设置</h3>
<div style="display: flex; gap: 20px; align-items: center;">
<div>
<label for="standard">选择线序标准:</label>
<select id="standard">
<option value="T568A">T568A</option>
<option value="T568B" selected>T568B</option>
</select>
</div>
<div>
<label for="show-shield">屏蔽线:</label>
<input type="checkbox" id="show-shield" checked>
</div>
</div>
</div>
<div class="control-group">
<h3>故障配置</h3>
<div id="cable-faults"></div>
</div>
<div class="action-buttons">
<button id="generate-btn">生成网络线序图</button>
<button id="copy-svg-btn">复制SVG代码</button>
<button id="save-svg-btn">保存SVG文件</button>
</div>
</div>
<div id="svg-container"></div>
<div class="svg-code-container" id="svg-code-container">
<h3>SVG代码</h3>
<pre id="svg-code"></pre>
</div>
</div>
<div class="copy-success" id="copy-success">复制成功!</div>
<script>
// 初始化故障配置面板
function initFaultConfig() {
const cableIds = ['1', '2', '3', '6', '4', '5', '7', '8', 'S'];
const faultConfigDiv = document.getElementById('cable-faults');
cableIds.forEach(id => {
const row = document.createElement('div');
row.className = 'cable-fault-row';
const label = document.createElement('span');
label.textContent = `线${id}`;
const faultSelect = document.createElement('select');
faultSelect.id = `fault-type-${id}`;
faultSelect.innerHTML = `
<option value="none">正常</option>
<option value="open">开路</option>
<option value="short">短路</option>
<option value="cross">交叉</option>
<option value="bridge">跨接</option>
`;
const shortTargetSelect = document.createElement('select');
shortTargetSelect.id = `short-target-${id}`;
shortTargetSelect.style.display = 'none';
shortTargetSelect.innerHTML = cableIds
.filter(targetId => targetId !== id)
.map(targetId => `<option value="${targetId}">线${targetId}</option>`)
.join('');
const shortPositionSelect = document.createElement('select');
shortPositionSelect.id = `short-position-${id}`;
shortPositionSelect.style.display = 'none';
shortPositionSelect.innerHTML = `
<option value="start">头端</option>
<option value="end">尾端</option>
`;
const shortDistanceInput = document.createElement('input');
shortDistanceInput.id = `short-distance-${id}`;
shortDistanceInput.type = 'number';
shortDistanceInput.min = '0';
shortDistanceInput.max = '100';
shortDistanceInput.step = '0.1';
shortDistanceInput.value = '1.1';
shortDistanceInput.style.width = '80px';
shortDistanceInput.placeholder = '短路距离(m)';
shortDistanceInput.style.display = 'none';
const bridgeTargetSelect = document.createElement('select');
bridgeTargetSelect.id = `bridge-target-${id}`;
bridgeTargetSelect.style.display = 'none';
bridgeTargetSelect.innerHTML = cableIds
.filter(targetId => targetId !== id)
.map(targetId => `<option value="${targetId}">线${targetId}</option>`)
.join('');
faultSelect.addEventListener('change', function() {
const isOpen = this.value === 'open';
const isShort = this.value === 'short';
const isCross = this.value === 'cross';
const isBridge = this.value === 'bridge';
// 处理交叉故障的联动隐藏和自动设置
if (isCross) {
let crossPairId;
if (id === '1') crossPairId = '2';
else if (id === '2') crossPairId = '1';
else if (id === '3') crossPairId = '6';
else if (id === '6') crossPairId = '3';
else if (id === '4') crossPairId = '5';
else if (id === '5') crossPairId = '4';
else if (id === '7') crossPairId = '8';
else if (id === '8') crossPairId = '7';
if (crossPairId) {
const crossPairSelect = document.getElementById(`fault-type-${crossPairId}`);
const crossPairRow = crossPairSelect.closest('.cable-fault-row');
crossPairRow.style.display = 'none';
// 自动设置对应线对的交叉状态
crossPairSelect.value = 'cross';
}
} else {
// 当取消交叉时,显示对应的线对
let crossPairId;
if (id === '1') crossPairId = '2';
else if (id === '2') crossPairId = '1';
else if (id === '3') crossPairId = '6';
else if (id === '6') crossPairId = '3';
else if (id === '4') crossPairId = '5';
else if (id === '5') crossPairId = '4';
else if (id === '7') crossPairId = '8';
else if (id === '8') crossPairId = '7';
if (crossPairId) {
const crossPairRow = document.getElementById(`fault-type-${crossPairId}`).closest('.cable-fault-row');
crossPairRow.style.display = 'flex';
}
}
leftDistanceInput.style.display = isOpen ? 'inline' : 'none';
rightDistanceInput.style.display = isOpen ? 'inline' : 'none';
shortTargetSelect.style.display = isShort ? 'inline' : 'none';
shortPositionSelect.style.display = isShort ? 'inline' : 'none';
shortDistanceInput.style.display = isShort ? 'inline' : 'none';
bridgeTargetSelect.style.display = isBridge ? 'inline' : 'none';
});
const leftDistanceInput = document.createElement('input');
leftDistanceInput.id = `fault-distance-left-${id}`;
leftDistanceInput.type = 'number';
leftDistanceInput.min = '0';
leftDistanceInput.max = '100';
leftDistanceInput.step = '0.1';
leftDistanceInput.value = '10.0';
leftDistanceInput.style.width = '80px';
leftDistanceInput.placeholder = '左端距离(m)';
leftDistanceInput.style.display = 'none';
const rightDistanceInput = document.createElement('input');
rightDistanceInput.id = `fault-distance-right-${id}`;
rightDistanceInput.type = 'number';
rightDistanceInput.min = '0';
rightDistanceInput.max = '100';
rightDistanceInput.step = '0.1';
rightDistanceInput.value = '10.0';
rightDistanceInput.style.width = '80px';
rightDistanceInput.placeholder = '右端距离(m)';
rightDistanceInput.style.display = 'none';
faultSelect.addEventListener('change', function() {
const isOpen = this.value === 'open';
leftDistanceInput.style.display = isOpen ? 'inline' : 'none';
rightDistanceInput.style.display = isOpen ? 'inline' : 'none';
});
row.appendChild(label);
row.appendChild(faultSelect);
row.appendChild(leftDistanceInput);
row.appendChild(rightDistanceInput);
row.appendChild(shortTargetSelect);
row.appendChild(shortPositionSelect);
row.appendChild(shortDistanceInput);
row.appendChild(bridgeTargetSelect);
faultConfigDiv.appendChild(row);
});
}
// 页面加载时初始化故障配置
initFaultConfig();
// 生成SVG并显示代码
document.getElementById('generate-btn').addEventListener('click', function() {
const standard = document.getElementById('standard').value;
const showShield = document.getElementById('show-shield').checked;
const faultConfig = {};
const cableIds = ['1', '2', '3', '6', '4', '5', '7', '8'];
if (showShield) {
cableIds.push('S');
}
cableIds.forEach(id => {
const faultType = document.getElementById(`fault-type-${id}`).value;
const leftDistance = document.getElementById(`fault-distance-left-${id}`).value;
const rightDistance = document.getElementById(`fault-distance-right-${id}`).value;
const shortTarget = document.getElementById(`short-target-${id}`).value;
const shortPosition = document.getElementById(`short-position-${id}`).value;
const shortDistance = document.getElementById(`short-distance-${id}`).value;
const bridgeTarget = document.getElementById(`bridge-target-${id}`).value;
faultConfig[id] = {
type: faultType,
leftDistance: faultType === 'open' ? parseFloat(leftDistance) : null,
rightDistance: faultType === 'open' ? parseFloat(rightDistance) : null,
shortTarget: faultType === 'short' ? shortTarget : null,
shortPosition: faultType === 'short' ? shortPosition : null,
shortDistance: faultType === 'short' ? parseFloat(shortDistance) : null,
bridgeTarget: faultType === 'bridge' ? bridgeTarget : null
};
});
const svgElement = generateSVG(standard, faultConfig, showShield);
// 显示SVG代码
const svgCode = new XMLSerializer().serializeToString(svgElement);
document.getElementById('svg-code').textContent = svgCode;
document.getElementById('svg-code-container').style.display = 'block';
});
// 复制SVG代码
document.getElementById('copy-svg-btn').addEventListener('click', function() {
const svgCode = document.getElementById('svg-code').textContent;
if (svgCode) {
navigator.clipboard.writeText(svgCode).then(function() {
const copySuccess = document.getElementById('copy-success');
copySuccess.style.display = 'block';
setTimeout(function() {
copySuccess.style.display = 'none';
}, 2000);
}).catch(function(err) {
console.error('无法复制: ', err);
alert('复制失败,请手动复制');
});
} else {
alert('请先生成SVG图像');
}
});
// 保存SVG文件
document.getElementById('save-svg-btn').addEventListener('click', function() {
const svgCode = document.getElementById('svg-code').textContent;
if (svgCode) {
const fileName = prompt('请输入要保存的文件名 (不包含扩展名):', '网络线序图');
if (fileName === null) { // 用户取消输入
return;
}
const fullFileName = fileName.trim() === '' ? '网络线序图.svg' : fileName + '.svg';
const blob = new Blob([svgCode], {type: 'image/svg+xml'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fullFileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} else {
alert('请先生成SVG图像');
}
});
function generateSVG(standard, faultConfig, showShield) {
const svgNS = "http://www.w3.org/2000/svg";
const svg = document.createElementNS(svgNS, "svg");
svg.setAttribute("width", "800");
svg.setAttribute("height", "600");
// 添加样式
const style = document.createElementNS(svgNS, "style");
style.textContent = `
.wire { stroke-width: 10; fill: none; }
.label { font-family: Arial; font-size: 24px; font-weight: bold; }
.solid { stroke-dasharray: none; }
.dashed { stroke-dasharray: 30 15; }
`;
svg.appendChild(style);
// 背景
const bg = document.createElementNS(svgNS, "rect");
bg.setAttribute("width", "100%");
bg.setAttribute("height", "100%");
bg.setAttribute("fill", "#f9f9f9");
svg.appendChild(bg);
// 线缆数据T568A 和 T568B 的区别)
const baseConfig = (standard === "T568A") ? [
{ id: 1, color: "#009900", y: 50, endY: 50, dash: true }, // 白绿T568A
{ id: 2, color: "#009900", y: 100, endY: 100, dash: false }, // 绿T568A
{ id: 3, color: "#ff9900", y: 170, endY: 170, dash: true }, // 白橙T568A
{ id: 6, color: "#ff9900", y: 220, endY: 220, dash: false }, // 橙T568A
{ id: 4, color: "#0000ff", y: 300, endY: 300, dash: false }, // 蓝
{ id: 5, color: "#0000ff", y: 350, endY: 350, dash: true }, // 白蓝
{ id: 7, color: "#996633", y: 430, endY: 430, dash: true }, // 白棕
{ id: 8, color: "#996633", y: 480, endY: 480, dash: false }, // 棕
{ id: 'S', color: "#777777", y: 550, endY: 550, dash: false } // 屏蔽层(可选)
] : [
{ id: 1, color: "#ff9900", y: 50, endY: 50, dash: true }, // 白橙T568B
{ id: 2, color: "#ff9900", y: 100, endY: 100, dash: false }, // 橙T568B
{ id: 3, color: "#009900", y: 170, endY: 170, dash: true }, // 白绿T568B
{ id: 6, color: "#009900", y: 220, endY: 220, dash: false }, // 绿T568B
{ id: 4, color: "#0000ff", y: 300, endY: 300, dash: false }, // 蓝
{ id: 5, color: "#0000ff", y: 350, endY: 350, dash: true }, // 白蓝
{ id: 7, color: "#996633", y: 430, endY: 430, dash: true }, // 白棕
{ id: 8, color: "#996633", y: 480, endY: 480, dash: false }, // 棕
{ id: 'S', color: "#777777", y: 550, endY: 550, dash: false } // 屏蔽层(可选)
];
// 根据是否显示屏蔽线过滤配置
const filteredConfig = showShield ? baseConfig : baseConfig.filter(cable => cable.id !== 'S');
// 应用故障配置
const cables = filteredConfig.map(cable => {
const config = faultConfig[cable.id];
let endY = cable.y;
// 处理交叉故障
if (config.type === 'cross') {
let crossPairId;
if (cable.id === 1) crossPairId = 2;
else if (cable.id === 2) crossPairId = 1;
else if (cable.id === 3) crossPairId = 6;
else if (cable.id === 6) crossPairId = 3;
else if (cable.id === 4) crossPairId = 5;
else if (cable.id === 5) crossPairId = 4;
else if (cable.id === 7) crossPairId = 8;
else if (cable.id === 8) crossPairId = 7;
if (crossPairId) {
const crossPairCable = filteredConfig.find(c => c.id === crossPairId);
if (crossPairCable) {
endY = crossPairCable.y;
}
}
}
// 处理跨接故障
if (config.type === 'bridge' && config.bridgeTarget) {
const bridgeTargetCable = filteredConfig.find(c => c.id.toString() === config.bridgeTarget.toString());
if (bridgeTargetCable) {
endY = bridgeTargetCable.y;
}
}
return {
...cable,
endY,
fault: config.type,
leftDistance: config.leftDistance,
rightDistance: config.rightDistance,
shortTarget: config.shortTarget,
shortPosition: config.shortPosition,
shortDistance: config.shortDistance,
bridgeTarget: config.bridgeTarget
};
});
// 生成每条线缆
cables.forEach(cable => {
// 开始直线段
const line1 = document.createElementNS(svgNS, "line");
line1.setAttribute("x1", "100");
line1.setAttribute("y1", cable.y);
line1.setAttribute("x2", "150");
line1.setAttribute("y2", cable.y);
line1.setAttribute("class", `wire ${cable.dash ? 'dashed' : 'solid'}`);
line1.setAttribute("stroke", cable.color);
svg.appendChild(line1);
// 中间曲线段(如果不是开路故障)
if (!cable.fault || cable.fault === 'none' || cable.fault === 'short' || cable.fault === 'cross' || cable.fault === 'bridge') {
const path = document.createElementNS(svgNS, "path");
const controlY = (cable.fault === 'cross' || cable.fault === 'bridge') ? (cable.y + cable.endY) / 2 : cable.y;
path.setAttribute("d", `M 150,${cable.y} Q 400,${controlY} 650,${cable.endY}`);
path.setAttribute("class", `wire ${cable.dash ? 'dashed' : 'solid'}`);
path.setAttribute("stroke", cable.color);
svg.appendChild(path);
} else if (cable.fault === 'open') {
// 开路故障时添加距离标签
// 左侧距离标签
const leftLabel = document.createElementNS(svgNS, "text");
leftLabel.setAttribute("x", "180");
leftLabel.setAttribute("y", cable.y + 5);
leftLabel.setAttribute("text-anchor", "start");
leftLabel.textContent = `${cable.leftDistance}m`;
svg.appendChild(leftLabel);
// 右侧距离标签
const rightLabel = document.createElementNS(svgNS, "text");
rightLabel.setAttribute("x", "630");
rightLabel.setAttribute("y", cable.y + 5);
rightLabel.setAttribute("text-anchor", "end");
rightLabel.textContent = `${cable.rightDistance}m`;
svg.appendChild(rightLabel);
}
// 结束直线段
const line2 = document.createElementNS(svgNS, "line");
line2.setAttribute("x1", "650");
line2.setAttribute("y1", cable.y);
line2.setAttribute("x2", "700");
line2.setAttribute("y2", cable.y);
line2.setAttribute("class", `wire ${cable.dash ? 'dashed' : 'solid'}`);
line2.setAttribute("stroke", cable.color);
svg.appendChild(line2);
// 标签
const label1 = document.createElementNS(svgNS, "text");
label1.setAttribute("x", "70");
label1.setAttribute("y", cable.y + 5);
label1.setAttribute("class", "label");
label1.setAttribute("text-anchor", "end");
label1.textContent = cable.id;
svg.appendChild(label1);
const label2 = document.createElementNS(svgNS, "text");
label2.setAttribute("x", "730");
label2.setAttribute("y", cable.y + 5);
label2.setAttribute("class", "label");
label2.textContent = cable.id;
svg.appendChild(label2);
});
// 在所有线缆渲染完成后,渲染短路标记
cables.forEach(cable => {
if (cable.fault === 'short') {
const targetCable = cables.find(c => c.id.toString() === cable.shortTarget.toString());
if (targetCable) {
const x = cable.shortPosition === 'start' ? 150 : 650;
const shortLabel = document.createElementNS(svgNS, "text");
shortLabel.setAttribute("x", x.toString());
shortLabel.setAttribute("y", Math.min(cable.y, targetCable.y) - 10);
shortLabel.setAttribute("text-anchor", "middle");
shortLabel.textContent = `${cable.shortDistance}m`;
svg.appendChild(shortLabel);
const dot1 = document.createElementNS(svgNS, "circle");
dot1.setAttribute("cx", x.toString());
dot1.setAttribute("cy", cable.y.toString());
dot1.setAttribute("r", "8");
dot1.setAttribute("fill", "black");
svg.appendChild(dot1);
const dot2 = document.createElementNS(svgNS, "circle");
dot2.setAttribute("cx", x.toString());
dot2.setAttribute("cy", targetCable.y.toString());
dot2.setAttribute("r", "8");
dot2.setAttribute("fill", "black");
svg.appendChild(dot2);
const shortLine = document.createElementNS(svgNS, "line");
shortLine.setAttribute("x1", x.toString());
shortLine.setAttribute("y1", cable.y.toString());
shortLine.setAttribute("x2", x.toString());
shortLine.setAttribute("y2", targetCable.y.toString());
shortLine.setAttribute("stroke", "black");
shortLine.setAttribute("stroke-width", "3");
svg.appendChild(shortLine);
}
}
});
// 添加到DOM
const container = document.getElementById('svg-container');
container.innerHTML = '';
container.appendChild(svg);
return svg;
}
</script>
</body>
</html>