This commit is contained in:
2025-09-16 16:39:48 +08:00
commit c5808e85e2
336 changed files with 695951 additions and 0 deletions

View 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>
);
}