新增grade(成绩统计)页面

This commit is contained in:
2025-12-25 03:34:48 +00:00
parent 37c61f8e72
commit a389879406
32 changed files with 1773 additions and 60 deletions

View File

@@ -338,7 +338,7 @@ export default function Cursors() {
setTimeout(() => {
setToneStatus(null);
}, 1200);
console.log(isConnected);
// console.log(isConnected);
}
}
});

View File

@@ -1,5 +1,7 @@
import React, { useState } from 'react';
import useDeviceStore from '@/store/deviceStore';
import useDisplayStore from '@/store/displayStore';
import { API_URLS } from '@/config/api';
const ResultTable = () => {
const [records, setRecords] = useState([
@@ -92,8 +94,68 @@ const ResultTable = () => {
};
const { getCurrentProject,getCurrentOperator, getCurrentTestConfig} = useDisplayStore();
// 提交表单
const handleSubmit = () => {
const { faultScenarios, seatNumber, seatOrg,setTotalToastMessage } = useDeviceStore();
// 获取测试结果统计
const getTestResults = () => {
const currentProject = getCurrentProject();
if (!currentProject?.testResults) return { passCount: 0, failCount: 0 };
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;
return { passCount, failCount };
};
// 获取基准状态
const getRefStatus = () => {
const currentConfig = getCurrentTestConfig();
const moduleType = currentConfig?.moduleType;
if (!moduleType) return false;
const { ref } = useDisplayStore.getState();
if (moduleType === '8000') return ref.copper?.status || false;
if (moduleType === 'cfp') return ref.cfp?.status || false;
if (moduleType === 'ofp') return ref.ofp?.status || false;
return false;
};
const currentProject = getCurrentProject();
const currentOperator = getCurrentOperator();
const currentConfig = getCurrentTestConfig();
const refStatus = getRefStatus();
const { passCount, failCount } = getTestResults();
const handleSubmit = async () => {
// 验证表单
const isValid = records.every(record => {
if (!record.linkName.trim()) return false;
@@ -112,9 +174,52 @@ const ResultTable = () => {
// 直接使用当前的 records 更新 store
useDeviceStore.getState().updateReports(records);
alert('记录提交成功!');
try {
const statisticsData = {
userId: seatNumber,
org: seatOrg,
scenario: faultScenarios,
project: currentProject?.name || '',
operator: currentOperator?.name || '',
testLimit: currentConfig?.params?.limitValue || '',
refStatus,
testResultsCount: { passCount, failCount },
projects: useDisplayStore.getState().projects,
selectedIndexes:useDisplayStore.getState().selectedIndexes,
reports: records,
};
if (['Office', 'Industry', 'DataCenter'].includes(faultScenarios)) {
const response = await fetch(API_URLS.TEACHING.DATA, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
scenario: faultScenarios,
fingerprint: seatNumber,
data: statisticsData,
timestamp: Date.now(),
}),
});
if (!response.ok) {
try {
const err = await response.json();
console.error('数据发送失败:', err);
} catch {
console.error('数据发送失败');
}
} else {
setTotalToastMessage('记录提交成功!');
}
}
} catch (e) {
console.error('数据发送异常:', e);
}
};
return (
<div className="bg-[#1E293B] rounded-lg p-6 text-white">
<div className="flex justify-between items-center mb-6">
@@ -244,4 +349,4 @@ const ResultTable = () => {
);
};
export default ResultTable;
export default ResultTable;

View File

@@ -136,8 +136,8 @@ export default function Result() {
}
const newResults = testResults.map(result => {
console.log(testResults);
console.log(result);
// console.log(testResults);
// console.log(result);
if (result.name === selectedResults[0]) {
// 处理CFP模块类型的特殊重命名逻辑
@@ -149,7 +149,7 @@ export default function Result() {
if (oldName === result.inputname) {
// 查找关联结果inputname相同但name不同的结果
const relatedResult = testResults.find(r => r.inputname === result.inputname && r.name !== result.name);
console.log("找到的关联结果:", relatedResult);
// console.log("找到的关联结果:", relatedResult);
// 如果是当前选中的结果更新name和inputname
if (result.name === selectedResults[0]) {
// 更新选中结果的name和inputname

View File

@@ -229,7 +229,7 @@ export default function Testing() {
wireOrder === 'Ethernet Two-Pair' ||
wireOrder === 'M12-D Two-Pair') {
CopperWiremapResultStatus = 'pass';
console.log(CopperWiremapResultStatus);
// console.log(CopperWiremapResultStatus);
}
} else if (CopperWiremapStatus === 'pass-2pair') {
if (wireOrder === 'Ethernet Two-Pair' ||
@@ -305,7 +305,7 @@ export default function Testing() {
for (const pair of loopPairs) {
if (data.performance.OHM.LOOP[pair] > limitdata.LOOP) {
hasNegativeMargin = true;
console.log("电阻fail")
// console.log("电阻fail")
break;
}
}
@@ -322,7 +322,7 @@ export default function Testing() {
for (const pair of pairUblPairs) {
if (Math.abs(data.performance.OHM.PAIRUBL[pair]) > limitdata.PAIRUBL) {
hasNegativeMargin = true;
console.log("UBL fail")
// console.log("UBL fail")
break;
}
}
@@ -334,7 +334,7 @@ export default function Testing() {
for (const pair of p2pUblPairs) {
if (Math.abs(data.performance.OHM.P2PUBL[pair]) > limitdata.P2PUBL) {
hasNegativeMargin = true;
console.log("P2P fail")
// console.log("P2P fail")
break;
}
}

View File

@@ -57,7 +57,7 @@ export default function Tools() {
const handleVfl = () => {
fetchConnectionMap();
if (!mainVFLEnd) return;
console.log(connectionStatus?.mainPaths?.['main-vfl']);
// console.log(connectionStatus?.mainPaths?.['main-vfl']);
// 在connectionMap中查找与mainVFLEnd匹配的连接
let connectedPort = null;

View File

@@ -358,7 +358,7 @@ export default function CopperPerformance( ) {
const renderFooter = () => {
const showHDTDR = estmodel === 'general' && testResult?.CopperResultStatus === 'fail' && view === 'DRAW' && curtitle === '回波损耗';
const showHDTDX = estmodel === 'general' && testResult?.CopperResultStatus === 'fail' && view === 'DRAW' && curtitle === 'NEXT';
console.log(estmodel, CopperResultStatus, view, curtitle, showHDTDR, showHDTDX);
// console.log(estmodel, CopperResultStatus, view, curtitle, showHDTDR, showHDTDX);
return (
<div className="h-[60px] bg-[#132843] flex items-center justify-end px-8 gap-4">
{showHDTDR && (

View File

@@ -136,8 +136,8 @@ export default function Result() {
}
const newResults = testResults.map(result => {
console.log(testResults);
console.log(result);
// console.log(testResults);
// console.log(result);
if (result.name === selectedResults[0]) {
// 处理CFP模块类型的特殊重命名逻辑
@@ -149,7 +149,7 @@ export default function Result() {
if (oldName === result.inputname) {
// 查找关联结果inputname相同但name不同的结果
const relatedResult = testResults.find(r => r.inputname === result.inputname && r.name !== result.name);
console.log("找到的关联结果:", relatedResult);
// console.log("找到的关联结果:", relatedResult);
// 如果是当前选中的结果更新name和inputname
if (result.name === selectedResults[0]) {
// 更新选中结果的name和inputname

View File

@@ -229,7 +229,7 @@ export default function Testing() {
wireOrder === 'Ethernet Two-Pair' ||
wireOrder === 'M12-D Two-Pair') {
CopperWiremapResultStatus = 'pass';
console.log(CopperWiremapResultStatus);
// console.log(CopperWiremapResultStatus);
}
} else if (CopperWiremapStatus === 'pass-2pair') {
if (wireOrder === 'Ethernet Two-Pair' ||
@@ -305,7 +305,7 @@ export default function Testing() {
for (const pair of loopPairs) {
if (data.performance.OHM.LOOP[pair] > limitdata.LOOP) {
hasNegativeMargin = true;
console.log("电阻fail")
// console.log("电阻fail")
break;
}
}
@@ -322,7 +322,7 @@ export default function Testing() {
for (const pair of pairUblPairs) {
if (Math.abs(data.performance.OHM.PAIRUBL[pair]) > limitdata.PAIRUBL) {
hasNegativeMargin = true;
console.log("UBL fail")
// console.log("UBL fail")
break;
}
}
@@ -334,7 +334,7 @@ export default function Testing() {
for (const pair of p2pUblPairs) {
if (Math.abs(data.performance.OHM.P2PUBL[pair]) > limitdata.P2PUBL) {
hasNegativeMargin = true;
console.log("P2P fail")
// console.log("P2P fail")
break;
}
}

View File

@@ -57,7 +57,7 @@ export default function Tools() {
const handleVfl = () => {
fetchConnectionMap();
if (!mainVFLEnd) return;
console.log(connectionStatus?.mainPaths?.['main-vfl']);
// console.log(connectionStatus?.mainPaths?.['main-vfl']);
// 在connectionMap中查找与mainVFLEnd匹配的连接
let connectedPort = null;

View File

@@ -84,8 +84,13 @@ export default function IndustryTask() {
<strong className="text-gray-100">测试准备</strong> 使<span className="font-medium text-yellow-300">DSX </span>
</li>
<li>
<strong className="text-gray-100">认证测试</strong> <span className="font-medium text-yellow-300">TIA 1005 </span><span className="font-medium text-yellow-300">(Crosstalk)</span><span className="font-medium text-yellow-300">(EMI)E2</span>
<strong className="text-gray-100">认证测试</strong> <span className="font-medium text-yellow-300">GB/T </span><span className="font-medium text-yellow-300">(Crosstalk)</span>
</li>
{/*
<li>
<strong className="text-gray-100">认证测试:</strong> 执行<span className="font-medium text-yellow-300">TIA 1005 </span>标准测试,重点关注<span className="font-medium text-yellow-300">串扰(Crosstalk)</span>和<span className="font-medium text-yellow-300">电磁干扰(EMI)抗性至少需要保证E2等级</span>。
</li>
*/}
<li>
<strong className="text-gray-100">数据分析</strong> <span className="font-semibold text-green-400">(PASS)</span><span className="font-semibold text-red-400">(FAIL*)</span>
</li>

View File

@@ -58,7 +58,7 @@ export default function OfficeTask() {
<p className="text-sm font-semibold text-cyan-400 uppercase tracking-wider">新任务单</p>
<h1 className="text-2xl md:text-3xl font-bold text-gray-100 mt-1">任务要求网络链路认证</h1>
<p className="text-xs text-gray-400">任务编号: SIM-TRN-NTW-005 </p>
<p className="text-xs text-gray-400">地点: Innovate Solutions 公司 - 8 层西翼</p>
<p className="text-xs text-gray-400">地点: Joja 公司 - 8 层西翼</p>
</div>
{/* 情况说明 (背景) */}
@@ -66,13 +66,13 @@ export default function OfficeTask() {
<h2 className="text-xl font-semibold text-cyan-300">情况说明</h2>
<p className="text-gray-300 leading-relaxed">
Innovate Solutions 公司刚刚完成了对其 8 层西翼办公区<span className="font-medium text-yellow-300">Room1 Room4</span> <span className="font-medium text-yellow-300"> 1 (Rack1)</span>
Joja 公司刚刚完成了对其 8 层西翼办公区<span className="font-medium text-yellow-300">Room1 Room4</span> <span className="font-medium text-yellow-300"> 1 (Rack1)</span>
</p>
<div className=" h-100 relative">
<Image src="/Office.png" alt="office" fill className="object-contain"/>
</div>
<p className="text-gray-300 leading-relaxed">
在这些重要链路正式启用前集团工程部要求进行 <span className="font-semibold text-red-400">全面的物理层认证测试</span> <span className="font-medium text-yellow-300">TIA</span>
在这些重要链路正式启用前集团工程部要求进行 <span className="font-semibold text-red-400">全面的物理层认证测试</span> <span className="font-medium text-yellow-300">GB/T</span>
</p>
</div>
@@ -86,6 +86,9 @@ export default function OfficeTask() {
<li>
<strong className="text-gray-100">部署测试设备</strong> 使 <span className="font-medium text-yellow-300">DSX 线</span>
</li>
<li>
<strong className="text-gray-100">创建测试项目</strong> <span className="font-medium text-yellow-300">Joja-8F</span>
</li>
<li>
<strong className="text-gray-100">执行认证测试</strong> <span className="font-medium text-yellow-300"></span> <span className="font-medium text-yellow-300">T568B</span> 线 <span className="font-medium text-yellow-300">Cat 6 (F/UTP)</span> 线
</li>
@@ -99,7 +102,7 @@ export default function OfficeTask() {
<strong className="text-gray-100">诊断故障点</strong> (<span className='text-red-400 font-bold'>FAIL*</span>)使,
</li>
<li>
<strong className="text-gray-100">记录与报告</strong> 使<code className="bg-gray-700 px-1.5 py-0.5 rounded text-xs text-yellow-300">[]-[]</code> (<code className="bg-gray-700 px-1.5 py-0.5 rounded text-xs text-yellow-300">Room1-TO-1-Rack1-1A-1</code>)
<strong className="text-gray-100">记录与报告</strong> 使<code className="bg-gray-700 px-1.5 py-0.5 rounded text-xs text-yellow-300">[]-[]</code> (<code className="bg-gray-700 px-1.5 py-0.5 rounded text-xs text-yellow-300">Room1-TO-1-Rack1-1A-1</code>)
</li>
</ul>
</div>

View File

@@ -23,6 +23,10 @@ const API_URLS = {
START: `${BASE_URL}/dsxapi/api/competition/start`,
END: `${BASE_URL}/dsxapi/api/competition/end`,
},
// 教学相关
TEACHING: {
DATA: `${BASE_URL}/dsxapi/api/teaching/data`,
},
// 管理员鉴权相关
ADMIN: {
VERIFY: `${BASE_URL}/verify-admin`,

View File

@@ -3,6 +3,7 @@ import Office from '@/components/scene/Office';
import Industry from '@/components/scene/Industry';
import WorldSkill from '@/components/scene/WorldSkill';
import CompetitionPage from './competition';
import Grade from './grade';
import { API_URLS } from '@/config/api';
import { useRouter } from 'next/router';
@@ -18,6 +19,7 @@ export default function AdminPage() {
const [availableScenes, setAvailableScenes] = useState(['Office', 'Industry', 'WorldSkill']);
const [showModal, setShowModal] = useState(false);
const [showhistoryModal, setShowhistoryModal] = useState(false);
const [showgradeModal, setShowgradeModal] = useState(false);
const [uuid, setUuid] = useState(null);
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
const [confirmDialogMessage, setConfirmDialogMessage] = useState('');
@@ -420,6 +422,25 @@ export default function AdminPage() {
</div>
)}
{/* 成绩模态框 */}
{showgradeModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="w-full h-full">
<div className='absolute top-26 right-12 z-50' >
<button
onClick={async () => {
setShowgradeModal(false)
}}
className="cursor-pointer bg-[#0ff]/20 hover:bg-[#0ff]/30 text-[#0ff] px-4 py-2 rounded transition-colors"
>
返回控制页面
</button>
</div>
<Grade isComponent={false}/>
</div>
</div>
)}
<div className="max-w-8xl mx-auto">
<div className="flex justify-between items-center mb-6">
@@ -469,6 +490,16 @@ export default function AdminPage() {
</button>
</>
)}
{/* <button
onClick={async () => {
setShowgradeModal(true)
}}
className="px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition-colors"
>
过程评价
</button> */}
<button
onClick={handleInit}
disabled={loading}

1540
src/pages/grade.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -84,6 +84,8 @@ export default function Home() {
WorldSkillScenarios,
seatNumber,
updateSeatNumber,
seatOrg,
updateSeatOrg,
seatUUID,
updateSeatUUID,
seatStartTime,
@@ -145,6 +147,7 @@ useEffect(() => {
const urlParams = new URLSearchParams(currentUrl.split('?')[1] || '');
let faultScenariosParam = urlParams.get('faultScenarios') || '';
let estmodelParam = urlParams.get('estmodel') || '';
let org = urlParams.get('org') || '';
try {
faultScenariosParam = decodeURIComponent(faultScenariosParam);
@@ -163,6 +166,9 @@ useEffect(() => {
if (estmodelParam) {
updateEstmodel(estmodelParam);
}
if (org) {
updateSeatOrg(org);
}
} catch (error) {
// 鉴权失败或解析异常:阻止访问
document.body.innerHTML =
@@ -198,13 +204,14 @@ useEffect(() => {
}
const data = await response.json();
console.log('data:', data);
const username = data?.username;
if (!username) {
throw new Error('认证成功但未返回用户名');
}
// 鉴权成功:更新赛位号
updateSeatNumber(username);
// 更新组织机构
setIsFromUrl(true)
} catch (error) {
// 鉴权失败或解析异常:阻止访问
@@ -217,7 +224,6 @@ useEffect(() => {
}, []);
// 工具函数:合并类名,过滤掉假值
function classNames(...classes) {
return classes.filter(Boolean).join(' ');
@@ -639,7 +645,7 @@ function classNames(...classes) {
</button>
{/* 右侧按钮 */}
{/* {faultScenarios !== 'WorldSkill' && ( */}
{faultScenarios !== 'CopperAnalyzer' && (
<button
onClick={() => setIsRightDrawerOpen(!isRightDrawerOpen)}
@@ -656,7 +662,7 @@ function classNames(...classes) {
</div>
</button>
{/* )} */}
)}
{/* 左侧抽屉模态框 */}

View File

@@ -264,7 +264,10 @@ devtools(
// faultScenarios: "CopperAnalyzer",
WorldSkillScenarios: "OFFICE",
// 赛位号
// seatNumber: "husky",
seatNumber: "",
// 组织机构
seatOrg: "",
// 比赛ID
seatUUID: null,
// 比赛开始时间
@@ -497,7 +500,12 @@ devtools(
seatNumber: number
}));
},
// 更新组织机构
updateSeatOrg: (org) => {
set(() => ({
seatOrg: org
}));
},
// 更新比赛ID
updateSeatUUID: (uuid) => {
set(() => ({