dev
This commit is contained in:
274
src/components/dsxpage/CableId.js
Normal file
274
src/components/dsxpage/CableId.js
Normal file
@@ -0,0 +1,274 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import Keyboard from '../lib/Keyboard';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
|
||||
export default function CableId() {
|
||||
const [showKeyboard, setShowKeyboard] = useState(true);
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [inputValue2, setInputValue2] = useState('');
|
||||
const [activeInput, setActiveInput] = useState(1); // 1 表示第一个输入框,2 表示第二个输入框
|
||||
const {
|
||||
getCurrentProject,
|
||||
getCurrentCableId,
|
||||
getCurrentCableId2,
|
||||
updateProject,
|
||||
goBack
|
||||
} = useDisplayStore();
|
||||
|
||||
const currentProject = getCurrentProject();
|
||||
// 获取当前选中的电缆ID
|
||||
const currentCableId = getCurrentCableId().name;
|
||||
// 获取当前选中的电缆ID2
|
||||
const currentCableId2 = getCurrentCableId2().name;
|
||||
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
|
||||
|
||||
// 只在cfp视图下初始化第二个输入框
|
||||
useEffect(() => {
|
||||
setInputValue(currentCableId);
|
||||
if (view === 'cfp') {
|
||||
setInputValue2(currentCableId2);
|
||||
}
|
||||
}, [currentCableId, currentCableId2, view]);
|
||||
|
||||
// 计算下一个序号的ID
|
||||
const getNextId = (currentId) => {
|
||||
if (!currentId) return '';
|
||||
|
||||
// 检查是否以数字结尾
|
||||
const numMatch = currentId.match(/^(.*?)(\d+)$/);
|
||||
if (numMatch) {
|
||||
const [, prefix, num] = numMatch;
|
||||
const nextNum = String(Number(num) + 1).padStart(num.length, '0');
|
||||
return prefix + nextNum;
|
||||
}
|
||||
|
||||
// 检查是否以字母结尾
|
||||
const letterMatch = currentId.match(/^(.*?)([a-zA-Z]+)$/);
|
||||
if (letterMatch) {
|
||||
const [, prefix, letters] = letterMatch;
|
||||
|
||||
// 将字母转换为数组以便处理
|
||||
const letterArray = letters.split('');
|
||||
let carry = true;
|
||||
|
||||
// 从右向左处理每个字母
|
||||
for (let i = letterArray.length - 1; i >= 0 && carry; i--) {
|
||||
if (letterArray[i] === 'z') {
|
||||
letterArray[i] = 'a';
|
||||
carry = true;
|
||||
} else if (letterArray[i] === 'Z') {
|
||||
letterArray[i] = 'A';
|
||||
carry = true;
|
||||
} else {
|
||||
letterArray[i] = String.fromCharCode(letterArray[i].charCodeAt(0) + 1);
|
||||
carry = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还有进位,说明需要在前面添加一个字母
|
||||
if (carry) {
|
||||
if (letters[0] >= 'a' && letters[0] <= 'z') {
|
||||
letterArray.unshift('a');
|
||||
} else {
|
||||
letterArray.unshift('A');
|
||||
}
|
||||
}
|
||||
|
||||
return prefix + letterArray.join('');
|
||||
}
|
||||
|
||||
// 如果既不是数字也不是字母结尾,直接返回原值
|
||||
return currentId;
|
||||
};
|
||||
|
||||
// 只在cfp视图下自动更新第二个输入框
|
||||
useEffect(() => {
|
||||
if (view === 'cfp' && inputValue) {
|
||||
setInputValue2(getNextId(inputValue));
|
||||
}
|
||||
}, [inputValue, view]);
|
||||
|
||||
const handleComplete = () => {
|
||||
if (view === 'main' && !inputValue.trim()) return;
|
||||
if (view === 'cfp' && (!inputValue.trim() || !inputValue2.trim())) return;
|
||||
|
||||
const currentIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
const currentCableIds = currentProject?.cableIds || [];
|
||||
|
||||
let cableIds;
|
||||
if (view === 'main') {
|
||||
// 在main视图下,只更新选中的ID,保留其他ID
|
||||
const selectedId = getCurrentCableId().id;
|
||||
cableIds = currentCableIds.map(cable =>
|
||||
cable.id === selectedId ? { ...cable, name: inputValue.trim() } : cable
|
||||
);
|
||||
} else {
|
||||
cableIds = [
|
||||
{ id: '1', name: inputValue.trim() },
|
||||
{ id: '2', name: inputValue2.trim() }
|
||||
];
|
||||
}
|
||||
|
||||
updateProject(currentIndex, { cableIds });
|
||||
goBack();
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
switch (view) {
|
||||
case 'main':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div
|
||||
className="relative mb-4 cursor-pointer"
|
||||
onClick={() => setShowKeyboard(true)}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black"
|
||||
value={inputValue}
|
||||
placeholder="请输入线缆ID"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
// 保存光标位置
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{showKeyboard && (
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
setShowKeyboard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'cfp':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div className="mb-8">
|
||||
<div className="mb-1 text-white text-sm">输出光纤ID1</div>
|
||||
<div className="relative cursor-pointer">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm px-4 text-black overflow-x-auto whitespace-nowrap"
|
||||
value={inputValue}
|
||||
placeholder="请输入线缆ID1"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setActiveInput(1);
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-1 text-white text-sm">输入光纤ID2</div>
|
||||
<div className="relative cursor-pointer">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm px-4 text-black overflow-x-auto whitespace-nowrap"
|
||||
value={inputValue2}
|
||||
placeholder="请输入线缆ID2"
|
||||
onChange={(e) => {
|
||||
setInputValue2(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setActiveInput(2);
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{showKeyboard && (
|
||||
<Keyboard
|
||||
value={activeInput === 1 ? inputValue : inputValue2}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
if (activeInput === 1) {
|
||||
setInputValue(newValue);
|
||||
} else {
|
||||
setInputValue2(newValue);
|
||||
}
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
setShowKeyboard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
<TitleBar
|
||||
title="更改ID"
|
||||
backTo={useDisplayStore.getState().navigation.previous?.name || 'home'}
|
||||
view={useDisplayStore.getState().navigation.previous?.view || 'main'}
|
||||
/>
|
||||
{renderContent()}
|
||||
{view === 'main' && (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
{!showKeyboard && (<button
|
||||
onClick={handleComplete}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
完成
|
||||
</button>)}
|
||||
</div>
|
||||
)}
|
||||
{view === 'cfp' && (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
{!showKeyboard && (<button
|
||||
onClick={handleComplete}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
完成
|
||||
</button>)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
349
src/components/dsxpage/CopperPerformance.js
Normal file
349
src/components/dsxpage/CopperPerformance.js
Normal file
@@ -0,0 +1,349 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import ResultTitleBar from '../lib/ResultTitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
import FrequencyChart from '../lib/FrequencyChart';
|
||||
import HDTDChart from '../lib/HDTDChart';
|
||||
|
||||
export default function CopperPerformance( ) {
|
||||
const { navigation, navigateTo,goBack } = useDisplayStore();
|
||||
const { view } = navigation.current;
|
||||
const curtitle = navigation.current.params.curtitle;
|
||||
const testResult = navigation.current.params.testResult;
|
||||
const limitValue = testResult.testconfig.params.limitValue;
|
||||
const [limitdata, setLimitdata] = useState(null);
|
||||
const wireOrder = testResult?.testconfig?.params?.wireOrder;
|
||||
|
||||
useEffect(() => {
|
||||
// 异步加载限制数据
|
||||
const loadLimitData = async () => {
|
||||
if (limitValue) {
|
||||
try {
|
||||
// 从limitValue中提取基础名称(移除+PoE和+ALL后缀)
|
||||
const baseName = limitValue.split(' (+')[0];
|
||||
// 使用基础名称加载带有(+ALL)后缀的文件
|
||||
const data = await import(`@/store/COPPER/${baseName} (+ALL).json`);
|
||||
setLimitdata(data);
|
||||
} catch (error) {
|
||||
console.error('Error loading limit data:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
loadLimitData();
|
||||
}, [limitValue]);
|
||||
|
||||
|
||||
|
||||
|
||||
const [poeTab, setPoeTab] = useState('回路');
|
||||
|
||||
const renderResultTitleBar = () => {
|
||||
const handleBack = () => {
|
||||
goBack();
|
||||
};
|
||||
let title = '';
|
||||
switch (view) {
|
||||
case 'LENGTH':
|
||||
title = '长度';
|
||||
break;
|
||||
case 'OHM':
|
||||
title = '电阻';
|
||||
break;
|
||||
case 'PoE':
|
||||
title = 'PoE';
|
||||
break;
|
||||
case 'DRAW':
|
||||
title = '图表';
|
||||
break;
|
||||
};
|
||||
return <ResultTitleBar title={curtitle} onBack={handleBack} />;
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
switch (view) {
|
||||
case 'LENGTH':
|
||||
return (
|
||||
<div className="w-full h-[490px] bg-[#6b6d6b]">
|
||||
<div className="w-full h-full bg-white p-4 rounded-md">
|
||||
<div className="w-full">
|
||||
<div className="grid grid-cols-4 text-center border-b border-gray-300 pb-2">
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-blue-600 text-lg"></span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-blue-600 text-lg">传输延迟</span>
|
||||
<span className="text-gray-500">(ns)</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-blue-600 text-lg">延迟时差</span>
|
||||
<span className="text-gray-500">(ns)</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-blue-600 text-lg">长度</span>
|
||||
<span className="text-gray-500">(m)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 space-y-4">
|
||||
{[
|
||||
{ pair: '1,2', color: 'text-orange-500', delay: testResult?.resultdata?.performance?.DELAY?.PAIR12, delaySkew: testResult?.resultdata?.performance?.DELAYSKEW?.PAIR12, length: testResult?.resultdata?.performance?.LENGTH?.PAIR12 },
|
||||
{ pair: '3,6', color: 'text-green-500', delay: testResult?.resultdata?.performance?.DELAY?.PAIR36, delaySkew: testResult?.resultdata?.performance?.DELAYSKEW?.PAIR36, length: testResult?.resultdata?.performance?.LENGTH?.PAIR36 },
|
||||
...(wireOrder !== 'Ethernet Two-Pair' && wireOrder !== 'M12-D Two-Pair' ? [
|
||||
|
||||
{ pair: '4,5', color: 'text-blue-500', delay: testResult?.resultdata?.performance?.DELAY?.PAIR45, delaySkew: testResult?.resultdata?.performance?.DELAYSKEW?.PAIR45, length: testResult?.resultdata?.performance?.LENGTH?.PAIR45 },
|
||||
{ pair: '7,8', color: 'text-yellow-500', delay: testResult?.resultdata?.performance?.DELAY?.PAIR78, delaySkew: testResult?.resultdata?.performance?.DELAYSKEW?.PAIR78, length: testResult?.resultdata?.performance?.LENGTH?.PAIR78 }
|
||||
] : [])
|
||||
].map(({ pair, color, delay, delaySkew, length }) => (
|
||||
<div key={pair} className={`grid grid-cols-4 text-center border-gray-300 pt-2 ${color}`}>
|
||||
<div className="font-bold">{pair}</div>
|
||||
<div className={limitdata && delay > limitdata.DELAY ? 'bg-red-100 rounded-md' : ''}>{delay}</div>
|
||||
<div className={limitdata && delaySkew > limitdata.DELAYSKEW ? 'bg-red-100 rounded-md' : ''}>{delaySkew}</div>
|
||||
<div className={`text-center ${(limitdata && length > limitdata.LENGTH) && !(limitValue?.includes('ISO') || limitValue?.includes('Profinet')) ? 'bg-red-200 rounded-md' : ''}`}>{length}</div>
|
||||
</div>
|
||||
))}
|
||||
{limitdata && (
|
||||
<div className="grid grid-cols-4 text-center border-t border-gray-300 pt-2">
|
||||
<div className="font-bold">极限</div>
|
||||
|
||||
<div>{limitdata.DELAY}</div>
|
||||
<div>{limitdata.DELAYSKEW}</div>
|
||||
<div>{limitdata.LENGTH}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'OHM':
|
||||
return (
|
||||
<div className="w-full h-[490px] bg-[#6b6d6b]">
|
||||
<div className="w-full h-full bg-white p-0 rounded-md">
|
||||
<div className="w-full">
|
||||
{/* 选项卡 */}
|
||||
<div className="flex mb-4">
|
||||
{['回路'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setPoeTab(tab)}
|
||||
className={`flex-1 py-2 text-center text-xl font-bold ${poeTab === tab ? 'bg-gradient-to-b from-[#b0b0b0] via-[#e0e4e0] to-[#fff] text-black' : 'bg-[#303030] text-[#fffe92]'}`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 数据展示区域 */}
|
||||
{poeTab === '回路' && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="grid grid-cols-2 text-center border-b border-gray-300 pb-2">
|
||||
<div className="font-bold"> </div>
|
||||
<div >
|
||||
<span className="text-gray-500 text-lg">值(Ω)</span>
|
||||
</div>
|
||||
</div>
|
||||
{[
|
||||
{ pair: '1,2', color: 'text-orange-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR12 },
|
||||
{ pair: '3,6', color: 'text-green-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR36 },
|
||||
...(wireOrder !== 'Ethernet Two-Pair' && wireOrder !== 'M12-D Two-Pair' ? [
|
||||
{ pair: '4,5', color: 'text-blue-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR45 },
|
||||
{ pair: '7,8', color: 'text-yellow-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR78 }
|
||||
] : [])
|
||||
].map(({ pair, color, value }) => (
|
||||
|
||||
<div key={pair} className="grid grid-cols-2 text-center py-2">
|
||||
<div className={`font-bold ${color}`}>{pair}</div>
|
||||
<div>{value || 'N/A'}</div>
|
||||
</div>
|
||||
|
||||
))}
|
||||
<div className="grid grid-cols-2 text-center border-t border-gray-300 pt-2">
|
||||
<div className="font-bold">极限</div>
|
||||
{limitdata && (
|
||||
<div>{limitdata.LOOP}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'PoE':
|
||||
return (
|
||||
<div className="w-full h-[490px] bg-[#6b6d6b] ">
|
||||
<div className="w-full h-full bg-white p-0 rounded-md">
|
||||
<div className="w-full">
|
||||
{/* 选项卡 */}
|
||||
<div className="flex mb-4">
|
||||
{['回路', '线对UBL', 'P2P UBL'].map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setPoeTab(tab)}
|
||||
className={`flex-1 py-1 text-center text-lg font-bold ${poeTab === tab ? 'bg-gradient-to-b from-[#b0b0b0] via-[#e0e4e0] to-[#fff] text-black' : 'bg-[#303030] text-[#fffe92]'}`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 数据展示区域 */}
|
||||
{poeTab === '回路' && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="grid grid-cols-2 text-center border-b border-gray-300 pb-2">
|
||||
<div className="font-bold"> </div>
|
||||
<div >
|
||||
<span className="text-gray-500 text-lg">值(Ω)</span>
|
||||
</div>
|
||||
</div>
|
||||
{[
|
||||
{ pair: '1,2', color: 'text-orange-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR12 },
|
||||
{ pair: '3,6', color: 'text-green-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR36 },
|
||||
...(wireOrder !== 'Ethernet Two-Pair' && wireOrder !== 'M12-D Two-Pair' ? [
|
||||
|
||||
{ pair: '4,5', color: 'text-blue-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR45 },
|
||||
{ pair: '7,8', color: 'text-yellow-500', value: testResult?.resultdata?.performance?.OHM?.LOOP?.PAIR78 }
|
||||
] : [])
|
||||
].map(({ pair, color, value }) => (
|
||||
|
||||
<div key={pair} className="grid grid-cols-2 text-center py-2">
|
||||
<div className={`font-bold ${color}`}>{pair}</div>
|
||||
<div className={`${value > limitdata?.LOOP ? 'bg-red-100' : ''}`}>
|
||||
{value || 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
))}
|
||||
<div className="grid grid-cols-2 text-center border-t border-gray-300 pt-2">
|
||||
<div className="font-bold">极限</div>
|
||||
{limitdata && (
|
||||
<div>{limitdata.LOOP}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{poeTab === '线对UBL' && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="grid grid-cols-3 text-center border-b border-gray-300 pb-2">
|
||||
<div className="font-bold"> </div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500 text-lg">值(Ω)</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500 text-lg">极限(Ω)</span>
|
||||
</div>
|
||||
</div>
|
||||
{[
|
||||
{ pair: '1,2', color: 'text-orange-500', value: testResult?.resultdata?.performance?.OHM?.PAIRUBL?.PAIR12 },
|
||||
{ pair: '3,6', color: 'text-green-500', value: testResult?.resultdata?.performance?.OHM?.PAIRUBL?.PAIR36 },
|
||||
...(wireOrder !== 'Ethernet Two-Pair' && wireOrder !== 'M12-D Two-Pair' ? [
|
||||
|
||||
{ pair: '4,5', color: 'text-blue-500', value: testResult?.resultdata?.performance?.OHM?.PAIRUBL?.PAIR45 },
|
||||
{ pair: '7,8', color: 'text-yellow-500', value: testResult?.resultdata?.performance?.OHM?.PAIRUBL?.PAIR78 }
|
||||
] : [])
|
||||
].map(({ pair, color, value }) => (
|
||||
|
||||
<div key={pair} className="grid grid-cols-3 text-center py-2">
|
||||
<div className={`font-bold ${color}`}>{pair}</div>
|
||||
<div className={`${value > limitdata?.PAIRUBL ? 'bg-red-100' : ''}`}>
|
||||
{value || 'N/A'}
|
||||
</div>
|
||||
{limitdata && (
|
||||
<div>{limitdata.PAIRUBL}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{poeTab === 'P2P UBL' && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="grid grid-cols-3 text-center border-b border-gray-300 pb-2">
|
||||
<div className="font-bold"> </div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500 text-lg">值(Ω)</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500 text-lg">极限(Ω)</span>
|
||||
</div>
|
||||
</div>
|
||||
{[
|
||||
{ pair: '12-36', color: 'text-orange-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR1236 },
|
||||
...(wireOrder !== 'Ethernet Two-Pair' && wireOrder !== 'M12-D Two-Pair' ? [
|
||||
|
||||
{ pair: '12-45', color: 'text-green-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR1245 },
|
||||
{ pair: '12-78', color: 'text-blue-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR1278 },
|
||||
{ pair: '36-45', color: 'text-yellow-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR3645 },
|
||||
{ pair: '36-78', color: 'text-purple-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR3678 },
|
||||
{ pair: '45-78', color: 'text-pink-500', value: testResult?.resultdata?.performance?.OHM?.P2PUBL?.PAIR4578 }
|
||||
] : [])
|
||||
].map(({ pair, color, value }) => (
|
||||
<div key={pair} className="grid grid-cols-3 text-center py-2">
|
||||
<div className={`font-bold ${color}`}>{pair}</div>
|
||||
<div className={`${value > limitdata?.P2PUBL ? 'bg-red-100' : ''}`}>
|
||||
{value || 'N/A'}
|
||||
</div>
|
||||
{limitdata && (
|
||||
<div>{limitdata.P2PUBL}</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'DRAW':
|
||||
return (
|
||||
<div className="w-full h-[full] bg-[#6b6d6b]">
|
||||
<div className="w-full h-full bg-white p-4 rounded-md">
|
||||
{limitdata && (
|
||||
<FrequencyChart
|
||||
curtitle={curtitle}
|
||||
limitValue={limitValue}
|
||||
limitdata={limitdata}
|
||||
wireOrder={wireOrder}
|
||||
data={testResult?.resultdata?.performance?.data}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'HDTD':
|
||||
return (
|
||||
<div className="w-full h-[full] bg-[#6b6d6b]">
|
||||
<div className="w-full h-full bg-white p-4 rounded-md">
|
||||
{limitdata && (
|
||||
<HDTDChart
|
||||
curtitle={curtitle}
|
||||
HDTD={testResult?.resultdata?.HDTD}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{renderResultTitleBar()}
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
474
src/components/dsxpage/HomePage.js
Normal file
474
src/components/dsxpage/HomePage.js
Normal file
@@ -0,0 +1,474 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
import useDeviceStore from '@/store/deviceStore';
|
||||
|
||||
export default function HomePage() {
|
||||
const {
|
||||
getCurrentProject,
|
||||
getCurrentTestConfig,
|
||||
getCurrentOperator,
|
||||
getCurrentCableId,
|
||||
getCurrentCableId2,
|
||||
navigateTo,
|
||||
hasPlayedSound,
|
||||
setToastMessage
|
||||
} = useDisplayStore();
|
||||
|
||||
const { ref } = useDisplayStore.getState();
|
||||
|
||||
useEffect(() => {
|
||||
if (hasPlayedSound) {
|
||||
useDisplayStore.setState({ hasPlayedSound: false });
|
||||
}
|
||||
}, [hasPlayedSound]);
|
||||
|
||||
const { connectionStatus, mainUnitAdapter, remoteUnitAdapter,mainUnitModules,setTotalToastMessage } = useDeviceStore();
|
||||
|
||||
|
||||
// 获取当前项目数据
|
||||
const currentProject = getCurrentProject();
|
||||
|
||||
// 获取当前选中的测试配置
|
||||
const currentConfig = getCurrentTestConfig();
|
||||
|
||||
// 获取当前选中的操作员
|
||||
const currentOperator = getCurrentOperator();
|
||||
|
||||
// 获取当前选中的电缆ID
|
||||
const currentCableId = getCurrentCableId();
|
||||
// 获取当前选中的电缆ID2
|
||||
const currentCableId2 = getCurrentCableId2();
|
||||
|
||||
const passCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'pass';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'pass';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'pass';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
const failCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'fail';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'fail';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'fail';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
|
||||
const ConnectStatus = connectionStatus?.CFPConnectStatus ?? connectionStatus?.['8000ConnectStatus'];
|
||||
|
||||
|
||||
|
||||
|
||||
// 计算下一个线缆ID
|
||||
const getNextCableId = (currentId) => {
|
||||
const match = currentId.match(/^(.*?)(\d+)$/);
|
||||
if (match) {
|
||||
const prefix = match[1];
|
||||
const number = parseInt(match[2]) + 1;
|
||||
return `${prefix}${number.toString().padStart(match[2].length, '0')}`;
|
||||
}
|
||||
return currentId;
|
||||
};
|
||||
|
||||
const handleProjectClick = () => {
|
||||
navigateTo('project', 'main');
|
||||
};
|
||||
|
||||
const handleOperatorsClick = () => {
|
||||
navigateTo('operators', 'main');
|
||||
};
|
||||
|
||||
// 渲染测试配置区域的内容
|
||||
const renderTestConfigContent = () => {
|
||||
if (!currentConfig) return null;
|
||||
|
||||
switch (currentConfig.moduleType) {
|
||||
case '8000':
|
||||
return (
|
||||
<div className="flex flex-col justify-center h-full space-y-0.2 text-sm">
|
||||
<div className="text-black text-sm">{currentConfig.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.cableType}</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.wireOrder}</div>
|
||||
</div>
|
||||
);
|
||||
case 'cfp':
|
||||
return (
|
||||
<div className="flex flex-col justify-center h-full space-y-0.2 text-sm">
|
||||
<div className="text-black text-sm">智能远端</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.cableType}</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.refJumper} 跳线参照</div>
|
||||
</div>
|
||||
);
|
||||
case 'ofp':
|
||||
return (
|
||||
<div className="flex flex-col justify-center h-full space-y-0.2 text-sm">
|
||||
<div className="text-black text-sm">自动OTDR</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{currentConfig.params.cableType}</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
<TitleBar title="HOME" />
|
||||
|
||||
<div className="h-[490px] bg-[#303040] p-2 flex flex-col">
|
||||
{/* 上方信息区域 */}
|
||||
<div className="h-[315px] bg-[#c5c2c5] rounded-sm p-3 shadow-lg flex flex-col gap-2">
|
||||
{/* 项目配置部分 */}
|
||||
<div className="flex justify-between items-start cursor-pointer" onClick={handleProjectClick}>
|
||||
<div>
|
||||
<div className="text-lg font-bold mb-2">项目:{currentProject?.name}</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-green-500">{passCount|| 0}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/pass.png"
|
||||
alt="通过"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-red-500">{failCount|| 0}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/fail.png"
|
||||
alt="失败"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-6 h-6 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 测试配置区域 */}
|
||||
<div
|
||||
className="h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('testConfig', 'main')}
|
||||
>
|
||||
{renderTestConfigContent()}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-500 text-sm">{currentConfig?.modulelable}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 线缆ID区域 */}
|
||||
<div
|
||||
className="h-[60px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('cableId', currentConfig?.moduleType === 'cfp' ? 'cfp' : 'main')}
|
||||
>
|
||||
<div className="flex flex-col justify-center h-full space-y-0.2 text-sm">
|
||||
{currentConfig?.moduleType === 'cfp' ? (
|
||||
<>
|
||||
<div className="text-black">下一个输入ID: {currentCableId?.name}</div>
|
||||
<div className="text-black">下一个输出ID: {currentCableId2?.name}</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-black">下一个ID:
|
||||
<p>{currentCableId?.name}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作员区域 */}
|
||||
<div
|
||||
className="h-[40px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={handleOperatorsClick}
|
||||
>
|
||||
<div className="text-black text-sm">操作员: {currentOperator?.name}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 底部按钮区域 */}
|
||||
<div className="flex-1 flex items-center justify-center gap-16">
|
||||
<div className="flex flex-col items-center gap-2"
|
||||
onClick={() => navigateTo('tools', 'main')}>
|
||||
<div className="w-20 h-20 rounded-sm shadow-lg flex items-center justify-center cursor-pointer">
|
||||
<div className="w-full h-full relative">
|
||||
<Image
|
||||
src="/tools.png"
|
||||
alt="工具"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-white text-sm">工具</span>
|
||||
</div>
|
||||
<div
|
||||
className="flex flex-col items-center gap-2 cursor-pointer"
|
||||
onClick={() => navigateTo('result', 'main')}
|
||||
>
|
||||
<div className="w-20 h-20 rounded-sm shadow-lg flex items-center justify-center">
|
||||
<div className="w-full h-full relative">
|
||||
<Image
|
||||
src="/result.png"
|
||||
alt="结果"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-white text-sm">结果</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 底部栏 */}
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-5">
|
||||
{/* 设置参照按钮 */}
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex-1 flex justify-center">
|
||||
{(currentConfig?.moduleType === 'cfp' || currentConfig?.moduleType === 'ofp') && (
|
||||
<button
|
||||
className="w-[120px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
onClick={() => {
|
||||
// 根据模块类型设置不同的参照逻辑
|
||||
if (currentConfig?.moduleType === 'cfp') {
|
||||
navigateTo('tools', 'ref-cfp');
|
||||
} else if (currentConfig?.moduleType === 'ofp') {
|
||||
navigateTo('tools', 'ref-ofp');
|
||||
}
|
||||
}}
|
||||
>
|
||||
{currentConfig?.moduleType === 'ofp' ? '设置基准' : '设置参照'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{/* 测试按钮 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<button
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
onClick={() => {
|
||||
if (currentConfig?.moduleType === '8000' || currentConfig?.moduleType === 'cfp') {
|
||||
if (!ConnectStatus) {
|
||||
setToastMessage('未连接,请检查连接状态');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentConfig?.moduleType === '8000') {
|
||||
if (mainUnitModules?.[0]?.id !== '8000') {
|
||||
setToastMessage('当前模块与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
const limitValue = currentConfig.params.limitValue;
|
||||
|
||||
|
||||
if (limitValue.includes('Channel') || limitValue.includes('Ch')) {
|
||||
if (!mainUnitAdapter.id.includes('channel') || !remoteUnitAdapter.id.includes('channel')) {
|
||||
setToastMessage('当前适配器与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
} else if (limitValue.includes('Perm') || limitValue.includes('PL')) {
|
||||
if (!mainUnitAdapter.id.includes('permanent') || !remoteUnitAdapter.id.includes('permanent')) {
|
||||
setToastMessage('当前适配器与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
} else if (limitValue.includes('MPTL')) {
|
||||
const hasPermanent = mainUnitAdapter.id.includes('permanent') || remoteUnitAdapter.id.includes('permanent');
|
||||
const hasPatchcode = mainUnitAdapter.id.includes('patchcode') || remoteUnitAdapter.id.includes('patchcode');
|
||||
|
||||
if (!(hasPermanent && hasPatchcode)) {
|
||||
setToastMessage('当前适配器与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentConfig?.moduleType === 'cfp') {
|
||||
if (mainUnitModules?.[0]?.id !== 'cfp') {
|
||||
setToastMessage('当前模块与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查光纤类型
|
||||
const cableType = currentConfig.params.cableType;
|
||||
const isMultiMode = cableType.includes('OM');
|
||||
|
||||
// 根据单/多模获取对应的路径
|
||||
let mainOutPath, mainInPath, remoteOutPath, remoteInPath;
|
||||
|
||||
if (isMultiMode) {
|
||||
// 多模光纤路径
|
||||
mainOutPath = connectionStatus?.mainPaths?.['main-cfp-mm-out']?.path || [];
|
||||
mainInPath = connectionStatus?.mainPaths?.['main-cfp-in']?.path || [];
|
||||
remoteOutPath = connectionStatus?.remotePaths?.['remote-cfp-mm-out']?.path || [];
|
||||
remoteInPath = connectionStatus?.remotePaths?.['remote-cfp-in']?.path || [];
|
||||
|
||||
// 检查是否所有端口都已连接
|
||||
if (!mainOutPath.length || !mainInPath.length || !remoteOutPath.length || !remoteInPath.length) {
|
||||
setTotalToastMessage('请确保所有光纤端口都已正确连接');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否存在单模跳线
|
||||
const hasInvalidConnector = [...mainOutPath, ...mainInPath, ...remoteOutPath, ...remoteInPath].some(item => {
|
||||
return item.includes('sm');
|
||||
});
|
||||
|
||||
if (hasInvalidConnector) {
|
||||
setTotalToastMessage('多模光纤链路中存在单模跳线,请检查连接');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 单模光纤路径
|
||||
mainOutPath = connectionStatus?.mainPaths?.['main-cfp-sm-out']?.path || [];
|
||||
mainInPath = connectionStatus?.mainPaths?.['main-cfp-in']?.path || [];
|
||||
remoteOutPath = connectionStatus?.remotePaths?.['remote-cfp-sm-out']?.path || [];
|
||||
remoteInPath = connectionStatus?.remotePaths?.['remote-cfp-in']?.path || [];
|
||||
|
||||
// 检查是否所有端口都已连接
|
||||
if (!mainOutPath.length || !mainInPath.length || !remoteOutPath.length || !remoteInPath.length) {
|
||||
setTotalToastMessage('请确保所有光纤端口都已正确连接');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否存在多模跳线
|
||||
const hasInvalidConnector = [...mainOutPath, ...mainInPath, ...remoteOutPath, ...remoteInPath].some(item => {
|
||||
return item.includes('mm');
|
||||
});
|
||||
|
||||
if (hasInvalidConnector) {
|
||||
setTotalToastMessage('单模光纤链路中存在多模跳线,请检查连接');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentConfig?.moduleType === 'ofp') {
|
||||
if (mainUnitModules?.[0]?.id !== 'ofp') {
|
||||
setToastMessage('当前模块与所选标准不兼容');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查光纤类型是否匹配
|
||||
const cableType = currentConfig.params.cableType;
|
||||
const isMultiMode = cableType.includes('OM');
|
||||
const portType = Object.values(connectionStatus?.mainPaths || {})[0]?.start;
|
||||
|
||||
if (isMultiMode && portType !== 'main-ofp-mm-out') {
|
||||
setTotalToastMessage('当前光纤类型必须使用多模端口测试');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isMultiMode && portType !== 'main-ofp-sm-out') {
|
||||
setTotalToastMessage('当前光纤类型必须使用单模端口测试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查光纤连接器类型是否匹配
|
||||
const path = Object.values(connectionStatus?.mainPaths || {})[0]?.path || [];
|
||||
const hasInvalidConnector = path.some(item => {
|
||||
if (isMultiMode) {
|
||||
return item.includes('sm-') || item.includes('smc-');
|
||||
} else {
|
||||
return item.includes('mm-') || item.includes('mmc-');
|
||||
}
|
||||
});
|
||||
|
||||
if (hasInvalidConnector) {
|
||||
setTotalToastMessage(isMultiMode ? '未检测到多模光纤,请检查光纤连接' : '未检测到单模光纤,请检查光纤连接');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connectionStatus?.OFPStatus) {
|
||||
setTotalToastMessage('请接入实际被测链路,进行测试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查光纤连接数量
|
||||
const fiberConnectorCount = path.filter(item => {
|
||||
return item.includes('sm-') || item.includes('mm-') ||
|
||||
item.includes('smc-') || item.includes('mmc-');
|
||||
}).length;
|
||||
|
||||
if (fiberConnectorCount > 3) {
|
||||
setTotalToastMessage('该场景仅支持使用一段光纤连接测试仪与链路');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
navigateTo('testing','nosave');
|
||||
}}
|
||||
>
|
||||
测试
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
681
src/components/dsxpage/MenuList.js
Normal file
681
src/components/dsxpage/MenuList.js
Normal file
@@ -0,0 +1,681 @@
|
||||
import React, { useState,useEffect } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import SubTitleBar from '../lib/SubTitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
|
||||
// 菜单数据定义
|
||||
const menuData = {
|
||||
MODULE: {
|
||||
title: '模块',
|
||||
items: [
|
||||
{ value: '8000', label: 'DSX-8000' },
|
||||
{ value: 'cfp', label: 'CertiFiber Pro-Quad' },
|
||||
{ value: 'ofp', label: 'OptiFiber Pro-Quad' }
|
||||
]
|
||||
},
|
||||
CABLE_TYPE: {
|
||||
title: '电缆类型',
|
||||
subTitle: '电缆类型',
|
||||
items: [
|
||||
{ value: 'custom', label: '定制', disabled: true },
|
||||
{
|
||||
value: 'general',
|
||||
label: '通用',
|
||||
children: [
|
||||
// { value: 'Cat8 S/FTP', label: 'Cat8 S/FTP' },
|
||||
// { value: 'Cat7A S/FTP', label: 'Cat7A S/FTP' },
|
||||
// { value: 'Cat7 S/FTP', label: 'Cat7 S/FTP' },
|
||||
// { value: 'Cat6A S/FTP', label: 'Cat6A S/FTP' },
|
||||
{ value: 'Cat6A U/UTP', label: 'Cat6A U/UTP' },
|
||||
{ value: 'Cat6A F/UTP', label: 'Cat6A F/UTP' },
|
||||
{ value: 'Cat6 U/UTP', label: 'Cat6 U/UTP' },
|
||||
{ value: 'Cat6 F/UTP', label: 'Cat6 F/UTP' },
|
||||
// { value: 'Cat6 U/FTP', label: 'Cat6 U/FTP' },
|
||||
{ value: 'Cat5e U/UTP', label: 'Cat5e U/UTP' },
|
||||
{ value: 'Cat5e F/UTP', label: 'Cat5e F/UTP' },
|
||||
// { value: 'Cat5 U/UTP', label: 'Cat5 U/UTP' },
|
||||
// { value: 'Cat5 F/UTP', label: 'Cat5 F/UTP' }
|
||||
]
|
||||
},
|
||||
{ value: 'coaxial', label: '通用同轴电缆', disabled: true },
|
||||
{ value: 'manufacturer', label: '制造商', disabled: true }
|
||||
]
|
||||
},
|
||||
LIMIT_VALUE: {
|
||||
title: '测试极限值',
|
||||
subTitle: '极限值组',
|
||||
items: [
|
||||
{
|
||||
value: 'TIA',
|
||||
label: 'TIA',
|
||||
children: {
|
||||
// Cat8: {
|
||||
// label: 'Cat8',
|
||||
// children: [
|
||||
// { value: 'TIA Cat 8 Perm.Link', label: 'TIA Cat 8 Perm.Link' },
|
||||
// { value: 'TIA Cat 8 Perm.Link (+ALL)', label: 'TIA Cat 8 Perm.Link (+ALL)' },
|
||||
// { value: 'TIA Cat 8 Perm.Link (+PoE)', label: 'TIA Cat 8 Perm.Link (+PoE)' },
|
||||
// { value: 'TIA Cat 8 Channel', label: 'TIA Cat 8 Channel' },
|
||||
// { value: 'TIA Cat 8 Channel (+ALL)', label: 'TIA Cat 8 Channel (+ALL)' },
|
||||
// { value: 'TIA Cat 8 Channel (+PoE)', label: 'TIA Cat 8 Channel (+PoE)' }
|
||||
// ]
|
||||
// },
|
||||
Cat6A: {
|
||||
label: 'Cat6A',
|
||||
children: [
|
||||
{ value: 'TIA Cat 6A Perm.Link', label: 'TIA Cat 6A Perm.Link' },
|
||||
{ value: 'TIA Cat 6A Perm.Link (+ALL)', label: 'TIA Cat 6A Perm.Link (+ALL)' },
|
||||
{ value: 'TIA Cat 6A Perm.Link (+PoE)', label: 'TIA Cat 6A Perm.Link (+PoE)' },
|
||||
{ value: 'TIA Cat 6A Channel', label: 'TIA Cat 6A Channel' },
|
||||
{ value: 'TIA Cat 6A Channel (+ALL)', label: 'TIA Cat 6A Channel (+ALL)' },
|
||||
{ value: 'TIA Cat 6A Channel (+PoE)', label: 'TIA Cat 6A Channel (+PoE)' },
|
||||
{ value: 'TIA Cat 6A MPTL', label: 'TIA Cat 6A MPTL' },
|
||||
{ value: 'TIA Cat 6A MPTL (+PoE)', label: 'TIA Cat 6A MPTL (+PoE)' },
|
||||
// { value: 'TIA 1005 Cat 6A Perm.Link', label: 'TIA 1005 Cat 6A Perm.Link' },
|
||||
// { value: 'TIA 1005 Cat 6A Channel', label: 'TIA 1005 Cat 6A Channel' },
|
||||
{ value: 'TIA 1005 Cat 6A Channel E1 (+ALL)', label: 'TIA 1005 Cat 6A Channel E1 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6A Channel E1 (+PoE)', label: 'TIA 1005 Cat 6A Channel E1 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 6A Channel E2 (+ALL)', label: 'TIA 1005 Cat 6A Channel E2 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6A Channel E2 (+PoE)', label: 'TIA 1005 Cat 6A Channel E2 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 6A Channel E3 (+ALL)', label: 'TIA 1005 Cat 6A Channel E3 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6A Channel E3 (+PoE)', label: 'TIA 1005 Cat 6A Channel E3 (+PoE)' }
|
||||
]
|
||||
},
|
||||
Cat6: {
|
||||
label: 'Cat6',
|
||||
children: [
|
||||
{ value: 'TIA Cat 6 Perm.Link', label: 'TIA Cat 6 Perm.Link' },
|
||||
{ value: 'TIA Cat 6 Perm.Link (+ALL)', label: 'TIA Cat 6 Perm.Link (+ALL)' },
|
||||
{ value: 'TIA Cat 6 Perm.Link (+PoE)', label: 'TIA Cat 6 Perm.Link (+PoE)' },
|
||||
{ value: 'TIA Cat 6 Channel', label: 'TIA Cat 6 Channel' },
|
||||
{ value: 'TIA Cat 6 Channel (+ALL)', label: 'TIA Cat 6 Channel (+ALL)' },
|
||||
{ value: 'TIA Cat 6 Channel (+PoE)', label: 'TIA Cat 6 Channel (+PoE)' },
|
||||
{ value: 'TIA Cat 6 MPTL', label: 'TIA Cat 6 MPTL' },
|
||||
{ value: 'TIA Cat 6 MPTL (+PoE)', label: 'TIA Cat 6 MPTL (+PoE)' },
|
||||
// { value: 'TIA 1005 Cat 6 Perm.Link', label: 'TIA 1005 Cat 6 Perm.Link' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel', label: 'TIA 1005 Cat 6 Channel' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel (+ALL)', label: 'TIA 1005 Cat 6 Channel (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel (+PoE)', label: 'TIA 1005 Cat 6 Channel (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 6 Channel E1 (+ALL)', label: 'TIA 1005 Cat 6 Channel E1 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel E1 (+PoE)', label: 'TIA 1005 Cat 6 Channel E1 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 6 Channel E2 (+ALL)', label: 'TIA 1005 Cat 6 Channel E2 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel E2 (+PoE)', label: 'TIA 1005 Cat 6 Channel E2 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 6 Channel E3 (+ALL)', label: 'TIA 1005 Cat 6 Channel E3 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 6 Channel E3 (+PoE)', label: 'TIA 1005 Cat 6 Channel E3 (+PoE)' }
|
||||
]
|
||||
},
|
||||
Cat5e: {
|
||||
label: 'Cat5e',
|
||||
children: [
|
||||
{ value: 'TIA Cat 5e Perm.Link', label: 'TIA Cat 5e Perm.Link' },
|
||||
{ value: 'TIA Cat 5e Perm.Link (+ALL)', label: 'TIA Cat 5e Perm.Link (+ALL)' },
|
||||
{ value: 'TIA Cat 5e Perm.Link (+PoE)', label: 'TIA Cat 5e Perm.Link (+PoE)' },
|
||||
{ value: 'TIA Cat 5e Channel', label: 'TIA Cat 5e Channel' },
|
||||
{ value: 'TIA Cat 5e Channel (+ALL)', label: 'TIA Cat 5e Channel (+ALL)' },
|
||||
{ value: 'TIA Cat 5e Channel (+PoE)', label: 'TIA Cat 5e Channel (+PoE)' },
|
||||
{ value: 'TIA Cat 5e MPTL', label: 'TIA Cat 5e MPTL' },
|
||||
{ value: 'TIA Cat 5e MPTL (+PoE)', label: 'TIA Cat 5e MPTL (+PoE)' },
|
||||
// { value: 'TIA 1005 Cat 5e Perm.Link', label: 'TIA 1005 Cat 5e Perm.Link' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel', label: 'TIA 1005 Cat 5e Channel' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel (+ALL)', label: 'TIA 1005 Cat 5e Channel (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel (+PoE)', label: 'TIA 1005 Cat 5e Channel (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 5e Channel E1 (+ALL)', label: 'TIA 1005 Cat 5e Channel E1 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel E1 (+PoE)', label: 'TIA 1005 Cat 5e Channel E1 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 5e Channel E2 (+ALL)', label: 'TIA 1005 Cat 5e Channel E2 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel E2 (+PoE)', label: 'TIA 1005 Cat 5e Channel E2 (+PoE)' },
|
||||
{ value: 'TIA 1005 Cat 5e Channel E3 (+ALL)', label: 'TIA 1005 Cat 5e Channel E3 (+ALL)' },
|
||||
// { value: 'TIA 1005 Cat 5e Channel E3 (+PoE)', label: 'TIA 1005 Cat 5e Channel E3 (+PoE)' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 'ISO',
|
||||
label: 'ISO',
|
||||
children: {
|
||||
// 'Class Ⅱ': {
|
||||
// label: 'Class Ⅱ',
|
||||
// children: [
|
||||
// { value: 'ISO11801 PL Class Ⅱ', label: 'ISO11801 PL Class Ⅱ' },
|
||||
// { value: 'ISO11801 PL Class Ⅱ (+ALL)', label: 'ISO11801 PL Class Ⅱ (+ALL)' },
|
||||
// { value: 'ISO11801 PL Class Ⅱ (+PoE)', label: 'ISO11801 PL Class Ⅱ (+PoE)' },
|
||||
// { value: 'ISO11801 Channel Class Ⅱ', label: 'ISO11801 Channel Class Ⅱ' },
|
||||
// { value: 'ISO11801 Channel Class Ⅱ (+ALL)', label: 'ISO11801 Channel Class Ⅱ (+ALL)' },
|
||||
// { value: 'ISO11801 Channel Class Ⅱ (+PoE)', label: 'ISO11801 Channel Class Ⅱ (+PoE)' }
|
||||
// ],
|
||||
// disabled: true
|
||||
// },
|
||||
// 'Class Ⅰ': {
|
||||
// label: 'Class Ⅰ',
|
||||
// children: [
|
||||
// { value: 'ISO11801 PL Class Ⅰ', label: 'ISO11801 PL Class Ⅰ' },
|
||||
// { value: 'ISO11801 PL Class Ⅰ (+ALL)', label: 'ISO11801 PL Class Ⅰ (+ALL)' },
|
||||
// { value: 'ISO11801 PL Class Ⅰ (+PoE)', label: 'ISO11801 PL Class Ⅰ (+PoE)' },
|
||||
// { value: 'ISO11801 Channel Class Ⅰ', label: 'ISO11801 Channel Class Ⅰ' },
|
||||
// { value: 'ISO11801 Channel Class Ⅰ (+ALL)', label: 'ISO11801 Channel Class Ⅰ (+ALL)' },
|
||||
// { value: 'ISO11801 Channel Class Ⅰ (+PoE)', label: 'ISO11801 Channel Class Ⅰ (+PoE)' }
|
||||
// ],
|
||||
// disabled: true
|
||||
// },
|
||||
// 'Class Fa': {
|
||||
// label: 'Class Fa',
|
||||
// children: [
|
||||
// { value: 'ISO11801 PL2 Class Fa', label: 'ISO11801 PL2 Class Fa' },
|
||||
// { value: 'ISO11801 PL2 Class Fa (+ALL)', label: 'ISO11801 PL2 Class Fa (+ALL)' },
|
||||
// { value: 'ISO11801 PL2 Class Fa (+PoE)', label: 'ISO11801 PL2 Class Fa (+PoE)' },
|
||||
// { value: 'ISO11801 PL3 Class Fa', label: 'ISO11801 PL3 Class Fa' },
|
||||
// { value: 'ISO11801 PL3 Class Fa (+ALL)', label: 'ISO11801 PL3 Class Fa (+ALL)' },
|
||||
// { value: 'ISO11801 PL3 Class Fa (+PoE)', label: 'ISO11801 PL3 Class Fa (+PoE)' },
|
||||
// { value: 'ISO11801 Channel Class Fa', label: 'ISO11801 Channel Class Fa' },
|
||||
// { value: 'ISO11801 Channel Class Fa (+ALL)', label: 'ISO11801 Channel Class Fa (+ALL)' },
|
||||
// { value: 'ISO11801 Channel Class Fa (+PoE)', label: 'ISO11801 Channel Class Fa (+PoE)' }
|
||||
// ],
|
||||
// disabled: true
|
||||
// },
|
||||
// 'Class F': {
|
||||
// label: 'Class F',
|
||||
// children: [
|
||||
// { value: 'ISO11801 PL Class F', label: 'ISO11801 PL Class F' },
|
||||
// { value: 'ISO11801 PL Class F (+ALL)', label: 'ISO11801 PL Class F (+ALL)' },
|
||||
// { value: 'ISO11801 PL Class F (+PoE)', label: 'ISO11801 PL Class F (+PoE)' },
|
||||
// { value: 'ISO11801 Channel Class F', label: 'ISO11801 Channel Class F' },
|
||||
// { value: 'ISO11801 Channel Class F (+ALL)', label: 'ISO11801 Channel Class F (+ALL)' },
|
||||
// { value: 'ISO11801 Channel Class F (+PoE)', label: 'ISO11801 Channel Class F (+PoE)' }
|
||||
// ],
|
||||
// disabled: true
|
||||
// },
|
||||
'Class Ea': {
|
||||
label: 'Class Ea',
|
||||
children: [
|
||||
{ value: 'ISO11801 PL2 Class Ea', label: 'ISO11801 PL2 Class Ea' },
|
||||
{ value: 'ISO11801 PL2 Class Ea (+ALL)', label: 'ISO11801 PL2 Class Ea (+ALL)' },
|
||||
{ value: 'ISO11801 PL2 Class Ea (+PoE)', label: 'ISO11801 PL2 Class Ea (+PoE)' },
|
||||
{ value: 'ISO11801 PL3 Class Ea', label: 'ISO11801 PL3 Class Ea' },
|
||||
{ value: 'ISO11801 PL3 Class Ea (+ALL)', label: 'ISO11801 PL3 Class Ea (+ALL)' },
|
||||
{ value: 'ISO11801 PL3 Class Ea (+PoE)', label: 'ISO11801 PL3 Class Ea (+PoE)' },
|
||||
{ value: 'ISO11801 Channel Class Ea', label: 'ISO11801 Channel Class Ea' },
|
||||
{ value: 'ISO11801 Channel Class Ea (+ALL)', label: 'ISO11801 Channel Class Ea (+ALL)' },
|
||||
{ value: 'ISO11801 Channel Class Ea (+PoE)', label: 'ISO11801 Channel Class Ea (+PoE)' },
|
||||
{ value: 'ISO MPTL Class Ea', label: 'ISO MPTL Class Ea' },
|
||||
{ value: 'ISO MPTL Class Ea (+PoE)', label: 'ISO MPTL Class Ea (+PoE)' }
|
||||
]
|
||||
},
|
||||
'Class E': {
|
||||
label: 'Class E',
|
||||
children: [
|
||||
{ value: 'ISO11801 PL Class E', label: 'ISO11801 PL Class E' },
|
||||
{ value: 'ISO11801 PL Class E (+ALL)', label: 'ISO11801 PL Class E (+ALL)' },
|
||||
{ value: 'ISO11801 PL Class E (+PoE)', label: 'ISO11801 PL Class E (+PoE)' },
|
||||
{ value: 'ISO11801 Channel Class E', label: 'ISO11801 Channel Class E' },
|
||||
{ value: 'ISO11801 Channel Class E (+ALL)', label: 'ISO11801 Channel Class E (+ALL)' },
|
||||
{ value: 'ISO11801 Channel Class E (+PoE)', label: 'ISO11801 Channel Class E (+PoE)' },
|
||||
{ value: 'ISO MPTL Class E', label: 'ISO MPTL Class E' },
|
||||
{ value: 'ISO MPTL Class E (+PoE)', label: 'ISO MPTL Class E (+PoE)' }
|
||||
]
|
||||
},
|
||||
'Class D': {
|
||||
label: 'Class D',
|
||||
children: [
|
||||
{ value: 'ISO11801 PL Class D', label: 'ISO11801 PL Class D' },
|
||||
{ value: 'ISO11801 PL Class D (+ALL)', label: 'ISO11801 PL Class D (+ALL)' },
|
||||
{ value: 'ISO11801 PL Class D (+PoE)', label: 'ISO11801 PL Class D (+PoE)' },
|
||||
{ value: 'ISO11801 Channel Class D', label: 'ISO11801 Channel Class D' },
|
||||
{ value: 'ISO11801 Channel Class D (+ALL)', label: 'ISO11801 Channel Class D (+ALL)' },
|
||||
{ value: 'ISO11801 Channel Class D (+PoE)', label: 'ISO11801 Channel Class D (+PoE)' },
|
||||
{ value: 'ISO MPTL Class D', label: 'ISO MPTL Class D' },
|
||||
{ value: 'ISO MPTL Class D (+PoE)', label: 'ISO MPTL Class D (+PoE)' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
value: '区域',
|
||||
label: '区域',
|
||||
children: {
|
||||
'中国': {
|
||||
label: '中国',
|
||||
children: [
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL no CP', label: 'GB/T 50312-2016 Cat 7A PL no CP' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL no CP (+ALL)', label: 'GB/T 50312-2016 Cat 7A PL no CP (+ALL)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL no CP (+PoE)', label: 'GB/T 50312-2016 Cat 7A PL no CP (+PoE)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL with CP', label: 'GB/T 50312-2016 Cat 7A PL with CP' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL with CP (+ALL)', label: 'GB/T 50312-2016 Cat 7A PL with CP (+ALL)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A PL with CP (+PoE)', label: 'GB/T 50312-2016 Cat 7A PL with CP (+PoE)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A Ch', label: 'GB/T 50312-2016 Cat 7A Ch' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A Ch (+ALL)', label: 'GB/T 50312-2016 Cat 7A Ch (+ALL)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7A Ch (+PoE)', label: 'GB/T 50312-2016 Cat 7A Ch (+PoE)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 PL', label: 'GB/T 50312-2016 Cat 7 PL' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 PL (+ALL)', label: 'GB/T 50312-2016 Cat 7 PL (+ALL)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 PL (+PoE)', label: 'GB/T 50312-2016 Cat 7 PL (+PoE)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 Ch', label: 'GB/T 50312-2016 Cat 7 Ch' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 Ch (+ALL)', label: 'GB/T 50312-2016 Cat 7 Ch (+ALL)' },
|
||||
// { value: 'GBT 50312-2016 Cat 7 Ch (+PoE)', label: 'GB/T 50312-2016 Cat 7 Ch (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL no CP', label: 'GB/T 50312-2016 Cat 6A PL no CP' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL no CP (+ALL)', label: 'GB/T 50312-2016 Cat 6A PL no CP (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL no CP (+PoE)', label: 'GB/T 50312-2016 Cat 6A PL no CP (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL with CP', label: 'GB/T 50312-2016 Cat 6A PL with CP' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL with CP (+ALL)', label: 'GB/T 50312-2016 Cat 6A PL with CP (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A PL with CP (+PoE)', label: 'GB/T 50312-2016 Cat 6A PL with CP (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A Ch', label: 'GB/T 50312-2016 Cat 6A Ch' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A Ch (+ALL)', label: 'GB/T 50312-2016 Cat 6A Ch (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6A Ch (+PoE)', label: 'GB/T 50312-2016 Cat 6A Ch (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 PL', label: 'GB/T 50312-2016 Cat 6 PL' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 PL (+ALL)', label: 'GB/T 50312-2016 Cat 6 PL (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 PL (+PoE)', label: 'GB/T 50312-2016 Cat 6 PL (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 Ch', label: 'GB/T 50312-2016 Cat 6 Ch' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 Ch (+ALL)', label: 'GB/T 50312-2016 Cat 6 Ch (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 6 Ch (+PoE)', label: 'GB/T 50312-2016 Cat 6 Ch (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e PL', label: 'GB/T 50312-2016 Cat 5e PL' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e PL (+ALL)', label: 'GB/T 50312-2016 Cat 5e PL (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e PL (+PoE)', label: 'GB/T 50312-2016 Cat 5e PL (+PoE)' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e Ch', label: 'GB/T 50312-2016 Cat 5e Ch' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e Ch (+ALL)', label: 'GB/T 50312-2016 Cat 5e Ch (+ALL)' },
|
||||
{ value: 'GBT 50312-2016 Cat 5e Ch (+PoE)', label: 'GB/T 50312-2016 Cat 5e Ch (+PoE)' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
value: '跳线',
|
||||
label: '跳线',
|
||||
disabled: true,
|
||||
children: {
|
||||
'Cat6A Patch Cords': {
|
||||
label: 'Cat6A Patch Cords',
|
||||
disabled: true,
|
||||
children: []
|
||||
},
|
||||
'Cat6 Patch Cords': {
|
||||
label: 'Cat6 Patch Cords',
|
||||
disabled: true,
|
||||
children: []
|
||||
},
|
||||
'Cat5e Patch Cords': {
|
||||
label: 'Cat5e Patch Cords',
|
||||
disabled: true,
|
||||
children: []
|
||||
},
|
||||
'M12 Patch Cords': {
|
||||
label: 'M12 Patch Cords',
|
||||
disabled: true,
|
||||
children: []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
value: '应用',
|
||||
label: '应用',
|
||||
disabled: false,
|
||||
children: [
|
||||
{ value: 'Profinet', label: 'Profinet' },
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
WIRE_ORDER: {
|
||||
title: '插座配置',
|
||||
items: [
|
||||
{ value: 'T568A', label: 'T568A' , disabled: true},
|
||||
{ value: 'T568B', label: 'T568B' },
|
||||
{ value: 'Ethernet Two-Pair', label: 'Ethernet Two-Pair' },
|
||||
{ value: 'M12-D Two-Pair', label: 'M12-D Two-Pair' },
|
||||
]
|
||||
},
|
||||
FIBER_TYPE: {
|
||||
title: '光纤类型',
|
||||
subTitle: '电缆组',
|
||||
items: [
|
||||
{
|
||||
value: '定制',
|
||||
label: '定制',
|
||||
disabled: true,
|
||||
children: {}
|
||||
},
|
||||
{
|
||||
value: 'general',
|
||||
label: '通用',
|
||||
children: [
|
||||
{ value: 'OM1 Mulitmode 62.5', label: 'OM1 Mulitmode 62.5' },
|
||||
{ value: 'OM2 Mulitmode 50', label: 'OM2 Mulitmode 50' },
|
||||
{ value: 'OM3 Mulitmode 50', label: 'OM3 Mulitmode 50' },
|
||||
{ value: 'OM4 Mulitmode 50', label: 'OM4 Mulitmode 50' },
|
||||
{ value: 'OM5 Mulitmode 50', label: 'OM5 Mulitmode 50' },
|
||||
{ value: 'OS1 Singlemode', label: 'OS1 Singlemode' },
|
||||
{ value: 'OS2 Singlemode', label: 'OS2 Singlemode' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: '制造商',
|
||||
label: '制造商',
|
||||
disabled: true,
|
||||
children: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
CFP_LIMIT: {
|
||||
title: '测试极限值',
|
||||
subTitle: '极限值组',
|
||||
items: [
|
||||
{
|
||||
value: 'TIA',
|
||||
label: 'TIA',
|
||||
children: [
|
||||
{ value: 'TIA-568.3-E Multimode (STD)', label: 'TIA-568.3-E Multimode (STD)' },
|
||||
{ value: 'TIA-568.3-E Multimode (REF)', label: 'TIA-568.3-E Multimode (REF)' },
|
||||
{ value: 'TIA-568.3-E Singlemode ISP (STD)', label: 'TIA-568.3-E Singlemode ISP (STD)' },
|
||||
{ value: 'TIA-568.3-E Singlemode OSP (STD)', label: 'TIA-568.3-E Singlemode OSP (STD)' },
|
||||
{ value: 'TIA-568.3-E Singlemode ISP (REF)', label: 'TIA-568.3-E Singlemode ISP (REF)' },
|
||||
{ value: 'TIA-568.3-E Singlemode OSP (REF)', label: 'TIA-568.3-E Singlemode OSP (REF)' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'China',
|
||||
label: '中国',
|
||||
children: [
|
||||
{ value: 'GB/T 50312-2016 Fiber Link', label: 'GB/T 50312-2016 Fiber Link' },
|
||||
{ value: 'GB/T 50312-2016 OF-300 Ch', label: 'GB/T 50312-2016 OF-300 Ch' },
|
||||
{ value: 'GB/T 50312-2016 OF-500 Ch', label: 'GB/T 50312-2016 OF-500 Ch' },
|
||||
{ value: 'GB/T 50312-2016 OF-2000 Ch', label: 'GB/T 50312-2016 OF-2000 Ch' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'ISO',
|
||||
label: 'ISO',
|
||||
children: [
|
||||
{ value: 'ISO/IEC 11801-2002 Fibre Link', label: 'ISO/IEC 11801-2002 Fibre Link' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-300 CH', label: 'ISO/IEC 11801-2002 OF-300 CH' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-500 CH', label: 'ISO/IEC 11801-2002 OF-500 CH' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-2000 CH', label: 'ISO/IEC 11801-2002 OF-2000 CH' },
|
||||
{ value: 'ISO/IEC 14763-3:2024 (Draft)', label: 'ISO/IEC 14763-3:2024 (Draft)' },
|
||||
{ value: 'ISO/IEC 14763-3:2014', label: 'ISO/IEC 14763-3:2014' },
|
||||
{ value: 'ISO/IEC 14763-3', label: 'ISO/IEC 14763-3' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
OFP_LIMIT: {
|
||||
title: 'OFP极限值',
|
||||
items: [
|
||||
{
|
||||
value: 'TIA',
|
||||
label: 'TIA',
|
||||
children: [
|
||||
{ value: 'ANSI/TIA-568.3-E', label: 'ANSI/TIA-568.3-E' },
|
||||
{ value: 'ANSI/TIA-568.3-E RL = 20 dB', label: 'ANSI/TIA-568.3-E RL = 20 dB' },
|
||||
{ value: 'ANSI/TIA-568.3-E RL = 35 dB', label: 'ANSI/TIA-568.3-E RL = 35 dB' },
|
||||
{ value: 'ANSI/TIA-568.3-E RL = 55 dB', label: 'ANSI/TIA-568.3-E RL = 55 dB' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'China',
|
||||
label: '中国',
|
||||
children: [
|
||||
{ value: 'GB/T 50312-2016 Fiber Link', label: 'GB/T 50312-2016 Fiber Link' },
|
||||
{ value: 'GB/T 50312-2016 OF-300 Ch', label: 'GB/T 50312-2016 OF-300 Ch' },
|
||||
{ value: 'GB/T 50312-2016 OF-500 Ch', label: 'GB/T 50312-2016 OF-500 Ch' },
|
||||
{ value: 'GB/T 50312-2016 OF-2000 Ch', label: 'GB/T 50312-2016 OF-2000 Ch' }
|
||||
]
|
||||
},
|
||||
{
|
||||
value: 'ISO',
|
||||
label: 'ISO',
|
||||
children: [
|
||||
{ value: 'ISO/IEC 11801-2002 Fibre Link', label: 'ISO/IEC 11801-2002 Fibre Link' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-300 CH', label: 'ISO/IEC 11801-2002 OF-300 CH' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-500 CH', label: 'ISO/IEC 11801-2002 OF-500 CH' },
|
||||
{ value: 'ISO/IEC 11801-2002 OF-2000 CH', label: 'ISO/IEC 11801-2002 OF-2000 CH' },
|
||||
{ value: 'ISO/IEC 14763-3:2024 (Draft)', label: 'ISO/IEC 14763-3:2024 (Draft)' },
|
||||
{ value: 'ISO/IEC 14763-3:2024 RL = 20 (Draft)', label: 'ISO/IEC 14763-3:2024 RL = 20 (Draft)' },
|
||||
{ value: 'ISO/IEC 14763-3:2024 RL = 35 (Draft)', label: 'ISO/IEC 14763-3:2024 RL = 35 (Draft)' },
|
||||
{ value: 'ISO/IEC 14763-3:2024 RL = 60 (Draft)', label: 'ISO/IEC 14763-3:2024 RL = 60 (Draft)' },
|
||||
{ value: 'ISO/IEC 14763-3:2014', label: 'ISO/IEC 14763-3:2014' },
|
||||
{ value: 'ISO/IEC 14763-3', label: 'ISO/IEC 14763-3' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default function MenuList() {
|
||||
const {
|
||||
navigation,
|
||||
getCurrentProject,
|
||||
updateProject,
|
||||
navigateTo,
|
||||
goBack
|
||||
} = useDisplayStore();
|
||||
|
||||
|
||||
|
||||
|
||||
const currentProject = getCurrentProject();
|
||||
const { menuType, selectedConfigId } = navigation.current.params;
|
||||
const [selectedPath, setSelectedPath] = useState([]);
|
||||
const [currentMenu, setCurrentMenu] = useState(menuData[menuType]);
|
||||
|
||||
|
||||
|
||||
// 获取当前选中的配置
|
||||
const currentConfig = currentProject.testConfigs.find(
|
||||
config => config.id === selectedConfigId
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 处理菜单选项选择
|
||||
const handleMenuSelect = (item) => {
|
||||
if (item.disabled) return;
|
||||
|
||||
if (item.children) {
|
||||
// 如果有子菜单,更新路径和当前菜单
|
||||
setSelectedPath([...selectedPath, item]);
|
||||
setCurrentMenu(item.children);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是最终选项,更新配置并返回
|
||||
let updatedConfig = { ...currentConfig };
|
||||
switch (menuType) {
|
||||
case 'CABLE_TYPE':
|
||||
updatedConfig.params.cableType = item.value;
|
||||
break;
|
||||
case 'LIMIT_VALUE':
|
||||
updatedConfig.params.limitValue = item.value;
|
||||
break;
|
||||
case 'WIRE_ORDER':
|
||||
updatedConfig.params.wireOrder = item.value;
|
||||
break;
|
||||
case 'FIBER_TYPE':
|
||||
updatedConfig.params.cableType = item.value;
|
||||
if (updatedConfig.moduleType === 'cfp') {
|
||||
if (item.value.includes('OM')) {
|
||||
updatedConfig.params.limitValue = 'TIA-568.3-E Multimode (STD)';
|
||||
} else {
|
||||
updatedConfig.params.limitValue = 'TIA-568.3-E Singlemode ISP (STD)';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'CFP_LIMIT':
|
||||
updatedConfig.params.limitValue = item.value;
|
||||
break;
|
||||
case 'OFP_LIMIT':
|
||||
updatedConfig.params.limitValue = item.value;
|
||||
break;
|
||||
case 'MODULE':
|
||||
// 如果模块类型发生变化,完全替换配置对象
|
||||
if (currentConfig.moduleType !== item.value) {
|
||||
switch (item.value) {
|
||||
case '8000':
|
||||
updatedConfig = {
|
||||
id: currentConfig.id,
|
||||
moduleType: '8000',
|
||||
modulelable: 'DSX-8000',
|
||||
params: {
|
||||
limitValue: 'TIA Cat 6 Channel',
|
||||
cableType: 'Cat6 U/UTP',
|
||||
wireOrder: 'T568B'
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'cfp':
|
||||
updatedConfig = {
|
||||
id: currentConfig.id,
|
||||
moduleType: 'cfp',
|
||||
modulelable: 'CertiFiber Pro-Quad',
|
||||
params: {
|
||||
limitValue: 'TIA-568.3-E Multimode (STD)',
|
||||
cableType: 'OM3 Multimode 50',
|
||||
refJumper: '1',
|
||||
spliceCount: '0',
|
||||
connectorCount: '2'
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'ofp':
|
||||
updatedConfig = {
|
||||
id: currentConfig.id,
|
||||
moduleType: 'ofp',
|
||||
modulelable: 'OptiFiber Pro-Quad',
|
||||
params: {
|
||||
limitValue: 'General Fiber RL = 35 dB',
|
||||
cableType: 'OM3 Multimode 50'
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
updatedConfig.moduleType = item.value;
|
||||
updatedConfig.modulelable = item.label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新项目数据
|
||||
const updatedConfigs = currentProject.testConfigs.map(config => {
|
||||
if (config.id === selectedConfigId) {
|
||||
return updatedConfig;
|
||||
}
|
||||
return config;
|
||||
});
|
||||
|
||||
const projectIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
updateProject(projectIndex, { testConfigs: updatedConfigs });
|
||||
|
||||
// 返回上一页
|
||||
goBack();
|
||||
};
|
||||
|
||||
// 处理返回上一级
|
||||
const handleBack = () => {
|
||||
if (selectedPath.length > 0) {
|
||||
const newPath = selectedPath.slice(0, -1);
|
||||
setSelectedPath(newPath);
|
||||
if (newPath.length === 0) {
|
||||
setCurrentMenu(menuData[menuType]);
|
||||
} else {
|
||||
let current = menuData[menuType];
|
||||
for (const item of newPath) {
|
||||
current = current.items.find(i => i.value === item.value).children;
|
||||
}
|
||||
setCurrentMenu(current);
|
||||
}
|
||||
} else {
|
||||
goBack();
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染菜单选项
|
||||
const renderMenuOptions = () => {
|
||||
if (!currentMenu) return null;
|
||||
|
||||
// 如果是子菜单,直接渲染子菜单项
|
||||
if (Array.isArray(currentMenu)) {
|
||||
return (
|
||||
<div className="w-full h-[490px]">
|
||||
{currentMenu.map((item) => (
|
||||
<div
|
||||
key={item.value}
|
||||
className={`h-[60px] w-full bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] p-4 shadow-lg flex items-center ${
|
||||
item.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
||||
} ${
|
||||
currentConfig.params.cableType === item.value ? 'bg-blue-500' : ''
|
||||
}`}
|
||||
onClick={() => handleMenuSelect(item)}
|
||||
>
|
||||
<div className="text-black text-sm">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 如果是对象形式的子菜单(如 LIMIT_VALUE 的二级菜单)
|
||||
if (typeof currentMenu === 'object' && !currentMenu.items) {
|
||||
return (
|
||||
<div className="w-full h-[490px]">
|
||||
{Object.entries(currentMenu).map(([key, item]) => (
|
||||
<div
|
||||
key={key}
|
||||
className={`h-[60px] w-full bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] p-4 shadow-lg flex items-center cursor-pointer ${
|
||||
currentConfig.params.cableType === item.value ? 'bg-blue-500' : ''
|
||||
}`}
|
||||
onClick={() => handleMenuSelect(item)}
|
||||
>
|
||||
<div className="text-black text-sm">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 如果是主菜单,渲染主菜单项
|
||||
return (
|
||||
<div className="w-full h-[490px]">
|
||||
{currentMenu.items.map((item) => (
|
||||
<div
|
||||
key={item.value}
|
||||
className={`h-[60px] w-full bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] p-4 shadow-lg flex items-center ${
|
||||
item.disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
|
||||
} ${
|
||||
currentConfig.params.cableType === item.value ? 'bg-blue-500' : ''
|
||||
}`}
|
||||
onClick={() => handleMenuSelect(item)}
|
||||
>
|
||||
<div className="text-black text-sm">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 获取当前菜单标题(返回 null 表示不显示)
|
||||
const getCurrentTitle = () => {
|
||||
if (selectedPath.length === 0) {
|
||||
const subTitle = menuData[menuType]?.subTitle;
|
||||
return subTitle ? subTitle : null; // 如果 subTitle 为空则返回 null
|
||||
}
|
||||
return selectedPath[selectedPath.length - 1].label;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
<TitleBar
|
||||
title={menuData[menuType].title}
|
||||
backTo={selectedPath.length > 0 ? 'menulist' : navigation.previous?.name}
|
||||
view={selectedPath.length > 0 ? 'setup' : navigation.previous?.view}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
{/* 只有当 getCurrentTitle() 返回值不为 null 时才渲染 SubTitleBar */}
|
||||
{getCurrentTitle() !== null && (
|
||||
<SubTitleBar title={getCurrentTitle()} />
|
||||
)}
|
||||
<div className="flex-1 bg-[#303040]">
|
||||
<div className="h-full overflow-y-auto [&::-webkit-scrollbar]:hidden">
|
||||
{renderMenuOptions()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
190
src/components/dsxpage/Operators.js
Normal file
190
src/components/dsxpage/Operators.js
Normal file
@@ -0,0 +1,190 @@
|
||||
import React, { useState } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import Keyboard from '../lib/Keyboard';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
|
||||
export default function Operators() {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const [selectedOperators, setSelectedOperators] = useState([]);
|
||||
const {
|
||||
getCurrentProject,
|
||||
setSelectedIndexes,
|
||||
updateCurrentView,
|
||||
addOperator,
|
||||
updateProject,
|
||||
goBack,
|
||||
setToastMessage
|
||||
} = useDisplayStore();
|
||||
|
||||
const currentProject = getCurrentProject();
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
const handleDeleteOperators = () => {
|
||||
if (selectedOperators.length === currentProject.operators.length) {
|
||||
setToastMessage('至少需要保留一个操作员');
|
||||
return;
|
||||
}
|
||||
|
||||
// 过滤掉被选中的操作员
|
||||
const newOperators = currentProject.operators.filter(
|
||||
operator => !selectedOperators.includes(operator.id)
|
||||
);
|
||||
|
||||
// 更新项目
|
||||
const currentIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
updateProject(currentIndex, { operators: newOperators });
|
||||
|
||||
// 如果当前选中的操作员被删除了,重置选中的操作员索引为0
|
||||
if (selectedOperators.includes(currentProject.operators[useDisplayStore.getState().selectedIndexes.operatorIndex].id)) {
|
||||
setSelectedIndexes({ operatorIndex: 0 });
|
||||
}
|
||||
|
||||
// 清空选中列表并返回main视图
|
||||
setSelectedOperators([]);
|
||||
updateCurrentView('main');
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
switch (view) {
|
||||
case 'main':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-0">
|
||||
<div className="space-y-0">
|
||||
{currentProject.operators.map((operator, index) => (
|
||||
<div
|
||||
key={operator.id}
|
||||
className={`h-[50px] p-4 shadow-lg flex items-center cursor-pointer ${index === useDisplayStore.getState().selectedIndexes.operatorIndex ? 'bg-blue-500' : 'bg-gradient-to-b from-[#e6e3e6] to-[#7b797b]'}`}
|
||||
onClick={() => {
|
||||
setSelectedIndexes({ operatorIndex: index });
|
||||
goBack();
|
||||
}}
|
||||
>
|
||||
<div className="text-black text-sm">{operator.name}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'delete':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-0">
|
||||
<div className="space-y-0">
|
||||
{currentProject.operators.map((operator) => (
|
||||
<div
|
||||
key={operator.id}
|
||||
className="h-[50px] p-4 shadow-lg flex items-center justify-between bg-gradient-to-b from-[#e6e3e6] to-[#7b797b]"
|
||||
>
|
||||
<div className="text-black text-sm">{operator.name}</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="w-4 h-4"
|
||||
checked={selectedOperators.includes(operator.id)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedOperators([...selectedOperators, operator.id]);
|
||||
} else {
|
||||
setSelectedOperators(selectedOperators.filter(id => id !== operator.id));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'new':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black"
|
||||
value={inputValue}
|
||||
placeholder="请输入操作员名称"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
// 保存光标位置
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
if (inputValue.trim()) {
|
||||
const newOperator = {
|
||||
id: Math.random().toString(36).substr(2, 9),
|
||||
name: inputValue.trim()
|
||||
};
|
||||
addOperator(newOperator);
|
||||
setInputValue('');
|
||||
updateCurrentView('main');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
<TitleBar
|
||||
title={
|
||||
view === 'main' ? "操作员" :
|
||||
view === 'new' ? "新操作员" :
|
||||
"删除操作员"
|
||||
}
|
||||
backTo={view === 'main' ? goBack : useDisplayStore.getState().navigation.previous?.name || 'home'}
|
||||
view={useDisplayStore.getState().navigation.previous?.view || 'main'}
|
||||
/>
|
||||
{renderContent()}
|
||||
{view === 'main' && (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-8">
|
||||
<button
|
||||
onClick={() => updateCurrentView('delete')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
编辑列表
|
||||
</button>
|
||||
<button
|
||||
onClick={() => updateCurrentView('new')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
新操作员
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{view === 'delete' && (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
<button
|
||||
onClick={handleDeleteOperators}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
629
src/components/dsxpage/Project.js
Normal file
629
src/components/dsxpage/Project.js
Normal file
@@ -0,0 +1,629 @@
|
||||
import React, { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import SubTitleBar from '../lib/SubTitleBar';
|
||||
import Keyboard from '../lib/Keyboard';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
|
||||
export default function Project() {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const {
|
||||
getCurrentProject,
|
||||
navigation,
|
||||
navigateTo,
|
||||
updateCurrentView,
|
||||
goBack,
|
||||
deleteProject,
|
||||
setSelectedIndexes,
|
||||
updateProject,
|
||||
addProject,
|
||||
setToastMessage
|
||||
} = useDisplayStore();
|
||||
|
||||
// 获取当前项目数据
|
||||
const currentProject = getCurrentProject();
|
||||
const passCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'pass';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'pass';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'pass';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
const failCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'fail';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'fail';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'fail';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
|
||||
const renderContent = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'choose':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4">
|
||||
<div className="space-y-2">
|
||||
{useDisplayStore.getState().projects.map((project, index) => (
|
||||
<div
|
||||
key={project.id}
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => {
|
||||
setSelectedIndexes({ projectIndex: index });
|
||||
setSelectedIndexes({ testConfigIndex: 0 });
|
||||
updateCurrentView('main');
|
||||
}}
|
||||
>
|
||||
<div className="text-black text-sm">{project.name}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'rxtx':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4">
|
||||
<button
|
||||
className="w-full h-[200px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex items-center justify-center"
|
||||
onClick={() => updateCurrentView('rxtx2')}
|
||||
>
|
||||
<div className="text-black text-lg">选择存储设备</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'rxtx2':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 space-y-4">
|
||||
<button className="w-full h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg">
|
||||
<div className="text-black text-lg">导出</div>
|
||||
</button>
|
||||
<button className="w-full h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg">
|
||||
<div className="text-black text-lg">导入</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'setup':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 space-y-4">
|
||||
<button
|
||||
className="w-full h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg"
|
||||
onClick={() => {
|
||||
if (currentProject?.id === 'default') {
|
||||
setToastMessage('默认项目不能重命名');
|
||||
return;
|
||||
}
|
||||
setInputValue(currentProject?.name || '');
|
||||
updateCurrentView('rename');
|
||||
}}
|
||||
>
|
||||
<div className="text-black text-lg">重命名</div>
|
||||
</button>
|
||||
<button
|
||||
className="w-full h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg"
|
||||
onClick={() => {
|
||||
if (currentProject?.id === 'default') {
|
||||
setToastMessage('默认项目不能删除');
|
||||
return;
|
||||
}
|
||||
// 删除当前项目
|
||||
deleteProject(useDisplayStore.getState().selectedIndexes.projectIndex);
|
||||
// 切换到默认项目(索引0)
|
||||
setSelectedIndexes({ projectIndex: 0 });
|
||||
updateCurrentView('main');
|
||||
}}
|
||||
>
|
||||
<div className="text-black text-lg">删除</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'rename':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black mb-4"
|
||||
value={inputValue}
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
if (currentProject?.id === 'default') {
|
||||
setToastMessage('默认项目不能重命名');
|
||||
setInputValue('');
|
||||
updateCurrentView('main');
|
||||
return;
|
||||
}
|
||||
if (inputValue.trim()) {
|
||||
const currentIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
updateProject(currentIndex, { name: inputValue.trim() });
|
||||
setInputValue('');
|
||||
updateCurrentView('main');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'new':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black mb-4"
|
||||
value={inputValue}
|
||||
placeholder="请输入项目名称"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
if (inputValue.trim()) {
|
||||
// 生成唯一ID的函数
|
||||
const generateId = () => Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// 创建新项目
|
||||
const newProject = {
|
||||
id: generateId(),
|
||||
name: inputValue.trim(),
|
||||
testResults: [],
|
||||
testConfigs: [{
|
||||
id: generateId(),
|
||||
moduleType: '8000',
|
||||
modulelable: 'DSX-8000',
|
||||
params: {
|
||||
limitValue: 'TIA Cat 6 Channel',
|
||||
cableType: 'Cat6 U/UTP',
|
||||
wireOrder: 'T568B'
|
||||
}
|
||||
}],
|
||||
cableIds: [{
|
||||
id: generateId(),
|
||||
name: '001'
|
||||
},
|
||||
{
|
||||
id: generateId(),
|
||||
name: '002'
|
||||
},
|
||||
],
|
||||
operators: [{
|
||||
id: generateId(),
|
||||
name: 'Bob'
|
||||
}]
|
||||
};
|
||||
|
||||
// 添加新项目
|
||||
addProject(newProject);
|
||||
|
||||
// 获取新项目的索引
|
||||
const newIndex = useDisplayStore.getState().projects.length - 1;
|
||||
|
||||
// 更新选中索引,重置所有相关索引
|
||||
setSelectedIndexes({
|
||||
projectIndex: newIndex,
|
||||
testConfigIndex: 0,
|
||||
operatorIndex: 0,
|
||||
cableIdIndex: 0
|
||||
});
|
||||
|
||||
// 清空输入并更新视图
|
||||
setInputValue('');
|
||||
updateCurrentView('main');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 space-y-4">
|
||||
{/* 操作员信息 */}
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('operators', 'main')}
|
||||
>
|
||||
<div className="text-black text-sm">操作员: {currentProject?.operators[useDisplayStore.getState().selectedIndexes.operatorIndex]?.name}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 结果统计 */}
|
||||
<div className="space-y-1">
|
||||
<div className="text-sm text-gray-400">
|
||||
结果 {currentProject?.testResults[0]?.date} - {currentProject?.testResults[currentProject.testResults.length - 1]?.date}
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('result', 'main')}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-green-500">{passCount|| 0}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/pass.png"
|
||||
alt="通过"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-red-500">{failCount|| 0}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/fail.png"
|
||||
alt="失败"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 测试设置 */}
|
||||
<div className="space-y-1 flex-1 overflow-hidden">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-400">测试设置</span>
|
||||
<button
|
||||
className="bg-[#404040] text-white px-3 py-1 rounded-sm text-sm"
|
||||
onClick={() => {
|
||||
// 生成唯一ID
|
||||
const generateId = () => Math.random().toString(36).substr(2, 9);
|
||||
|
||||
// 创建新的测试配置
|
||||
const newConfig = {
|
||||
id: generateId(),
|
||||
name: '新测试配置',
|
||||
moduleType: '8000',
|
||||
modulelable: 'DSX-8000',
|
||||
params: {
|
||||
limitValue: 'TIA Cat 6 Channel',
|
||||
cableType: 'Cat6 U/UTP',
|
||||
wireOrder: 'T568B',
|
||||
refJumper: '1',
|
||||
spliceCount: '0',
|
||||
connectorCount: '2'
|
||||
}
|
||||
};
|
||||
|
||||
// 更新项目,添加新的测试配置
|
||||
const currentProjectIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testConfigs: [...currentProject.testConfigs, newConfig]
|
||||
};
|
||||
updateProject(currentProjectIndex, updatedProject);
|
||||
|
||||
// 设置新配置为选中状态
|
||||
const newConfigIndex = updatedProject.testConfigs.length - 1;
|
||||
setSelectedIndexes({ testConfigIndex: newConfigIndex });
|
||||
|
||||
// 跳转到测试配置页面并进入编辑视图
|
||||
navigateTo('testConfig', 'setup');
|
||||
}}
|
||||
>
|
||||
新测试
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-[100px] overflow-y-auto space-y-2 pr-2">
|
||||
{currentProject?.testConfigs.map((config, index) => (
|
||||
<div
|
||||
key={config.id}
|
||||
className="h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg cursor-pointer"
|
||||
onClick={() => navigateTo('testConfig', 'main')}
|
||||
>
|
||||
<div className="flex justify-between">
|
||||
<div className="space-y-0.2">
|
||||
{config.moduleType === '8000' && (
|
||||
<>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
<div className="text-black text-sm">{config.params.wireOrder}</div>
|
||||
</>
|
||||
)}
|
||||
{config.moduleType === 'cfp' && (
|
||||
<>
|
||||
<div className="text-black text-sm">智能远端</div>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
<div className="text-black text-sm">{config.params.refJumper} 跳线参照</div>
|
||||
</>
|
||||
)}
|
||||
{config.moduleType === 'ofp' && (
|
||||
<>
|
||||
<div className="text-black text-sm">自动OTDR</div>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-end justify-between">
|
||||
<span className="text-gray-500 text-sm">{config.modulelable}</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
className="w-6 h-6 bg-[#404040] rounded-sm flex items-center justify-center"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(); // 阻止事件冒泡
|
||||
const currentProjectIndex = useDisplayStore.getState().selectedIndexes.projectIndex;
|
||||
const currentTestConfigIndex = useDisplayStore.getState().selectedIndexes.testConfigIndex;
|
||||
const selectedConfigId = currentProject.testConfigs[currentTestConfigIndex]?.id;
|
||||
|
||||
// 禁止删除最后一项配置
|
||||
if (currentProject.testConfigs.length <= 1) {
|
||||
setToastMessage('不能删除最后一项配置');
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新项目,删除选中的测试配置
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testConfigs: currentProject.testConfigs.filter(
|
||||
testConfig => testConfig.id !== config.id
|
||||
)
|
||||
};
|
||||
updateProject(currentProjectIndex, updatedProject);
|
||||
|
||||
// 更新选中索引
|
||||
const newIndex = updatedProject.testConfigs.findIndex(
|
||||
testConfig => testConfig.id === selectedConfigId
|
||||
);
|
||||
|
||||
// 如果删除的是当前选中的配置,或者找不到选中的配置ID,则设置索引为0
|
||||
setSelectedIndexes({
|
||||
testConfigIndex: newIndex === -1 ? 0 : newIndex
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="w-4 h-4 relative">
|
||||
<Image
|
||||
src="/delete.png"
|
||||
alt="删除"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 电缆ID集 */}
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm text-gray-400">电缆ID集</span>
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('cableId', 'main')}
|
||||
>
|
||||
<div className="text-black text-sm">
|
||||
下一个ID: {currentProject?.cableIds?.[0]?.name || '001'}
|
||||
</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderTitleBar = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'choose':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="更改项目" backTo={null} view="main" />
|
||||
<SubTitleBar title="选择您要的项目" />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'rxtx':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="传输项目" backTo={null} view="main" />
|
||||
<SubTitleBar title="选择存储设备" />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'rxtx2':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="传输项目" backTo={null} view="rxtx" />
|
||||
<SubTitleBar title="选择所需操作" />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'setup':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="管理项目" backTo={null} view="main" />
|
||||
<SubTitleBar title={currentProject?.name || 'DEFAULT'} />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'rename':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="重命名" backTo={null} view="setup" />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'new':
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="新项目" backTo={null} view="choose" />
|
||||
</>
|
||||
);
|
||||
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<TitleBar title="项目" backTo="home" view="main" />
|
||||
<SubTitleBar title={currentProject?.name || 'DEFAULT'} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'choose':
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-8">
|
||||
<button
|
||||
onClick={() => updateCurrentView('new')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
新项目
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'rxtx':
|
||||
case 'rxtx2':
|
||||
case 'setup':
|
||||
case 'rename':
|
||||
case 'new':
|
||||
return null;
|
||||
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-8">
|
||||
<button
|
||||
onClick={() => updateCurrentView('choose')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
更改项目
|
||||
</button>
|
||||
<button
|
||||
onClick={() => updateCurrentView('rxtx')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
传输
|
||||
</button>
|
||||
<button
|
||||
onClick={() => updateCurrentView('setup')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
管理
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{renderTitleBar()}
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
661
src/components/dsxpage/Result.js
Normal file
661
src/components/dsxpage/Result.js
Normal file
@@ -0,0 +1,661 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import SubTitleBar from '../lib/SubTitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
import Keyboard from '../lib/Keyboard';
|
||||
|
||||
export default function Result() {
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
const { getCurrentProject,setToastMessage } = useDisplayStore();
|
||||
const currentProject = getCurrentProject();
|
||||
const [isManageMode, setIsManageMode] = useState(false);
|
||||
const [selectedResults, setSelectedResults] = useState([]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [showKeyboard, setShowKeyboard] = useState(true);
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
// 获取当前项目名称
|
||||
const projectName = currentProject?.name || '';
|
||||
|
||||
// 获取测试结果并统计通过/失败数量
|
||||
const testResults = currentProject?.testResults || [];
|
||||
const passCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'pass';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'pass';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'pass';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
const failCount = currentProject?.testResults.filter(result => {
|
||||
const copperStatus = result.CopperResultStatus;
|
||||
const cfpStatus = result.CFPResultStatus;
|
||||
const ofpStatus = result.ofpResultStatus;
|
||||
|
||||
if (copperStatus) {
|
||||
return copperStatus === 'fail';
|
||||
} else if (cfpStatus) {
|
||||
return cfpStatus === 'fail';
|
||||
} else if (ofpStatus) {
|
||||
return ofpStatus === 'fail';
|
||||
}
|
||||
return false;
|
||||
}).length;
|
||||
|
||||
// 对测试结果进行排序:失败在前,通过在后,相同结果按name排序(数字部分按数值大小排序)
|
||||
const sortedResults = [...testResults].sort((a, b) => {
|
||||
const aPass = a.resultdata?.result === 'pass';
|
||||
const bPass = b.resultdata?.result === 'pass';
|
||||
|
||||
// 如果通过状态相同,则按name排序
|
||||
if (aPass === bPass) {
|
||||
// 将名称分割成文本和数字部分
|
||||
const splitName = (name) => {
|
||||
const match = name.match(/^(.*?)(\d+)$/);
|
||||
if (match) {
|
||||
return {
|
||||
text: match[1],
|
||||
number: parseInt(match[2], 10)
|
||||
};
|
||||
}
|
||||
return { text: name, number: 0 };
|
||||
};
|
||||
|
||||
const nameA = splitName(a.name);
|
||||
const nameB = splitName(b.name);
|
||||
|
||||
// 先比较文本部分
|
||||
if (nameA.text !== nameB.text) {
|
||||
return nameA.text.localeCompare(nameB.text);
|
||||
}
|
||||
// 文本相同则比较数字部分
|
||||
return nameA.number - nameB.number;
|
||||
}
|
||||
|
||||
return aPass ? 1 : -1;
|
||||
});
|
||||
|
||||
// 处理选择结果
|
||||
const handleSelect = (result) => {
|
||||
setSelectedResults(prev => {
|
||||
if (prev.includes(result.name)) {
|
||||
return prev.filter(name => name !== result.name);
|
||||
} else {
|
||||
return [...prev, result.name];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 处理删除选中的结果
|
||||
const handleDelete = () => {
|
||||
if (selectedResults.length === 0) return;
|
||||
|
||||
const newResults = testResults.filter(result => !selectedResults.includes(result.name));
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testResults: newResults
|
||||
};
|
||||
const projectIndex = useDisplayStore.getState().projects.findIndex(p => p.id === currentProject.id);
|
||||
useDisplayStore.getState().updateProject(projectIndex, updatedProject);
|
||||
setSelectedResults([]);
|
||||
setIsManageMode(false);
|
||||
setShowDeleteDialog(false);
|
||||
|
||||
};
|
||||
|
||||
|
||||
// 重命名逻辑
|
||||
useEffect(() => {
|
||||
if (view === 'rename' && selectedResults.length === 1) {
|
||||
const selectedResult = testResults.find(result => result.name === selectedResults[0]);
|
||||
setInputValue(selectedResult.name);
|
||||
}
|
||||
}, [view, selectedResults, testResults]);
|
||||
|
||||
// 处理重命名完成
|
||||
const handleRename = () => {
|
||||
if (!inputValue.trim()) return;
|
||||
|
||||
// 检查是否与其他结果名称冲突
|
||||
const isDuplicate = testResults.some(result =>
|
||||
result.name !== selectedResults[0] && result.name === inputValue.trim()
|
||||
);
|
||||
|
||||
if (isDuplicate) {
|
||||
setToastMessage('该名称已存在,请使用其他名称');
|
||||
return;
|
||||
}
|
||||
|
||||
const newResults = testResults.map(result => {
|
||||
console.log(testResults);
|
||||
console.log(result);
|
||||
|
||||
if (result.name === selectedResults[0]) {
|
||||
// 处理CFP模块类型的特殊重命名逻辑
|
||||
if (result.testconfig?.moduleType === 'cfp') {
|
||||
const oldName = result.name;
|
||||
const newName = inputValue.trim();
|
||||
|
||||
// 判断旧名字是input还是output
|
||||
if (oldName === result.inputname) {
|
||||
// 查找关联结果:inputname相同但name不同的结果
|
||||
const relatedResult = testResults.find(r => r.inputname === result.inputname && r.name !== result.name);
|
||||
console.log("找到的关联结果:", relatedResult);
|
||||
// 如果是当前选中的结果,更新name和inputname
|
||||
if (result.name === selectedResults[0]) {
|
||||
// 更新选中结果的name和inputname
|
||||
const updatedResult = { ...result, name: newName, inputname: newName };
|
||||
// 查找并更新关联结果的inputname
|
||||
const relatedResult = testResults.find(r => r.inputname === result.inputname && r.name !== result.name);
|
||||
if (relatedResult) {
|
||||
relatedResult.inputname = newName;
|
||||
}
|
||||
return updatedResult;
|
||||
}
|
||||
return result;
|
||||
} else if (oldName === result.outname) {
|
||||
// 查找关联结果:outname相同但name不同的结果
|
||||
if (result.name === selectedResults[0]) {
|
||||
// outname
|
||||
const updatedResult = { ...result, name: newName, outname: newName };
|
||||
// outname
|
||||
const relatedResult = testResults.find(r => r.outname === result.outname && r.name !== result.name);
|
||||
if (relatedResult) {
|
||||
relatedResult.outname = newName;
|
||||
}
|
||||
return updatedResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return { ...result, name: inputValue.trim() };
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testResults: newResults
|
||||
};
|
||||
|
||||
const projectIndex = useDisplayStore.getState().projects.findIndex(p => p.id === currentProject.id);
|
||||
useDisplayStore.getState().updateProject(projectIndex, updatedProject);
|
||||
useDisplayStore.getState().navigateTo('result', 'main');
|
||||
|
||||
// 重置管理状态和选中结果状态
|
||||
setIsManageMode(false);
|
||||
setSelectedResults([]);
|
||||
|
||||
};
|
||||
|
||||
//确认删除提示框
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
//确认移动提示框
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
const [targetProject, setTargetProject] = useState(null);
|
||||
const [showOverwriteDialog, setShowOverwriteDialog] = useState(false);
|
||||
const [duplicateResults, setDuplicateResults] = useState([]);
|
||||
|
||||
const moveResults = (project, index) => {
|
||||
// 检查是否选择了结果和目标项目
|
||||
if (selectedResults.length === 0 || !project) return;
|
||||
|
||||
// 检查是否在同一个项目中移动
|
||||
if (project.id === currentProject.id) {
|
||||
setToastMessage('不能移动到相同项目');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查目标项目中是否有重名结果
|
||||
const duplicates = selectedResults.filter(resultName =>
|
||||
project.testResults.some(targetResult => targetResult.name === resultName)
|
||||
);
|
||||
|
||||
if (duplicates.length > 0) {
|
||||
setDuplicateResults(duplicates);
|
||||
setTargetProject(project);
|
||||
setShowOverwriteDialog(true);
|
||||
} else {
|
||||
setTargetProject(project);
|
||||
setShowConfirmDialog(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMove = (overwrite = false) => {
|
||||
if (!targetProject) return;
|
||||
|
||||
// 获取要移动的结果对象
|
||||
const resultsToMove = testResults.filter(result => selectedResults.includes(result.name));
|
||||
|
||||
// 更新目标项目
|
||||
const updatedTargetResults = overwrite
|
||||
? [...targetProject.testResults.filter(result => !selectedResults.includes(result.name)), ...resultsToMove]
|
||||
: [...targetProject.testResults, ...resultsToMove];
|
||||
|
||||
const updatedTargetProject = {
|
||||
...targetProject,
|
||||
testResults: updatedTargetResults
|
||||
};
|
||||
|
||||
// 更新当前项目
|
||||
const updatedCurrentResults = testResults.filter(result => !selectedResults.includes(result.name));
|
||||
const updatedCurrentProject = {
|
||||
...currentProject,
|
||||
testResults: updatedCurrentResults
|
||||
};
|
||||
|
||||
// 保存更改
|
||||
const targetIndex = useDisplayStore.getState().projects.findIndex(p => p.id === targetProject.id);
|
||||
const currentIndex = useDisplayStore.getState().projects.findIndex(p => p.id === currentProject.id);
|
||||
|
||||
useDisplayStore.getState().updateProject(targetIndex, updatedTargetProject);
|
||||
useDisplayStore.getState().updateProject(currentIndex, updatedCurrentProject);
|
||||
|
||||
// 重置状态
|
||||
setSelectedResults([]);
|
||||
setIsManageMode(false);
|
||||
setShowConfirmDialog(false);
|
||||
setShowOverwriteDialog(false);
|
||||
setTargetProject(null);
|
||||
setDuplicateResults([]);
|
||||
|
||||
// 显示成功消息并返回主视图
|
||||
setToastMessage('移动成功');
|
||||
useDisplayStore.getState().navigateTo('result', 'main');
|
||||
};
|
||||
|
||||
// 渲染标题栏
|
||||
const renderTitleBar = () => {
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'rename':
|
||||
return (
|
||||
<TitleBar
|
||||
title="重命名结果"
|
||||
backTo={useDisplayStore.getState().navigation.previous?.name || 'home'}
|
||||
view={useDisplayStore.getState().navigation.previous?.view || 'main'}
|
||||
/>
|
||||
);
|
||||
case 'move':
|
||||
return (
|
||||
<TitleBar
|
||||
title="移至"
|
||||
backTo={useDisplayStore.getState().navigation.previous?.name || 'home'}
|
||||
view={useDisplayStore.getState().navigation.previous?.view || 'main'}
|
||||
/>
|
||||
);
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<TitleBar
|
||||
title="结果"
|
||||
backTo={'home'}
|
||||
view={'main'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染主要内容
|
||||
const renderContent = () => {
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'rename':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div
|
||||
className="relative mb-4 cursor-pointer"
|
||||
onClick={() => setShowKeyboard(true)}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black"
|
||||
value={inputValue}
|
||||
placeholder="请输入测试结果名称"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{showKeyboard && (
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
setShowKeyboard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case 'move':
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] flex flex-col relative">
|
||||
<SubTitleBar title="选择您要的项目" />
|
||||
|
||||
<div className="space-y-2 p-4">
|
||||
{useDisplayStore.getState().projects.map((project, index) => (
|
||||
<div
|
||||
key={project.id}
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => {
|
||||
moveResults(project, index);
|
||||
}}
|
||||
>
|
||||
<div className="text-black text-sm">{project.name}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
<div className="h-[60px] bg-gradient-to-b from-[#b0b0b0] via-[#e0e4e0] to-[#b0b0b0] px-4 flex flex-col justify-center">
|
||||
<div className="text-black">{projectName}</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-green-500">{passCount}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/pass.png"
|
||||
alt="通过"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-red-500">{failCount}</span>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/fail.png"
|
||||
alt="失败"
|
||||
sizes={"auto"}
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isManageMode && (
|
||||
<div className="h-[40px] px-4 bg-[#efebe6] flex items-center border-b border-gray-600">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedResults.length === sortedResults.length}
|
||||
onChange={() => {
|
||||
if (selectedResults.length === sortedResults.length) {
|
||||
setSelectedResults([]);
|
||||
} else {
|
||||
setSelectedResults(sortedResults.map(result => result.name));
|
||||
}
|
||||
}}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<span className="text-black">选择全部</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 bg-[#303040] overflow-y-auto" style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
|
||||
<div className="space-y-0">
|
||||
{sortedResults.map((result, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="h-[60px] px-4 bg-[#efebe6] flex items-center justify-between border-b border-gray-600 cursor-pointer"
|
||||
onClick={() => !isManageMode && useDisplayStore.getState().navigateTo('resultinfo', 'save', result)}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
{isManageMode && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedResults.includes(result.name)}
|
||||
onChange={() => handleSelect(result)}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
)}
|
||||
<span className="text-black">{result.name}</span>
|
||||
</div>
|
||||
<img
|
||||
src={
|
||||
result.CopperResultStatus === 'pass' ||
|
||||
result.ofpResultStatus === 'pass' ||
|
||||
(result.name === result.inputname && result.InPortStatus === 'pass') ||
|
||||
(result.name === result.outname && result.OutPortStatus === 'pass')
|
||||
? '/pass.png'
|
||||
: '/fail.png'
|
||||
}
|
||||
alt={
|
||||
result.CopperResultStatus === 'pass' ||
|
||||
result.ofpResultStatus === 'pass' ||
|
||||
(result.name === result.inputname && result.InPortStatus === 'pass') ||
|
||||
(result.name === result.outname && result.OutPortStatus === 'pass')
|
||||
? '通过'
|
||||
: '失败'
|
||||
}
|
||||
className="w-7 h-7"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染底部按钮
|
||||
const renderFooter = () => {
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'rename':
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
{!showKeyboard && (
|
||||
<button
|
||||
onClick={handleRename}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case 'move':
|
||||
return(
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-5">
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'main':
|
||||
default:
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-5">
|
||||
<div className="flex-1">
|
||||
<button
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
onClick={() => {
|
||||
if (selectedResults.length > 0) {
|
||||
useDisplayStore.getState().navigateTo('result', 'move');
|
||||
}
|
||||
}}
|
||||
disabled={selectedResults.length === 0}
|
||||
style={{ opacity: selectedResults.length > 0 ? 1 : 0.5 }}
|
||||
>
|
||||
移动
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 flex justify-center">
|
||||
<button
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
onClick={() => {
|
||||
if (selectedResults.length === 1) {
|
||||
const selectedResult = testResults.find(result => result.name === selectedResults[0]);
|
||||
useDisplayStore.getState().navigateTo('result', 'rename', selectedResult);
|
||||
}
|
||||
}}
|
||||
disabled={selectedResults.length !== 1}
|
||||
style={{ opacity: selectedResults.length === 1 ? 1 : 0.5 }}
|
||||
>
|
||||
重命名
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 flex justify-end">
|
||||
<button
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
onClick={() => isManageMode ? setShowDeleteDialog(true) : setIsManageMode(true)}
|
||||
>
|
||||
{isManageMode ? '删除' : '管理'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{renderTitleBar()}
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
{/* 确认删除对话框 */}
|
||||
{showDeleteDialog && (
|
||||
<div className="w-[480px] h-[640px] bg-[#002842d4] absolute z-9999">
|
||||
<div className='pl-10 pt-60'>
|
||||
<div className="bg-[#2B3C5B] rounded-lg p-6 w-[400px] min-h-[200px] flex flex-col">
|
||||
<h3 className="text-white text-xl font-bold mb-4">提示</h3>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-white text-lg">是否删除选中的测试结果?</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<div className="flex justify-center gap-10">
|
||||
<button
|
||||
className="text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => setShowDeleteDialog(false)}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
className="text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => handleDelete()}
|
||||
>
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 确认移动对话框 */}
|
||||
{showConfirmDialog && (
|
||||
<div className="w-[480px] h-[640px] bg-[#002842d4] absolute z-9999">
|
||||
<div className='pl-10 pt-60'>
|
||||
<div className="bg-[#2B3C5B] rounded-lg p-6 w-[400px] min-h-[200px] flex flex-col">
|
||||
<h3 className="text-white text-xl font-bold mb-4">提示</h3>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-white text-lg">是否将选中的测试结果移动到项目 {targetProject?.name}?</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<div className="flex justify-center gap-10">
|
||||
<button
|
||||
className=" text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => setShowConfirmDialog(false)}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
className=" text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => handleMove(false)}
|
||||
>
|
||||
确认
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)}
|
||||
{/* 重名覆盖确认对话框 */}
|
||||
{showOverwriteDialog && (
|
||||
<div className="w-[480px] h-[640px] bg-[#002842d4] absolute z-9999">
|
||||
<div className='pl-10 pt-60'>
|
||||
<div className="bg-[#2B3C5B] rounded-lg p-6 w-[400px] min-h-[200px] flex flex-col">
|
||||
<h3 className="text-white text-xl font-bold mb-4">提示</h3>
|
||||
<div className="flex-1 flex flex-col items-center justify-center">
|
||||
<div className="text-white text-lg mb-4">以下测试结果在目标项目中已存在:</div>
|
||||
<ul className="mb-4 list-disc pl-6">
|
||||
{duplicateResults.map((name, index) => (
|
||||
<li key={index} className="text-red-500">{name}</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="text-white text-lg">是否覆盖这些结果?</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<div className="flex justify-center gap-10">
|
||||
<button
|
||||
className="text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => {
|
||||
setShowOverwriteDialog(false);
|
||||
setTargetProject(null);
|
||||
setDuplicateResults([]);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
className="text-white text-s font-bold px-4 py-2 bg-[#1d3155] rounded-md hover:bg-[#2b4466]"
|
||||
onClick={() => handleMove(true)}
|
||||
>
|
||||
覆盖
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
658
src/components/dsxpage/ResultInfo.js
Normal file
658
src/components/dsxpage/ResultInfo.js
Normal file
@@ -0,0 +1,658 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import ResultTitleBar from '../lib/ResultTitleBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import CopperResultMain from '../lib/CopperResultMain';
|
||||
import OLTSResultMain from '../lib/OLTSResultMain';
|
||||
import OTDRResultMain from '../lib/OTDRResultMain';
|
||||
import Keyboard from '../lib/Keyboard';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
|
||||
// 初始化测试结果音效对象
|
||||
const testPassSound = typeof Audio !== 'undefined' ? new Audio('/sounds/test_pass.wav') : null;
|
||||
const testFailSound = typeof Audio !== 'undefined' ? new Audio('/sounds/test_fail.wav') : null;
|
||||
|
||||
// 自定义确认弹窗组件
|
||||
const ConfirmDialog = ({ message, onConfirm, onCancel }) => (
|
||||
<div className="w-[480px] h-[640px] bg-[#002842d4] absolute z-[9999] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
|
||||
<div className='pl-10 pt-60'>
|
||||
<div className="bg-[#2B3C5B] rounded-lg p-6 w-[400px] min-h-[200px] flex flex-col">
|
||||
<h3 className="text-white text-xl font-bold mb-4">提示</h3>
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-white text-lg">{message}</div>
|
||||
</div>
|
||||
<div className="flex justify-center gap-4 mt-4">
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="bg-[#354e7a] text-white px-6 py-2 rounded hover:bg-[#1E293B] transition-colors"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="bg-[#4a4a4a] text-white px-6 py-2 rounded hover:bg-[#3a3a3a] transition-colors"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default function ResultInfo() {
|
||||
const [showKeyboard, setShowKeyboard] = useState(true);
|
||||
const [cursorPosition, setCursorPosition] = useState(0);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [inputValue2, setInputValue2] = useState('');
|
||||
const [activeInput, setActiveInput] = useState(1); // 1 表示第一个输入框,2 表示第二个输入框
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
const [confirmDialogMessage, setConfirmDialogMessage] = useState('');
|
||||
const [confirmDialogCallback, setConfirmDialogCallback] = useState(null);
|
||||
const { navigation } = useDisplayStore.getState();
|
||||
const tempTestResult = navigation.current.params;
|
||||
|
||||
// 根据测试结果播放音效
|
||||
const { hasPlayedSound } = useDisplayStore.getState();
|
||||
|
||||
useEffect(() => {
|
||||
// 只有从测试页面进入nosave视图时才播放声音
|
||||
if (navigation.previous.name === 'testing' &&
|
||||
tempTestResult &&
|
||||
!hasPlayedSound) {
|
||||
if (tempTestResult.CopperResultStatus === 'pass'||tempTestResult.CFPResultStatus === 'pass' ||tempTestResult.ofpResultStatus === 'pass') {
|
||||
testPassSound?.play().catch(console.error);
|
||||
} else {
|
||||
testFailSound?.play().catch(console.error);
|
||||
}
|
||||
useDisplayStore.setState({ hasPlayedSound: true });
|
||||
}
|
||||
|
||||
|
||||
}, [navigation, tempTestResult, hasPlayedSound]);
|
||||
|
||||
// 从URL参数中获取临时测试结果
|
||||
const {
|
||||
getCurrentProject,
|
||||
getCurrentCableId,
|
||||
getCurrentCableId2,
|
||||
updateCurrentView,
|
||||
getCurrentTestConfig,
|
||||
navigateTo,
|
||||
setToastMessage,
|
||||
updateProject
|
||||
} = useDisplayStore();
|
||||
|
||||
const currentProject = getCurrentProject();
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
|
||||
// 获取当前的线缆ID
|
||||
const currentCableId = getCurrentCableId().name || '';
|
||||
const currentCableId2 = getCurrentCableId2().name || '';
|
||||
// 计算下一个序号的ID
|
||||
const getNextId = (currentId) => {
|
||||
if (!currentId) return '';
|
||||
|
||||
// 检查是否以数字结尾
|
||||
const numMatch = currentId.match(/^(.*?)(\d+)$/);
|
||||
if (numMatch) {
|
||||
const [, prefix, num] = numMatch;
|
||||
const nextNum = String(Number(num) + 1).padStart(num.length, '0');
|
||||
return prefix + nextNum;
|
||||
}
|
||||
|
||||
// 检查是否以字母结尾
|
||||
const letterMatch = currentId.match(/^(.*?)([a-zA-Z]+)$/);
|
||||
if (letterMatch) {
|
||||
const [, prefix, letters] = letterMatch;
|
||||
|
||||
// 将字母转换为数组以便处理
|
||||
const letterArray = letters.split('');
|
||||
let carry = true;
|
||||
|
||||
// 从右向左处理每个字母
|
||||
for (let i = letterArray.length - 1; i >= 0 && carry; i--) {
|
||||
if (letterArray[i] === 'z') {
|
||||
letterArray[i] = 'a';
|
||||
carry = true;
|
||||
} else if (letterArray[i] === 'Z') {
|
||||
letterArray[i] = 'A';
|
||||
carry = true;
|
||||
} else {
|
||||
letterArray[i] = String.fromCharCode(letterArray[i].charCodeAt(0) + 1);
|
||||
carry = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还有进位,说明需要在前面添加一个字母
|
||||
if (carry) {
|
||||
if (letters[0] >= 'a' && letters[0] <= 'z') {
|
||||
letterArray.unshift('a');
|
||||
} else {
|
||||
letterArray.unshift('A');
|
||||
}
|
||||
}
|
||||
|
||||
return prefix + letterArray.join('');
|
||||
}
|
||||
|
||||
// 如果既不是数字也不是字母结尾,直接返回原值
|
||||
return currentId;
|
||||
};
|
||||
|
||||
// 初始化输入值为当前的线缆ID
|
||||
useEffect(() => {
|
||||
setInputValue(currentCableId);
|
||||
setInputValue2(currentCableId2);
|
||||
}, [currentCableId, currentCableId2]);
|
||||
|
||||
// 当第一个输入框值变化时,仅在用户手动输入时自动更新第二个输入框
|
||||
useEffect(() => {
|
||||
if (tempTestResult?.testconfig?.moduleType === 'cfp' && inputValue && inputValue !== currentCableId) {
|
||||
setInputValue2(getNextId(inputValue));
|
||||
}
|
||||
}, [inputValue, view, currentCableId]);
|
||||
|
||||
//创建保存结果存储逻辑
|
||||
const handleComplete = () => {
|
||||
const currentId = inputValue.trim();
|
||||
|
||||
// 检查是否存在重名的测试结果
|
||||
const currentProject = getCurrentProject();
|
||||
const existingResults = currentProject?.testResults || [];
|
||||
|
||||
// 检查两个ID是否重复
|
||||
const isDuplicate = existingResults.some(result => result.name === currentId);
|
||||
|
||||
// 重名替换
|
||||
if (isDuplicate) {
|
||||
setConfirmDialogMessage('该线缆ID已使用,是否覆盖?');
|
||||
setConfirmDialogCallback(() => () => {
|
||||
// 更新临时测试结果的名称
|
||||
const updatedTestResult = {
|
||||
...tempTestResult,
|
||||
name: currentId
|
||||
};
|
||||
|
||||
// 更新当前项目的测试结果
|
||||
const currentIndex = useDisplayStore.getState().projects.findIndex(p => p === currentProject);
|
||||
if (currentIndex !== -1) {
|
||||
// 移除旧的测试结果并添加新的
|
||||
const newResults = existingResults.filter(result => result.name !== currentId);
|
||||
updateProject(currentIndex, {
|
||||
testResults: [...newResults, updatedTestResult]
|
||||
});
|
||||
// 更新navigation.current.params中的测试结果名称
|
||||
useDisplayStore.setState({
|
||||
navigation: {
|
||||
...navigation,
|
||||
current: {
|
||||
...navigation.current,
|
||||
params: updatedTestResult
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
updateCurrentView('save');
|
||||
setShowConfirmDialog(false);
|
||||
});
|
||||
setShowConfirmDialog(true);
|
||||
return;
|
||||
}
|
||||
// 更新临时测试结果的名称并保存到项目中
|
||||
if (tempTestResult) {
|
||||
const updatedTestResult = {
|
||||
...tempTestResult,
|
||||
name: currentId
|
||||
};
|
||||
|
||||
// 更新当前项目的cableIds.name为下一个ID
|
||||
const currentIndex = useDisplayStore.getState().projects.findIndex(p => p === currentProject);
|
||||
if (currentIndex !== -1) {
|
||||
//更新测试结果
|
||||
updateProject(currentIndex, {
|
||||
testResults: [...(currentProject.testResults || []), updatedTestResult]
|
||||
});
|
||||
|
||||
// 更新navigation.current.params中的测试结果名称
|
||||
const navigation = useDisplayStore.getState().navigation;
|
||||
useDisplayStore.setState({
|
||||
navigation: {
|
||||
...navigation,
|
||||
current: {
|
||||
...navigation.current,
|
||||
params: updatedTestResult
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取下一个ID
|
||||
const nextId = (() => {
|
||||
const currentId = inputValue.trim();
|
||||
if (!currentId) return currentId;
|
||||
|
||||
// 获取最后一个字符
|
||||
const lastChar = currentId.slice(-1);
|
||||
const prefix = currentId.slice(0, -1);
|
||||
|
||||
// 如果最后一个字符是数字
|
||||
if (/\d/.test(lastChar)) {
|
||||
const match = currentId.match(/^(.*?)(\d+)$/);
|
||||
if (match) {
|
||||
const numPrefix = match[1];
|
||||
const number = parseInt(match[2]) + 1;
|
||||
return `${numPrefix}${number.toString().padStart(match[2].length, '0')}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果最后一个字符是字母
|
||||
if (/[A-Za-z]/.test(lastChar)) {
|
||||
const nextChar = String.fromCharCode(lastChar.charCodeAt(0) + 1);
|
||||
// 如果超过Z或z,回到A或a
|
||||
if ((lastChar === 'Z' && nextChar > 'Z') || (lastChar === 'z' && nextChar > 'z')) {
|
||||
const baseChar = lastChar === 'Z' ? 'A' : 'a';
|
||||
return `${prefix}${baseChar}`;
|
||||
}
|
||||
return `${prefix}${nextChar}`;
|
||||
}
|
||||
|
||||
return currentId;
|
||||
})();
|
||||
|
||||
// 获取当前项目的所有cableIds
|
||||
const currentCableIds = currentProject?.cableIds || [];
|
||||
const selectedId = getCurrentCableId().id;
|
||||
|
||||
// 只更新选中的ID,保留其他ID不变
|
||||
const updatedCableIds = currentCableIds.map(cable =>
|
||||
cable.id === selectedId ? { ...cable, name: nextId } : cable
|
||||
);
|
||||
|
||||
// 更新项目
|
||||
updateProject(currentIndex, {
|
||||
cableIds: updatedCableIds
|
||||
});
|
||||
}
|
||||
updateCurrentView('save');
|
||||
}
|
||||
}
|
||||
const handleComplete2 = () => {
|
||||
const currentId = inputValue.trim();
|
||||
const currentId2 = inputValue2.trim();
|
||||
|
||||
// 检查两个ID是否相同
|
||||
if (currentId === currentId2) {
|
||||
setConfirmDialogMessage('输入输出ID不能相同,请检查');
|
||||
setConfirmDialogCallback(() => () => {
|
||||
setShowConfirmDialog(false);
|
||||
});
|
||||
setShowConfirmDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否存在重名的测试结果
|
||||
const currentProject = getCurrentProject();
|
||||
const existingResults = currentProject?.testResults || [];
|
||||
|
||||
// 检查两个ID是否重复
|
||||
const isDuplicate1 = existingResults.some(result => result.name === currentId);
|
||||
const isDuplicate2 = existingResults.some(result => result.name === currentId2);
|
||||
|
||||
const currentConfig = getCurrentTestConfig();
|
||||
const cableType = currentConfig.params.cableType;
|
||||
const isMultiMode = cableType.includes('OM');
|
||||
|
||||
if (isDuplicate1 || isDuplicate2) {
|
||||
const message = [];
|
||||
if (isDuplicate1) message.push(`线缆ID ${currentId}`);
|
||||
if (isDuplicate2) message.push(`线缆ID ${currentId2}`);
|
||||
|
||||
setConfirmDialogMessage(`${message.join(' 和 ')}已使用,是否覆盖?`);
|
||||
setConfirmDialogCallback(() => () => {
|
||||
// 更新临时测试结果的名称
|
||||
const updatedTestResult1 = {
|
||||
...tempTestResult,
|
||||
name: currentId,
|
||||
inputname: isMultiMode ? currentId2 : currentId,
|
||||
outname: isMultiMode ? currentId : currentId2,
|
||||
};
|
||||
|
||||
const updatedTestResult2 = {
|
||||
...tempTestResult,
|
||||
name: currentId2,
|
||||
inputname: isMultiMode ? currentId2 : currentId,
|
||||
outname: isMultiMode ? currentId : currentId2,
|
||||
};
|
||||
|
||||
// 更新当前项目的测试结果
|
||||
const currentIndex = useDisplayStore.getState().projects.findIndex(p => p === currentProject);
|
||||
if (currentIndex !== -1) {
|
||||
// 移除旧的测试结果并添加新的
|
||||
const newResults = existingResults.filter(result =>
|
||||
result.name !== currentId && result.name !== currentId2
|
||||
);
|
||||
updateProject(currentIndex, {
|
||||
testResults: [...newResults, updatedTestResult1, updatedTestResult2]
|
||||
});
|
||||
|
||||
// 更新navigation.current.params中的测试结果名称
|
||||
useDisplayStore.setState({
|
||||
navigation: {
|
||||
...navigation,
|
||||
current: {
|
||||
...navigation.current,
|
||||
params: updatedTestResult1
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
updateCurrentView('save');
|
||||
setShowConfirmDialog(false);
|
||||
});
|
||||
setShowConfirmDialog(true);
|
||||
return;
|
||||
}
|
||||
// 更新临时测试结果的名称并保存到项目中
|
||||
if (tempTestResult) {
|
||||
|
||||
// 创建两个测试结果
|
||||
const updatedTestResult1 = {
|
||||
...tempTestResult,
|
||||
name: currentId,
|
||||
inputname: isMultiMode ? currentId2 : currentId,
|
||||
outname: isMultiMode ? currentId : currentId2,
|
||||
};
|
||||
|
||||
const updatedTestResult2 = {
|
||||
...tempTestResult,
|
||||
name: currentId2,
|
||||
inputname: isMultiMode ? currentId2 : currentId,
|
||||
outname: isMultiMode ? currentId : currentId2,
|
||||
};
|
||||
|
||||
// 更新当前项目的cableIds.name为下一个ID
|
||||
const currentIndex = useDisplayStore.getState().projects.findIndex(p => p === currentProject);
|
||||
if (currentIndex !== -1) {
|
||||
//更新测试结果
|
||||
updateProject(currentIndex, {
|
||||
testResults: [...(currentProject.testResults || []), updatedTestResult1,updatedTestResult2]
|
||||
});
|
||||
|
||||
// 更新navigation.current.params中的测试结果名称
|
||||
const navigation = useDisplayStore.getState().navigation;
|
||||
useDisplayStore.setState({
|
||||
navigation: {
|
||||
...navigation,
|
||||
current: {
|
||||
...navigation.current,
|
||||
params: updatedTestResult1
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 获取下一个ID
|
||||
const nextId = getNextId(getNextId(inputValue.trim()));
|
||||
|
||||
// 获取下一个ID2
|
||||
const nextId2 = getNextId(getNextId(inputValue2.trim()));
|
||||
|
||||
// 获取当前项目的所有cableIds
|
||||
const currentCableIds = currentProject?.cableIds || [];
|
||||
const selectedId = getCurrentCableId().id;
|
||||
const selectedId2 = getCurrentCableId2().id;
|
||||
|
||||
|
||||
|
||||
// 只更新选中的ID,保留其他ID不变
|
||||
const updatedCableIds = currentCableIds.map(cable =>
|
||||
cable.id === selectedId ? { ...cable, name: nextId } :
|
||||
cable.id === selectedId2 ? { ...cable, name: nextId2 } :
|
||||
cable
|
||||
);
|
||||
|
||||
|
||||
|
||||
// 更新项目,添加两个测试结果
|
||||
updateProject(currentIndex, {
|
||||
cableIds: updatedCableIds,
|
||||
});
|
||||
}
|
||||
updateCurrentView('save');
|
||||
}
|
||||
}
|
||||
|
||||
// 创建测试结果的视图
|
||||
const renderContent = () => {
|
||||
const renderResultMain = () => {
|
||||
const moduleType = tempTestResult?.testconfig?.moduleType;
|
||||
switch (moduleType) {
|
||||
case '8000':
|
||||
return <CopperResultMain testResult={tempTestResult} />;
|
||||
case 'cfp':
|
||||
return <OLTSResultMain testResult={tempTestResult} />;
|
||||
case 'ofp':
|
||||
return <OTDRResultMain testResult={tempTestResult} />;
|
||||
default:
|
||||
return <CopperResultMain testResult={tempTestResult} />;
|
||||
}
|
||||
};
|
||||
|
||||
const renderSetName = () => {
|
||||
const moduleType = tempTestResult?.testconfig?.moduleType;
|
||||
switch (moduleType) {
|
||||
case 'cfp':
|
||||
return(
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div className="mb-8">
|
||||
<div className="mb-1 text-white text-sm">输出光纤ID1</div>
|
||||
<div className="relative cursor-pointer">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm px-4 text-black overflow-x-auto whitespace-nowrap"
|
||||
value={inputValue}
|
||||
placeholder="请输入线缆ID1"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setActiveInput(1);
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-1 text-white text-sm">输入光纤ID2</div>
|
||||
<div className="relative cursor-pointer">
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm px-4 text-black overflow-x-auto whitespace-nowrap"
|
||||
value={inputValue2}
|
||||
placeholder="请输入线缆ID2"
|
||||
onChange={(e) => {
|
||||
setInputValue2(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setActiveInput(2);
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{showKeyboard && (
|
||||
<Keyboard
|
||||
value={activeInput === 1 ? inputValue : inputValue2}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
if (activeInput === 1) {
|
||||
setInputValue(newValue);
|
||||
} else {
|
||||
setInputValue2(newValue);
|
||||
}
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
setShowKeyboard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4 flex flex-col">
|
||||
<div
|
||||
className="relative mb-4 cursor-pointer"
|
||||
onClick={() => setShowKeyboard(true)}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
className="w-full h-[50px] bg-[#ffffe1] rounded-sm p-4 text-black"
|
||||
value={inputValue}
|
||||
placeholder="请输入线缆ID"
|
||||
onChange={(e) => {
|
||||
setInputValue(e.target.value);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onClick={(e) => {
|
||||
setShowKeyboard(true);
|
||||
setCursorPosition(e.target.selectionStart);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
// 保存光标位置
|
||||
const cursorPosition = e.target.selectionStart;
|
||||
e.target.setSelectionRange(cursorPosition, cursorPosition);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{showKeyboard && (
|
||||
<Keyboard
|
||||
value={inputValue}
|
||||
cursorPosition={cursorPosition}
|
||||
onChange={(newValue, newPosition) => {
|
||||
setInputValue(newValue);
|
||||
setCursorPosition(newPosition);
|
||||
}}
|
||||
onComplete={() => {
|
||||
setShowKeyboard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
switch (view) {
|
||||
case 'nosave':
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{tempTestResult?.testconfig?.moduleType !== 'cfp' ? (
|
||||
<ResultTitleBar title="未保存结果" testResult={tempTestResult} backTo="home" view="main" />
|
||||
) : (<ResultTitleBar title=" " testResult={tempTestResult} backTo="home" view="main" />
|
||||
)}
|
||||
{renderResultMain()}
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-4">
|
||||
<button
|
||||
onClick={() => updateCurrentView('setname')}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'setname':
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
|
||||
<TitleBar
|
||||
title="保存结果"
|
||||
backTo={useDisplayStore.getState().navigation.previous?.name || 'home'}
|
||||
view={useDisplayStore.getState().navigation.previous?.view || 'main'}
|
||||
/>
|
||||
|
||||
<div className="flex-1 bg-[#303040] p-2 flex flex-col">
|
||||
{renderSetName()}
|
||||
</div>
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-4">
|
||||
{tempTestResult?.testconfig?.moduleType === "cfp" ? (
|
||||
!showKeyboard && (
|
||||
<button
|
||||
onClick={() => handleComplete2()}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
)
|
||||
) : (
|
||||
!showKeyboard && (<button
|
||||
onClick={() => handleComplete()}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'save':
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{tempTestResult?.testconfig?.moduleType !== 'cfp' ? (
|
||||
<ResultTitleBar testResult={tempTestResult} backTo="result" view="main" />
|
||||
) : (<ResultTitleBar title=" " testResult={tempTestResult} backTo="result" view="main" />
|
||||
)}
|
||||
{renderResultMain()}
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-4">
|
||||
<button
|
||||
onClick={() => {
|
||||
navigateTo('home', 'main');
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
主页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const content = renderContent();
|
||||
return (
|
||||
<div className="relative w-full h-full">
|
||||
{content}
|
||||
{showConfirmDialog && (
|
||||
<ConfirmDialog
|
||||
message={confirmDialogMessage}
|
||||
onConfirm={confirmDialogCallback}
|
||||
onCancel={() => setShowConfirmDialog(false)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
660
src/components/dsxpage/TestConfig.js
Normal file
660
src/components/dsxpage/TestConfig.js
Normal file
@@ -0,0 +1,660 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Image from 'next/image';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import SubTitleBar from '../lib/SubTitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import MenuList from './MenuList.js';
|
||||
|
||||
|
||||
export default function TestConfig() {
|
||||
const [selectedConfigId, setSelectedConfigId] = useState(null);
|
||||
const {
|
||||
getCurrentProject,
|
||||
navigation,
|
||||
navigateTo,
|
||||
updateCurrentView,
|
||||
goBack,
|
||||
setSelectedIndexes,
|
||||
updateProject,
|
||||
addProject,
|
||||
setToastMessage
|
||||
} = useDisplayStore();
|
||||
|
||||
// 获取当前项目数据
|
||||
const currentProject = getCurrentProject();
|
||||
|
||||
// 单多模判断
|
||||
const { getCurrentTestConfig } = useDisplayStore.getState();
|
||||
const currentConfig = getCurrentTestConfig();
|
||||
const cableType = currentConfig.params.cableType;
|
||||
const isMultiMode = cableType.includes('OM');
|
||||
|
||||
// 设置默认选中的配置
|
||||
useEffect(() => {
|
||||
// 优先使用导航参数中的配置ID
|
||||
const configIdFromParams = navigation.current.params?.selectedConfigId;
|
||||
if (configIdFromParams) {
|
||||
setSelectedConfigId(configIdFromParams);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有参数,使用当前选中的配置索引
|
||||
const currentTestConfigIndex = useDisplayStore.getState().selectedIndexes.testConfigIndex;
|
||||
if (currentProject?.testConfigs && currentTestConfigIndex >= 0) {
|
||||
setSelectedConfigId(currentProject.testConfigs[currentTestConfigIndex].id);
|
||||
}
|
||||
|
||||
// 确保导航历史正确记录
|
||||
if (navigation.current.name === 'testconfig' && navigation.current.view === 'main') {
|
||||
const current = navigation.current;
|
||||
const previous = navigation.previous;
|
||||
if (!previous || previous.name !== 'testconfig' || previous.view !== 'main') {
|
||||
useDisplayStore.getState().navigateTo('testconfig', 'main', current.params);
|
||||
}
|
||||
}
|
||||
}, [currentProject, navigation.current.params]);
|
||||
|
||||
// 渲染主视图内容
|
||||
const renderMainContent = () => {
|
||||
return (
|
||||
<div className="h-[490px] flex-1 bg-[#303040] p-4">
|
||||
<div className="h-full overflow-y-auto space-y-2 pr-2">
|
||||
{currentProject?.testConfigs.map((config) => (
|
||||
<div
|
||||
key={config.id}
|
||||
className="h-[100px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex items-center gap-4 cursor-pointer"
|
||||
onClick={() => {
|
||||
setSelectedConfigId(config.id);
|
||||
// 保存选中的配置ID到导航参数
|
||||
updateCurrentView('main', { selectedConfigId: config.id });
|
||||
}}
|
||||
>
|
||||
<div className="w-6 h-6 border-2 border-gray-600 rounded-full flex items-center justify-center">
|
||||
{selectedConfigId === config.id && (
|
||||
<div className="w-4 h-4 bg-blue-500 rounded-full" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 relative">
|
||||
|
||||
<div className="mt-1">
|
||||
{config.moduleType === '8000' && (
|
||||
<>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
<div className="text-black text-sm">{config.params.wireOrder}</div>
|
||||
</>
|
||||
)}
|
||||
{config.moduleType === 'cfp' && (
|
||||
<>
|
||||
<div className="text-black text-sm">智能远端</div>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
<div className="text-black text-sm">{config.params.refJumper} 跳线参照</div>
|
||||
</>
|
||||
)}
|
||||
{config.moduleType === 'ofp' && (
|
||||
<>
|
||||
<div className="text-black text-sm">自动OTDR</div>
|
||||
<div className="text-black text-sm">{config.params.limitValue}</div>
|
||||
<div className="text-black text-sm">{config.params.cableType}</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-gray-400 text-sm absolute top-0 right-0">{config.modulelable}</div>
|
||||
</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image
|
||||
src="/arrow.png"
|
||||
alt="箭头"
|
||||
fill
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染设置视图内容
|
||||
const renderSetupContent = () => {
|
||||
const currentConfig = currentProject?.testConfigs.find(
|
||||
config => config.id === selectedConfigId
|
||||
);
|
||||
|
||||
if (!currentConfig) return null;
|
||||
|
||||
const renderConfigContent = () => {
|
||||
switch (currentConfig.moduleType) {
|
||||
case '8000':
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-0">
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-t-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'CABLE_TYPE',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">电缆类型:{currentConfig.params.cableType}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] rounded-b-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l">NVP:根据电缆类型生成</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-0">
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-t-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'LIMIT_VALUE',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">测试极限值:{currentConfig.params.limitValue}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l">存储绘图数据:开</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] rounded-b-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l">HDTDR/HDTDX:仅限失败/通过</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'WIRE_ORDER',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">插座配置:{currentConfig.params.wireOrder}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'cfp':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-0">
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-t-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l font-bold">测试类型:智能远端</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] rounded-b-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l">双向:关</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'FIBER_TYPE',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">光纤类型:{currentConfig.params.cableType}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-0">
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-t-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'CFP_LIMIT',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">测试极限值:{currentConfig.params.limitValue}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l ">参照方法:{currentConfig.params.refJumper} 跳线</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l">连接器类型:LC</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] rounded-b-sm p-4 shadow-lg flex justify-between items-center cursor-pointer " onClick={() => updateCurrentView('cfp-conunt')}>
|
||||
<div className="text-black text-l" >接线/接头的数量:{currentConfig.params.connectorCount}/{currentConfig.params.spliceCount}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case 'ofp':
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-0">
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-t-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l font-bold">测试类型:自动OTDR</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l ">前导补偿:开</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
{/* <Image src="/arrow.png" alt="箭头" fill className="object-contain" /> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[50px] bg-gradient-to-b from-[#ffffff] to-[#ffffff] rounded-b-sm p-4 shadow-lg flex justify-between items-center">
|
||||
<div className="text-black text-l ">波长:{currentConfig.params.cableType.includes('OM') ? '850 nm、1310nm' : '1310nm、1550nm'}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
{/* <Image src="/arrow.png" alt="箭头" fill className="object-contain" /> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'FIBER_TYPE',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">光纤类型:{currentConfig.params.cableType}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="h-[50px] bg-gradient-to-b from-[#e6e3e6] to-[#7b797b] rounded-sm p-4 shadow-lg flex justify-between items-center cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'OFP_LIMIT',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black text-l font-bold">测试极限值:{currentConfig.params.limitValue}</div>
|
||||
<div className="w-3 h-3 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 bg-[#303040] p-4">
|
||||
<div className="h-full bg-gradient-to-b from-[#c6c3c6] to-[#c6c3c6] rounded-sm p-4 shadow-lg flex flex-col">
|
||||
<div
|
||||
className="flex justify-between items-center mb-4 cursor-pointer"
|
||||
onClick={() => navigateTo('menulist', 'setup', {
|
||||
menuType: 'MODULE',
|
||||
selectedConfigId: selectedConfigId,
|
||||
backTo: 'testconfig',
|
||||
backView: 'setup'
|
||||
})}
|
||||
>
|
||||
<div className="text-black font-bold text-lg">模块:{currentConfig.modulelable}</div>
|
||||
<div className="w-5 h-5 relative">
|
||||
<Image src="/arrow.png" alt="箭头" fill className="object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
{renderConfigContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染标题栏
|
||||
const renderTitleBar = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
const handleBack = () => {
|
||||
if (view === 'setup') {
|
||||
updateCurrentView('main');
|
||||
} else {
|
||||
goBack();
|
||||
}
|
||||
};
|
||||
|
||||
switch (view) {
|
||||
case 'main':
|
||||
return (
|
||||
<TitleBar
|
||||
title="更改测试"
|
||||
backTo={navigation.previous?.name || 'home'}
|
||||
view={navigation.previous?.view || 'main'}
|
||||
params={navigation.previous?.params || {}}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
case 'setup':
|
||||
return (
|
||||
<TitleBar
|
||||
title="测试设置"
|
||||
backTo={navigation.previous?.name || 'testconfig'}
|
||||
view={navigation.previous?.view || 'main'}
|
||||
params={navigation.previous?.params || {}}
|
||||
onBack={handleBack}
|
||||
/>
|
||||
);
|
||||
case 'cfp-conunt':
|
||||
return (
|
||||
<TitleBar
|
||||
title=" "
|
||||
view={'setup'}
|
||||
params={navigation.previous?.params || {}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染底部栏
|
||||
const renderFooter = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'main':
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-between px-8">
|
||||
<button
|
||||
onClick={() => {
|
||||
// 创建新的测试配置
|
||||
const newConfig = {
|
||||
id: uuidv4(),
|
||||
moduleType: '8000',
|
||||
modulelable: 'DSX-8000',
|
||||
params: {
|
||||
limitValue: 'TIA Cat 6 Channel',
|
||||
cableType: 'Cat6 U/UTP',
|
||||
wireOrder: 'T568B',
|
||||
refJumper: '1',
|
||||
spliceCount: '0',
|
||||
connectorCount: '2'
|
||||
}
|
||||
};
|
||||
|
||||
// 更新项目,添加新的测试配置
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testConfigs: [...currentProject.testConfigs, newConfig]
|
||||
};
|
||||
updateProject(useDisplayStore.getState().selectedIndexes.projectIndex, updatedProject);
|
||||
|
||||
// 设置新配置为选中状态并保存到导航参数
|
||||
setSelectedConfigId(newConfig.id);
|
||||
updateCurrentView('setup', { selectedConfigId: newConfig.id });
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
新测试
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!selectedConfigId) {
|
||||
setToastMessage('请先选择一个测试配置');
|
||||
return;
|
||||
}
|
||||
// 保存选中的配置ID到导航参数
|
||||
updateCurrentView('setup', { selectedConfigId: selectedConfigId });
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!selectedConfigId) {
|
||||
setToastMessage('请先选择一个测试配置');
|
||||
return;
|
||||
}
|
||||
const configIndex = currentProject.testConfigs.findIndex(
|
||||
config => config.id === selectedConfigId
|
||||
);
|
||||
setSelectedIndexes({ testConfigIndex: configIndex });
|
||||
navigateTo('home', 'main');
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
使用所选
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
case 'setup':
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
<button
|
||||
onClick={() => {
|
||||
// 保存配置并返回主视图
|
||||
updateCurrentView('main', { selectedConfigId: selectedConfigId });
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
case 'cfp-conunt':
|
||||
return (
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-end px-8">
|
||||
<button
|
||||
onClick={() => {
|
||||
// 保存配置并返回主视图
|
||||
updateCurrentView('setup', { selectedConfigId: selectedConfigId });
|
||||
}}
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#ffd773] to-[#e7aa29] rounded-sm flex items-center justify-center text-black font-bold shadow-lg"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const renderCFPCONUNTContent = () =>{
|
||||
const currentConfig = currentProject?.testConfigs.find(
|
||||
config => config.id === selectedConfigId
|
||||
);
|
||||
|
||||
if (!currentConfig) return null;
|
||||
|
||||
const handleValueChange = (field, value) => {
|
||||
const currentConfig = currentProject?.testConfigs.find(
|
||||
config => config.id === selectedConfigId
|
||||
);
|
||||
|
||||
if (!currentConfig) return;
|
||||
|
||||
// 更新当前配置的参数
|
||||
const updatedConfig = {
|
||||
...currentConfig,
|
||||
params: {
|
||||
...currentConfig.params,
|
||||
[field]: value
|
||||
}
|
||||
};
|
||||
|
||||
// 更新项目中的测试配置
|
||||
const updatedProject = {
|
||||
...currentProject,
|
||||
testConfigs: currentProject.testConfigs.map(config =>
|
||||
config.id === selectedConfigId ? updatedConfig : config
|
||||
)
|
||||
};
|
||||
|
||||
// 调用displayStore的updateProject方法更新状态
|
||||
updateProject(useDisplayStore.getState().selectedIndexes.projectIndex, updatedProject);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1 bg-[#0f172a] flex flex-col relative">
|
||||
<div className="absolute inset-0">
|
||||
<Image
|
||||
src={`/olts-weldingPointSet.png`}
|
||||
alt="CFPCOUNUNT"
|
||||
fill
|
||||
className="object-contain"
|
||||
sizes="100vw"
|
||||
/>
|
||||
</div>
|
||||
{currentConfig.params.connectorCount > 2 && (<div>
|
||||
<div className="absolute flex flex-col gap-10 pl-56.5 pt-45">
|
||||
<div className="relative">
|
||||
<Image
|
||||
src={`/icon-connector.svg`}
|
||||
alt="connector"
|
||||
width={30}
|
||||
height={30}
|
||||
className="object-contain"
|
||||
/>
|
||||
<span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white">
|
||||
{currentConfig.params.connectorCount-2}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute flex flex-col gap-10 pl-43 pt-45">
|
||||
<div className="relative">
|
||||
<Image
|
||||
src={`/icon-connector.svg`}
|
||||
alt="connector"
|
||||
width={30}
|
||||
height={30}
|
||||
className="object-contain"
|
||||
/>
|
||||
<span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white">
|
||||
{currentConfig.params.connectorCount-2}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>)}
|
||||
{currentConfig.params.spliceCount > 0 && (<div>
|
||||
<div className="absolute flex flex-col gap-10 pl-57 pt-65">
|
||||
<div className="relative">
|
||||
<div className="w-6 h-6 rounded-full bg-[#104878]"></div>
|
||||
<span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white">
|
||||
{currentConfig.params.spliceCount}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute flex flex-col gap-10 pl-44 pt-65">
|
||||
<div className="relative">
|
||||
<div className="w-6 h-6 rounded-full bg-[#104878]"></div>
|
||||
<span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white">
|
||||
{currentConfig.params.spliceCount}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>)}
|
||||
|
||||
<div className="relative flex flex-col gap-10 pl-75 pt-30">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="number"
|
||||
min="2"
|
||||
max="20"
|
||||
defaultValue={currentConfig.params.connectorCount}
|
||||
onChange={(e) => handleValueChange('connectorCount', e.target.value)}
|
||||
onKeyDown={(e) => e.preventDefault()}
|
||||
className="w-16 h-8 px-2 bg-[#104878] rounded text-center focus:outline-none focus:ring-2 focus:ring-[#0ff] text-white"
|
||||
/>
|
||||
<span className="ml-4 text-base font-roboto select-none text-black">全部连接</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="18"
|
||||
defaultValue={currentConfig.params.spliceCount}
|
||||
onChange={(e) => handleValueChange('spliceCount', e.target.value)}
|
||||
onKeyDown={(e) => e.preventDefault()}
|
||||
className="w-16 h-8 px-2 bg-[#104878] rounded text-center focus:outline-none focus:ring-2 focus:ring-[#0ff] text-white"
|
||||
/>
|
||||
<span className="ml-4 text-base font-roboto select-none text-black">接头</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
max="1"
|
||||
defaultValue={currentConfig.params.refJumper}
|
||||
onChange={(e) => handleValueChange('refJumper', e.target.value)}
|
||||
onKeyDown={(e) => e.preventDefault()}
|
||||
className="w-16 h-8 px-2 bg-[#104878] rounded text-center focus:outline-none focus:ring-2 focus:ring-[#0ff] text-white"
|
||||
/>
|
||||
<span className="ml-4 text-base font-roboto select-none text-black">跳线参照</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
};
|
||||
// 渲染内容
|
||||
const renderContent = () => {
|
||||
const { view } = navigation.current;
|
||||
|
||||
switch (view) {
|
||||
case 'main':
|
||||
return renderMainContent();
|
||||
case 'setup':
|
||||
return renderSetupContent();
|
||||
case 'cfp-conunt':
|
||||
return renderCFPCONUNTContent();
|
||||
default:
|
||||
return renderMainContent();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
{renderTitleBar()}
|
||||
{renderContent()}
|
||||
{renderFooter()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
747
src/components/dsxpage/Testing.js
Normal file
747
src/components/dsxpage/Testing.js
Normal file
@@ -0,0 +1,747 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import StatusBar from '../lib/StatusBar';
|
||||
import TitleBar from '../lib/TitleBar';
|
||||
import useDisplayStore from '@/store/displayStore';
|
||||
import useDeviceStore from '@/store/deviceStore';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
// 计算电阻余量的函数
|
||||
function calculateResistanceMargin(data, limitdata, limitValue) {
|
||||
try {
|
||||
// 判断测试类型
|
||||
const isPoETest = limitValue.includes('+PoE');
|
||||
const isALLTest = limitValue.includes('+ALL');
|
||||
|
||||
let minMargin = Infinity;
|
||||
|
||||
// 回路电阻计算
|
||||
const loopResistanceData = data?.performance?.data?.['Loop Resistance (Ω)'];
|
||||
const loopResistanceLimit = limitdata?.['Loop Resistance (Ω)']?.['PAIRLimit (Ω)'];
|
||||
|
||||
if (loopResistanceData && loopResistanceLimit) {
|
||||
const pairs = ['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
for (const pair of pairs) {
|
||||
const actualValues = loopResistanceData[pair];
|
||||
if (actualValues && Array.isArray(actualValues)) {
|
||||
for (let i = 0; i < actualValues.length && i < loopResistanceLimit.length; i++) {
|
||||
const actualValue = actualValues[i];
|
||||
const limitVal = loopResistanceLimit[i];
|
||||
if (actualValue !== undefined && actualValue !== null && limitVal !== undefined && limitVal !== null) {
|
||||
const margin = limitVal - Math.abs(actualValue);
|
||||
minMargin = Math.min(minMargin, margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 线对UBL计算(仅在+PoE或+ALL测试时)
|
||||
if (isPoETest || isALLTest) {
|
||||
const ublData = data?.performance?.data?.['UBL (Ω)'];
|
||||
const ublLimit = limitdata?.['UBL (Ω)']?.['PAIRLimit (Ω)'];
|
||||
|
||||
if (ublData && ublLimit) {
|
||||
const pairs = ['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
for (const pair of pairs) {
|
||||
const actualValues = ublData[pair];
|
||||
if (actualValues && Array.isArray(actualValues)) {
|
||||
for (let i = 0; i < actualValues.length && i < ublLimit.length; i++) {
|
||||
const actualValue = actualValues[i];
|
||||
const limitVal = ublLimit[i];
|
||||
if (actualValue !== undefined && actualValue !== null && limitVal !== undefined && limitVal !== null) {
|
||||
const margin = limitVal - Math.abs(actualValue);
|
||||
minMargin = Math.min(minMargin, margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// P2PUBL计算(仅在+PoE或+ALL测试时)
|
||||
if (isPoETest || isALLTest) {
|
||||
const p2pUblData = data?.performance?.data?.['P2PUBL (Ω)'];
|
||||
const p2pUblLimit = limitdata?.['P2PUBL (Ω)']?.['PAIRLimit (Ω)'];
|
||||
|
||||
if (p2pUblData && p2pUblLimit) {
|
||||
const pairs = ['PAIR1236', 'PAIR1245', 'PAIR1278', 'PAIR3645', 'PAIR3678', 'PAIR4578'];
|
||||
for (const pair of pairs) {
|
||||
const actualValues = p2pUblData[pair];
|
||||
if (actualValues && Array.isArray(actualValues)) {
|
||||
for (let i = 0; i < actualValues.length && i < p2pUblLimit.length; i++) {
|
||||
const actualValue = actualValues[i];
|
||||
const limitVal = p2pUblLimit[i];
|
||||
if (actualValue !== undefined && actualValue !== null && limitVal !== undefined && limitVal !== null) {
|
||||
const margin = limitVal - Math.abs(actualValue);
|
||||
minMargin = Math.min(minMargin, margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return minMargin === Infinity ? 0 : minMargin;
|
||||
} catch (error) {
|
||||
console.error('Error calculating resistance margin:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export default function Testing() {
|
||||
const { view } = useDisplayStore.getState().navigation.current;
|
||||
const { navigateTo,
|
||||
getCurrentTestConfig,
|
||||
getCurrentProject,
|
||||
getCurrentOperator,
|
||||
getCurrentCableId,
|
||||
getCurrentCableId2,
|
||||
setToastMessage,
|
||||
} = useDisplayStore();
|
||||
const currentConfig = getCurrentTestConfig();
|
||||
const { connectionStatus,setTotalToastMessage } = useDeviceStore();
|
||||
|
||||
// 铜缆测试路径和状态
|
||||
const copperMainPathKey = connectionStatus?.mainPaths ? Object.keys(connectionStatus.mainPaths)[0] : null;
|
||||
const copperRemotePathKey = connectionStatus?.remotePaths ? Object.keys(connectionStatus.remotePaths)[0] : null;
|
||||
const coppermainend = copperMainPathKey ? connectionStatus.mainPaths[copperMainPathKey]?.end : null;
|
||||
const copperremoteend = copperRemotePathKey ? connectionStatus.remotePaths[copperRemotePathKey]?.end : null;
|
||||
const copperRefStatus = useDisplayStore.getState()?.ref?.copper?.status || null;
|
||||
|
||||
// CFP测试路径和状态
|
||||
|
||||
const cableType = currentConfig.params.cableType;
|
||||
const isMultiMode = cableType.includes('OM');
|
||||
const cfpRefStatus = useDisplayStore.getState()?.ref?.cfp?.status || null;
|
||||
const cfpRefConnectStatus = useDisplayStore.getState()?.ref?.cfp?.connectStatus || null;
|
||||
|
||||
const cfpInResult = isMultiMode ? connectionStatus?.CFPMainIn : connectionStatus?.CFPMainOut;
|
||||
const cfpOutResult = isMultiMode ? connectionStatus?.CFPMainOut : connectionStatus?.CFPMainIn;
|
||||
|
||||
|
||||
// 获取isMultiMode
|
||||
const wavelength1 = isMultiMode ? '850' : '1310';
|
||||
const wavelength2 = isMultiMode ? '1300' : '1550';
|
||||
|
||||
// OFP测试路径和状态
|
||||
const ofpMainEnd = copperMainPathKey ? connectionStatus.mainPaths[copperMainPathKey]?.end : null;
|
||||
const ofpRefStatus = useDisplayStore.getState()?.ref?.ofp?.status || null;
|
||||
const ofpRefConnectStatus = useDisplayStore.getState()?.ref?.ofp?.connectStatus || null;
|
||||
const [tempTestResult, setTempTestResult] = useState(null);
|
||||
|
||||
// 获取测试链路清洁状态
|
||||
const moduleType = currentConfig?.moduleType;
|
||||
|
||||
// 检查路径中所有端口的清洁状态
|
||||
const checkPortsCleanStatus = (paths) => {
|
||||
if (!paths || paths.length === 0) return false;
|
||||
|
||||
// 检查每个端口的清洁状态
|
||||
const cleanPorts = paths.filter(portId => {
|
||||
const portElement = document.getElementById(portId);
|
||||
return portElement?.getAttribute('lcclean') === 'true';
|
||||
});
|
||||
|
||||
// 返回已清洁的端口数量
|
||||
return cleanPorts.length;
|
||||
};
|
||||
|
||||
let allPathsCleanStatus = {};
|
||||
let isAllPathsClean = 0; // 默认为0,表示全部未清洁
|
||||
|
||||
// 只在模块类型为 cfp 或 ofp 时执行检测
|
||||
if (moduleType === 'cfp' || moduleType === 'ofp') {
|
||||
if (moduleType === 'cfp') {
|
||||
// CFP模块检测所有路径
|
||||
const mainInPaths = connectionStatus?.mainPaths?.['main-cfp-in']?.path || [];
|
||||
const mainOutPaths = connectionStatus?.mainPaths?.[isMultiMode ? 'main-cfp-mm-out' : 'main-cfp-sm-out']?.path || [];
|
||||
const remoteInPaths = connectionStatus?.remotePaths?.['remote-cfp-in']?.path || [];
|
||||
const remoteOutPaths = connectionStatus?.remotePaths?.[isMultiMode ? 'remote-cfp-mm-out' : 'remote-cfp-sm-out']?.path || [];
|
||||
|
||||
// 获取每个路径的清洁端口数量
|
||||
const mainInClean = checkPortsCleanStatus(mainInPaths);
|
||||
const mainOutClean = checkPortsCleanStatus(mainOutPaths);
|
||||
const remoteInClean = checkPortsCleanStatus(remoteInPaths);
|
||||
const remoteOutClean = checkPortsCleanStatus(remoteOutPaths);
|
||||
|
||||
// 计算总端口数和已清洁端口数
|
||||
const totalPorts = mainInPaths.length + mainOutPaths.length +
|
||||
remoteInPaths.length + remoteOutPaths.length;
|
||||
const cleanedPorts = mainInClean + mainOutClean +
|
||||
remoteInClean + remoteOutClean;
|
||||
|
||||
// 设置清洁状态
|
||||
if (cleanedPorts === 0) isAllPathsClean = 0;
|
||||
else if (cleanedPorts < totalPorts) isAllPathsClean = 1;
|
||||
else if (cleanedPorts === totalPorts) isAllPathsClean = 2;
|
||||
|
||||
allPathsCleanStatus = {
|
||||
mainInClean: mainInClean === mainInPaths.length,
|
||||
mainOutClean: mainOutClean === mainOutPaths.length,
|
||||
remoteInClean: remoteInClean === remoteInPaths.length,
|
||||
remoteOutClean: remoteOutClean === remoteOutPaths.length
|
||||
};
|
||||
} else {
|
||||
// OFP模块只检测主机输出端口
|
||||
const mainOutPaths = connectionStatus?.mainPaths?.[isMultiMode ? 'main-ofp-mm-out' : 'main-ofp-sm-out']?.path || [];
|
||||
const cleanedPorts = checkPortsCleanStatus(mainOutPaths);
|
||||
|
||||
// 设置清洁状态
|
||||
if (cleanedPorts === 0) isAllPathsClean = 0;
|
||||
else if (cleanedPorts < mainOutPaths.length) isAllPathsClean = 1;
|
||||
else if (cleanedPorts === mainOutPaths.length) isAllPathsClean = 2;
|
||||
|
||||
allPathsCleanStatus = {
|
||||
mainOutClean: cleanedPorts === mainOutPaths.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!tempTestResult) {
|
||||
const loadTestResult = async () => {
|
||||
try {
|
||||
const currentProject = getCurrentProject();
|
||||
const currentOperator = getCurrentOperator();
|
||||
// 获取当前选中的电缆ID
|
||||
const currentCableId = getCurrentCableId().name;
|
||||
// 获取当前选中的电缆ID2
|
||||
const currentCableId2 = getCurrentCableId2().name;
|
||||
const CopperWiremapStatus = connectionStatus?.CopperWiremapStatus;
|
||||
const CopperPerformanceStatus = connectionStatus?.CopperPerformanceStatus;
|
||||
const ofpResult = connectionStatus?.OFPStatus;
|
||||
const moduleType = currentConfig?.moduleType;
|
||||
const cableType = currentConfig?.params?.cableType;
|
||||
const wireOrder = currentConfig?.params?.wireOrder;
|
||||
|
||||
let testResult;
|
||||
|
||||
// 不同模块组装测试结果逻辑
|
||||
if (moduleType === '8000') {
|
||||
let CopperResultStatus = 'fail';
|
||||
let CopperWiremapResultStatus = 'fail';
|
||||
|
||||
if (CopperWiremapStatus === 'pass' ) {
|
||||
if (wireOrder === 'T568B' ||
|
||||
wireOrder === 'Ethernet Two-Pair' ||
|
||||
wireOrder === 'M12-D Two-Pair') {
|
||||
CopperWiremapResultStatus = 'pass';
|
||||
console.log(CopperWiremapResultStatus);
|
||||
}
|
||||
} else if (CopperWiremapStatus === 'pass-2pair') {
|
||||
if (wireOrder === 'Ethernet Two-Pair' ||
|
||||
wireOrder === 'M12-D Two-Pair') {
|
||||
CopperWiremapResultStatus = 'pass';
|
||||
} else if (wireOrder === 'T568B') {
|
||||
CopperWiremapResultStatus = 'fail';
|
||||
CopperResultStatus = 'fail';
|
||||
}
|
||||
}else if (CopperWiremapStatus === 'sopen') {
|
||||
if (wireOrder === 'T568B') {
|
||||
CopperWiremapResultStatus = 'fail';
|
||||
CopperResultStatus = 'fail';
|
||||
}
|
||||
else if (cableType.includes('U/UTP')) {
|
||||
CopperWiremapResultStatus = 'pass';
|
||||
}
|
||||
else if (cableType.includes('F/UTP')) {
|
||||
CopperWiremapResultStatus = 'fail';
|
||||
CopperResultStatus = 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
if (CopperWiremapResultStatus === 'pass') {
|
||||
try {
|
||||
const response = await import(`@/store/COPPER/${CopperPerformanceStatus}.json`);
|
||||
const data = response.default;
|
||||
|
||||
// 加载限制数据
|
||||
const limitValue = currentConfig?.params?.limitValue;
|
||||
try {
|
||||
// 从limitValue中提取基础名称(移除+PoE和+ALL后缀)
|
||||
const baseName = limitValue.split(' (+')[0];
|
||||
// 使用基础名称加载带有(+ALL)后缀的文件
|
||||
const limitResponse = await import(`@/store/COPPER/${baseName} (+ALL).json`);
|
||||
const limitdata = limitResponse;
|
||||
|
||||
// 计算所有参数的余量
|
||||
const paramTitles = ['插入损耗', '回波损耗', 'NEXT', 'PS NEXT', 'ACR-N', 'PS ACR-N',
|
||||
'ACR-F', 'PS ACR-F', 'CDNEXT', 'CMRL', 'TCL', 'ELTCTL', '电阻'];
|
||||
const dataGroupMap = {
|
||||
'插入损耗': 'Insertion Loss (dB)',
|
||||
'回波损耗': 'RL (dB)',
|
||||
'NEXT': 'NEXT (dB)',
|
||||
'PS NEXT': 'PS NEXT (dB)',
|
||||
'ACR-N': 'ACR-N (dB)',
|
||||
'PS ACR-N': 'PS ACR-N (dB)',
|
||||
'ACR-F': 'ACR-F (dB)',
|
||||
'PS ACR-F': 'PS ACR-F (dB)',
|
||||
'CDNEXT': 'CDNEXT (dB)',
|
||||
'CMRL': 'CMRL (dB)',
|
||||
'TCL': 'TCL (dB)',
|
||||
'ELTCTL': 'ELTCTL (dB)',
|
||||
'电阻': 'Resistance (Ω)'
|
||||
};
|
||||
|
||||
let hasNegativeMargin = false;
|
||||
|
||||
// 长度判断
|
||||
if (limitdata.LENGTH && data.performance.LENGTH) {
|
||||
const lengthPairs = ['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
for (const pair of lengthPairs) {
|
||||
if (data.performance.LENGTH[pair] > limitdata.LENGTH) {
|
||||
hasNegativeMargin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 电阻判断
|
||||
if (limitdata.LOOP && data.performance.OHM?.LOOP) {
|
||||
const loopPairs = ['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
for (const pair of loopPairs) {
|
||||
if (data.performance.OHM.LOOP[pair] > limitdata.LOOP) {
|
||||
hasNegativeMargin = true;
|
||||
console.log("电阻fail")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是 +PoE 或 +ALL 测试
|
||||
const hasPoE = limitValue?.includes('+PoE');
|
||||
const hasALL = limitValue?.includes('+ALL');
|
||||
// +PoE 或 +ALL 测试的额外检查
|
||||
if ((hasPoE || hasALL) && data.performance.OHM) {
|
||||
// 线对 UBL 余量检查
|
||||
if (limitdata.PAIRUBL && data.performance.OHM.PAIRUBL) {
|
||||
const pairUblPairs = ['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
for (const pair of pairUblPairs) {
|
||||
if (Math.abs(data.performance.OHM.PAIRUBL[pair]) > limitdata.PAIRUBL) {
|
||||
hasNegativeMargin = true;
|
||||
console.log("UBL fail")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// P2P UBL 余量检查
|
||||
if (limitdata.P2PUBL && data.performance.OHM.P2PUBL) {
|
||||
const p2pUblPairs = ['PAIR1236', 'PAIR1245', 'PAIR1278', 'PAIR3645', 'PAIR3678', 'PAIR4578'];
|
||||
for (const pair of p2pUblPairs) {
|
||||
if (Math.abs(data.performance.OHM.P2PUBL[pair]) > limitdata.P2PUBL) {
|
||||
hasNegativeMargin = true;
|
||||
console.log("P2P fail")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const paramTitle of paramTitles) {
|
||||
const dataGroup = dataGroupMap[paramTitle];
|
||||
|
||||
// 电阻参数的特殊处理
|
||||
if (paramTitle === '电阻') {
|
||||
const resistanceMargin = calculateResistanceMargin(data, limitdata, limitValue);
|
||||
if (resistanceMargin < 0) {
|
||||
hasNegativeMargin = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dataGroup || !data?.performance?.data?.[dataGroup] || !limitdata?.[dataGroup]) continue;
|
||||
|
||||
const testData = data.performance.data[dataGroup];
|
||||
const limitValues = limitdata[dataGroup]?.['PAIRLimit (dB)'] || [];
|
||||
|
||||
const pairs = ['NEXT (dB)', 'ACR-N (dB)'].includes(dataGroup) ?
|
||||
['PAIR1236', 'PAIR1245', 'PAIR1278', 'PAIR3645', 'PAIR3678', 'PAIR4578'] :
|
||||
['ACR-F (dB)', 'CDNEXT (dB)'].includes(dataGroup) ?
|
||||
['PAIR1236', 'PAIR1245', 'PAIR1278', 'PAIR3612', 'PAIR3645', 'PAIR3678',
|
||||
'PAIR4512', 'PAIR4535', 'PAIR4578', 'PAIR7812', 'PAIR7936', 'PAIR7845'] :
|
||||
['PAIR12', 'PAIR36', 'PAIR45', 'PAIR78'];
|
||||
|
||||
for (let index = 0; index < limitValues.length && !hasNegativeMargin; index++) {
|
||||
const limitValue = limitValues[index];
|
||||
if (limitValue === undefined || limitValue === null) break;
|
||||
|
||||
for (const pair of pairs) {
|
||||
const actualValue = testData[pair]?.[index];
|
||||
if (actualValue !== undefined && actualValue !== null) {
|
||||
const margin = paramTitle === '插入损耗' ?
|
||||
Math.abs(limitValue) - Math.abs(actualValue) :
|
||||
Math.abs(actualValue) - limitValue;
|
||||
if (margin < 0) {
|
||||
hasNegativeMargin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopperResultStatus = hasNegativeMargin ? 'fail' : 'pass';
|
||||
} catch (error) {
|
||||
console.error('Error calculating margins:', error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载测试结果失败:', error);
|
||||
}
|
||||
}
|
||||
testResult = {
|
||||
id: uuidv4(),
|
||||
name: currentProject?.cableIds[0]?.name || '',
|
||||
testconfig: JSON.parse(JSON.stringify(currentConfig || {})),
|
||||
operators: currentOperator?.name || '',
|
||||
CopperWiremapStatus,
|
||||
CopperWiremapResultStatus,
|
||||
CopperPerformanceStatus,
|
||||
CopperRef: copperRefStatus,
|
||||
CopperResultStatus,
|
||||
};
|
||||
} else if (moduleType === 'cfp' ) {
|
||||
// 极限值导入
|
||||
const limitValue = currentConfig?.params?.limitValue;
|
||||
const CFP_LIMIT = require('@/store/FIBER/CFP_LIMIT').default;
|
||||
const limitData = CFP_LIMIT[limitValue];
|
||||
|
||||
if (limitData) {
|
||||
// 使用limitData中的极限值进行后续处理
|
||||
const {
|
||||
adapterLoss,
|
||||
firstLastAdapterLoss,
|
||||
spliceLoss,
|
||||
totalLoss1310nm,
|
||||
totalLoss1550nm,
|
||||
lossPerKm850nm,
|
||||
lossPerKm13000nm,
|
||||
lossPerKm1310nm,
|
||||
lossPerKm1550nm,
|
||||
maxSMLength,
|
||||
manMMLength
|
||||
} = limitData;
|
||||
}
|
||||
// 用户配置加载
|
||||
const connectorCount = currentConfig?.params?.connectorCount;
|
||||
const spliceCount = currentConfig?.params?.spliceCount;
|
||||
const refJumper = currentConfig?.params?.refJumper;
|
||||
|
||||
let cfpMainInStatus = null;
|
||||
let cfpMainOutStatus = null;
|
||||
|
||||
if (cfpInResult) {
|
||||
try {
|
||||
// 加载第一个波长的数据
|
||||
const wave1Data = await import(`@/store/FIBER/${cfpInResult}/${wavelength1}-dump.json`);
|
||||
// 加载第二个波长的数据
|
||||
const wave2Data = await import(`@/store/FIBER/${cfpInResult}/${wavelength2}-dump.json`);
|
||||
|
||||
// 添加随机浮动因子(损耗上下浮动1%)
|
||||
const getDistanceRandomFactor = () => 1; // 长度不浮动
|
||||
const getLossRandomFactor = () => 1 + (Math.random() * 0.02 - 0.01); // 生成0.99到1.01之间的随机数
|
||||
|
||||
// 先计算两个波长的损耗值
|
||||
const wave1Loss = wave1Data.Summary.totalLoss * getLossRandomFactor();
|
||||
const wave2Loss = wave2Data.Summary.totalLoss * getLossRandomFactor();
|
||||
// 如果是参考状态,增加0.1
|
||||
const finalWave1Loss = !cfpRefStatus ? wave1Loss : wave1Loss + 0.1;
|
||||
const finalWave2Loss = !cfpRefStatus ? wave2Loss : wave2Loss + 0.1;
|
||||
|
||||
cfpMainInStatus = {
|
||||
wavelength1: {
|
||||
wave: wavelength1,
|
||||
distance: (wave1Data.Summary.totalDistance * getDistanceRandomFactor()).toFixed(2),
|
||||
loss: finalWave1Loss.toFixed(2)
|
||||
},
|
||||
wavelength2: {
|
||||
wave: wavelength2,
|
||||
distance: (wave2Data.Summary.totalDistance * getDistanceRandomFactor()).toFixed(2),
|
||||
loss: finalWave2Loss.toFixed(2)
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
setTotalToastMessage("被测链路与参考跳纤类型不一致,请检查");
|
||||
}, 1500); // 3000 毫秒 = 3 秒
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (cfpOutResult) {
|
||||
try {
|
||||
// 加载第一个波长的数据
|
||||
const wave1Data = await import(`@/store/FIBER/${cfpOutResult}/${wavelength1}-dump.json`);
|
||||
// 加载第二个波长的数据
|
||||
const wave2Data = await import(`@/store/FIBER/${cfpOutResult}/${wavelength2}-dump.json`);
|
||||
|
||||
// 添加随机浮动因子(损耗上下浮动1%)
|
||||
const getDistanceRandomFactor = () => 1; // 长度不浮动
|
||||
const getLossRandomFactor = () => 1 + (Math.random() * 0.02 - 0.01); // 生成0.99到1.01之间的随机数
|
||||
|
||||
// 先计算两个波长的损耗值
|
||||
const wave1Loss = wave1Data.Summary.totalLoss * getLossRandomFactor();
|
||||
const wave2Loss = wave2Data.Summary.totalLoss * getLossRandomFactor();
|
||||
// 如果是参考状态,增加0.1
|
||||
const finalWave1Loss = !cfpRefStatus ? wave1Loss : wave1Loss + 0.1;
|
||||
const finalWave2Loss = !cfpRefStatus ? wave2Loss : wave2Loss + 0.1;
|
||||
|
||||
cfpMainOutStatus = {
|
||||
wavelength1: {
|
||||
wave: wavelength1,
|
||||
distance: (wave1Data.Summary.totalDistance * getDistanceRandomFactor()).toFixed(2),
|
||||
loss: finalWave1Loss.toFixed(2)
|
||||
},
|
||||
wavelength2: {
|
||||
wave: wavelength2,
|
||||
distance: (wave2Data.Summary.totalDistance * getDistanceRandomFactor()).toFixed(2),
|
||||
loss: finalWave2Loss.toFixed(2)
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
setTimeout(() => {
|
||||
setToastMessage("未检测到光纤,请检查接口");
|
||||
}, 1500); // 3000 毫秒 = 3 秒
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 结果计算与判断
|
||||
// 第一根光纤
|
||||
const cfpMainIndistance = cfpMainInStatus?.wavelength1?.distance || 0;
|
||||
const cfpMainIndistance2 = cfpMainInStatus?.wavelength2?.distance || 0;
|
||||
const cfpMainInloss = cfpMainInStatus?.wavelength1?.loss || 0;
|
||||
const cfpMainInloss2 = cfpMainInStatus?.wavelength2?.loss || 0;
|
||||
// 长度判断
|
||||
const maxLength = isMultiMode ? limitData.maxMMLength : limitData.maxSMLength;
|
||||
const isLengthValidIn = cfpMainIndistance <= maxLength &&
|
||||
cfpMainIndistance2 <= maxLength ;
|
||||
// 损耗判断
|
||||
const lossPerKm = isMultiMode ? (limitData.lossPerKm850nm):(limitData.lossPerKm1310nm);
|
||||
const lossPerKm2 = isMultiMode ? (limitData.lossPerKm1300nm):(limitData.lossPerKm1550nm);
|
||||
|
||||
const totalLossLimit = limitData.adapterLoss !== null ?
|
||||
connectorCount * limitData.adapterLoss +
|
||||
spliceCount * limitData.spliceLoss +
|
||||
(cfpMainIndistance/1000) * lossPerKm
|
||||
: isMultiMode ?
|
||||
(limitData.totalLoss850nm):(limitData.totalLoss1310nm);
|
||||
|
||||
const totalLossLimit2 = limitData.adapterLoss !== null ?
|
||||
connectorCount * limitData.adapterLoss +
|
||||
spliceCount * limitData.spliceLoss +
|
||||
(cfpMainIndistance2/1000) * lossPerKm2
|
||||
: isMultiMode ?
|
||||
(limitData.totalLoss1300nm):(limitData.totalLoss1550nm);
|
||||
|
||||
const isLossValidIn = cfpMainInloss <= totalLossLimit &&
|
||||
cfpMainInloss2 <= totalLossLimit2 ;
|
||||
|
||||
// 第二根光纤
|
||||
const cfpMainOutdistance = cfpMainOutStatus?.wavelength1?.distance || 0;
|
||||
const cfpMainOutdistance2 = cfpMainOutStatus?.wavelength2?.distance || 0;
|
||||
const cfpMainOutloss = cfpMainOutStatus?.wavelength1?.loss || 0;
|
||||
const cfpMainOutloss2 = cfpMainOutStatus?.wavelength2?.loss || 0;
|
||||
// 长度判断
|
||||
const maxLengthOut = isMultiMode ? limitData.maxMMLength : limitData.maxSMLength;
|
||||
const isLengthValidOut = cfpMainOutdistance <= maxLength &&
|
||||
cfpMainOutdistance2 <= maxLength ;
|
||||
// 损耗判断
|
||||
const lossPerKmOut = isMultiMode ? (limitData.lossPerKm850nm):(limitData.lossPerKm1310nm);
|
||||
const lossPerKm2Out = isMultiMode ? (limitData.lossPerKm1300nm):(limitData.lossPerKm1550nm);
|
||||
|
||||
const totalLossLimitOut = limitData.adapterLoss !== null ?
|
||||
connectorCount * limitData.adapterLoss +
|
||||
spliceCount * limitData.spliceLoss +
|
||||
(cfpMainIndistance/1000) * lossPerKmOut
|
||||
: isMultiMode ?
|
||||
(limitData.totalLoss850nm):(limitData.totalLoss1310nm);
|
||||
|
||||
const totalLossLimit2Out = limitData.adapterLoss !== null ?
|
||||
connectorCount * limitData.adapterLoss +
|
||||
spliceCount * limitData.spliceLoss +
|
||||
(cfpMainOutdistance2/1000) * lossPerKm2Out
|
||||
: isMultiMode ?
|
||||
(limitData.totalLoss1300nm):(limitData.totalLoss1550nm);
|
||||
|
||||
const isLossValidOut = cfpMainOutloss <= totalLossLimitOut &&
|
||||
cfpMainOutloss2 <= totalLossLimit2Out ;
|
||||
|
||||
|
||||
// 结果判断
|
||||
const CFPResultStatus = isLengthValidIn && isLossValidIn && isLengthValidOut && isLossValidOut ? 'pass' : 'fail';
|
||||
// IN端口通过失败判断(根据 isMultiMode 判断使用哪组参数)
|
||||
const InPortStatus = isMultiMode
|
||||
? (isLengthValidIn && isLossValidIn ? 'pass' : 'fail')
|
||||
: (isLengthValidOut && isLossValidOut ? 'pass' : 'fail');
|
||||
// OUT端口通过失败判断
|
||||
const OutPortStatus = isMultiMode
|
||||
? (isLengthValidOut && isLengthValidOut ? 'pass' : 'fail')
|
||||
: (isLengthValidIn && isLossValidIn ? 'pass' : 'fail');
|
||||
|
||||
testResult = {
|
||||
id: uuidv4(),
|
||||
name: currentCableId || '',
|
||||
name2: currentCableId2 || '',
|
||||
testconfig: JSON.parse(JSON.stringify(currentConfig || {})),
|
||||
operators: currentOperator?.name || '',
|
||||
CFPRef:cfpRefStatus,
|
||||
CFPRefConnect:cfpRefConnectStatus,
|
||||
CFPMainIn:cfpMainInStatus,
|
||||
isLengthValidIn,
|
||||
isLossValidIn,
|
||||
InPortStatus, // 添加IN端口状态
|
||||
isLengthValidOut,
|
||||
isLossValidOut,
|
||||
OutPortStatus, // 添加OUT端口状态
|
||||
CFPMainOut:cfpMainOutStatus,
|
||||
CFPResultStatus,
|
||||
PortCleanStatus:isAllPathsClean,
|
||||
};
|
||||
} else if (moduleType === 'ofp') {
|
||||
|
||||
const ofpConnectionStatus = connectionStatus;
|
||||
|
||||
let ofpResultStatus = 'fail';
|
||||
|
||||
if (ofpResult.includes('pass')) {
|
||||
if (!ofpRefStatus) {
|
||||
|
||||
if (connectionStatus.OFPoutRefStatus.includes('sm-') || connectionStatus.OFPoutRefStatus.includes('mm-')) {
|
||||
ofpResultStatus = 'fail';
|
||||
} else {
|
||||
ofpResultStatus = 'pass';
|
||||
}
|
||||
|
||||
} else if (ofpRefStatus === 'start') {
|
||||
if (connectionStatus.OFPoutRefStatus.includes('smc-') || connectionStatus.OFPoutRefStatus.includes('mmc-')) {
|
||||
ofpResultStatus = 'pass';
|
||||
}
|
||||
} else if (ofpRefStatus === 'end') {
|
||||
const hasSmOrMm = connectionStatus.OFPoutRefStatus.includes('sm-') || connectionStatus.OFPoutRefStatus.includes('mm-');
|
||||
const hasSmcOrMmc = connectionStatus.OFPoutRefStatus.includes('smc-') || connectionStatus.OFPoutRefStatus.includes('mmc-');
|
||||
const hasConnectedSmOrMm = connectionStatus.OFPConnectedToRefStatus?.includes('sm-') || connectionStatus.OFPConnectedToRefStatus?.includes('mm-');
|
||||
const hasConnectedSmcOrMmc = connectionStatus.OFPConnectedToRefStatus?.includes('smc-') || connectionStatus.OFPConnectedToRefStatus?.includes('mmc-');
|
||||
|
||||
// 只有前导和末尾都是补偿线时才是pass,其他情况都是fail
|
||||
if (hasSmcOrMmc && hasConnectedSmcOrMmc) {
|
||||
ofpResultStatus = 'pass';
|
||||
} else {
|
||||
ofpResultStatus = 'fail';
|
||||
}
|
||||
}
|
||||
} else if (ofpResult === 'connector-fail-end') {
|
||||
if (!ofpRefStatus) {
|
||||
|
||||
if (connectionStatus.OFPoutRefStatus.includes('sm-') || connectionStatus.OFPoutRefStatus.includes('mm-')) {
|
||||
ofpResultStatus = 'fail';
|
||||
} else {
|
||||
ofpResultStatus = 'pass';
|
||||
}
|
||||
|
||||
} else if (ofpRefStatus === 'start') {
|
||||
if (connectionStatus.OFPoutRefStatus.includes('smc-') || connectionStatus.OFPoutRefStatus.includes('mmc-')) {
|
||||
ofpResultStatus = 'pass';
|
||||
}
|
||||
} else if (ofpRefStatus === 'end') {
|
||||
const hasSmOrMm = connectionStatus.OFPoutRefStatus.includes('sm-') || connectionStatus.OFPoutRefStatus.includes('mm-');
|
||||
const hasSmcOrMmc = connectionStatus.OFPoutRefStatus.includes('smc-') || connectionStatus.OFPoutRefStatus.includes('mmc-');
|
||||
const hasConnectedSmOrMm = connectionStatus.OFPConnectedToRefStatus?.includes('sm-') || connectionStatus.OFPConnectedToRefStatus?.includes('mm-');
|
||||
const hasConnectedSmcOrMmc = connectionStatus.OFPConnectedToRefStatus?.includes('smc-') || connectionStatus.OFPConnectedToRefStatus?.includes('mmc-');
|
||||
|
||||
// 只有前导和末尾都是补偿线时才是pass,其他情况都是fail
|
||||
if (hasSmcOrMmc && hasConnectedSmcOrMmc) {
|
||||
ofpResultStatus = 'pass';
|
||||
} else {
|
||||
ofpResultStatus = 'fail';
|
||||
}
|
||||
}
|
||||
}else if (ofpResult === 'connector-fail-start') {
|
||||
if (ofpRefStatus === 'start') {
|
||||
const hasSmOrMm = connectionStatus.OFPoutRefStatus.includes('sm-') || connectionStatus.OFPoutRefStatus.includes('mm-');
|
||||
const hasSmcOrMmc = connectionStatus.OFPoutRefStatus.includes('smc-') || connectionStatus.OFPoutRefStatus.includes('mmc-');
|
||||
if(hasSmOrMm){
|
||||
ofpResultStatus = 'fail';
|
||||
}else if(hasSmcOrMmc){
|
||||
ofpResultStatus = 'fail';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testResult = {
|
||||
id: uuidv4(),
|
||||
name: currentProject?.cableIds[0]?.name || '',
|
||||
testconfig: JSON.parse(JSON.stringify(currentConfig || {})),
|
||||
operators: currentOperator?.name || '',
|
||||
ofpResult,
|
||||
ofpRefStatus,
|
||||
OFPRefConnect:ofpRefConnectStatus,
|
||||
ofpConnectionStatus,
|
||||
ofpResultStatus,
|
||||
PortCleanStatus:isAllPathsClean,
|
||||
};
|
||||
}
|
||||
|
||||
setTempTestResult(testResult);
|
||||
} catch (error) {
|
||||
console.error('加载测试结果失败:', error);
|
||||
}
|
||||
};
|
||||
loadTestResult();
|
||||
}
|
||||
}, [connectionStatus?.CopperWiremapStatus, connectionStatus?.CopperPerformanceStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tempTestResult) {
|
||||
const moduleType = tempTestResult.testconfig?.moduleType;
|
||||
let delayTime = 1500; // 默认延迟1.5秒
|
||||
|
||||
if (moduleType === '8000') {
|
||||
delayTime = 5000; // 8000模块延迟5秒
|
||||
} else if (moduleType === 'cfp') {
|
||||
delayTime = 1500; // cfp模块延迟1.5秒
|
||||
} else if (moduleType === 'ofp') {
|
||||
delayTime = 6000; // ofp模块延迟6秒
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
navigateTo('resultinfo', 'nosave', tempTestResult);
|
||||
}, delayTime);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [tempTestResult]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col overflow-hidden">
|
||||
<StatusBar />
|
||||
<TitleBar
|
||||
title="进程"
|
||||
/>
|
||||
<div className="h-[490px] bg-[#303040] relative">
|
||||
{/* 背景图片 */}
|
||||
<div
|
||||
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
|
||||
style={{ backgroundImage: 'url(/testing.gif)' }}
|
||||
/>
|
||||
|
||||
{/* 测试配置limit值 */}
|
||||
<div className="absolute w-full top-8 z-10">
|
||||
<span className="flex items-center justify-center text-black text-xl font-bold">
|
||||
{currentConfig?.params?.limitValue || '未设置'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-[60px] bg-[#303030] flex items-center justify-center px-4">
|
||||
<button
|
||||
className="w-[100px] h-[40px] bg-gradient-to-b from-[#656565] to-[#313431] rounded-sm flex items-center justify-center text-white font-bold shadow-lg"
|
||||
onClick={() => navigateTo('home', 'main')}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1004
src/components/dsxpage/Tools.js
Normal file
1004
src/components/dsxpage/Tools.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user