完善grade(统计成绩页),增加删除数据按钮,增加标准答案字典,支持导出标准答案,完善计分方式
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
.next/
|
||||
.next
|
||||
2
.next/cache/.rscinfo
vendored
2
.next/cache/.rscinfo
vendored
@@ -1 +1 @@
|
||||
{"encryption.key":"AxmjrhnEIlWTqaOj84DDbm8NSadx1bBdyyMc77kaHFY=","encryption.expire_at":1766739099583}
|
||||
{"encryption.key":"41RLTu25s6bKnxtTa0cAIxEID3ece30//eTx5e3P3aE=","encryption.expire_at":1767849224402}
|
||||
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
BIN
.next/cache/webpack/client-development/0.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
BIN
.next/cache/webpack/client-development/1.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
BIN
.next/cache/webpack/client-development/index.pack.gz
vendored
Binary file not shown.
Binary file not shown.
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
BIN
.next/cache/webpack/server-development/0.pack.gz
vendored
Binary file not shown.
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
BIN
.next/cache/webpack/server-development/index.pack.gz
vendored
Binary file not shown.
@@ -2,6 +2,6 @@
|
||||
"/_app": "pages/_app.js",
|
||||
"/_error": "pages/_error.js",
|
||||
"/_document": "pages/_document.js",
|
||||
"/": "pages/index.js",
|
||||
"/grade": "pages/grade.js"
|
||||
"/grade": "pages/grade.js",
|
||||
"/": "pages/index.js"
|
||||
}
|
||||
55
.next/trace
55
.next/trace
File diff suppressed because one or more lines are too long
@@ -162,9 +162,9 @@ export default function Office({
|
||||
<div
|
||||
className={`w-16 h-16 mb-1 transition-colors flex items-center justify-center ${getPortStyle(`1A-${i + 1}`)}`}
|
||||
jstype="testport-copper"
|
||||
id={`1A-${i + 1}`}
|
||||
onClick={() => onPortClick(`1A-${i + 1}`)}
|
||||
onMouseEnter={() => onPortHover(`1A-${i + 1}`)}
|
||||
id={`Rack1-1A-${i + 1}`}
|
||||
onClick={() => onPortClick(`Rack1-1A-${i + 1}`)}
|
||||
onMouseEnter={() => onPortHover(`Rack1-1A-${i + 1}`)}
|
||||
>
|
||||
<div className={`w-8 h-8 bg-[url('/rj45.png')] bg-contain bg-no-repeat bg-center`}></div>
|
||||
</div>
|
||||
@@ -190,9 +190,9 @@ export default function Office({
|
||||
className={`w-16 h-16 mb-1 transition-colors flex items-center justify-center ${getPortStyle(`1B-${i + 1}`)}`}
|
||||
jstype="testport-fiber"
|
||||
lcclean="false"
|
||||
id={`1B-${i + 1}`}
|
||||
onClick={() => onPortClick(`1B-${i + 1}`)}
|
||||
onMouseEnter={() => onPortHover(`1B-${i + 1}`)}
|
||||
id={`Rack1-1B-${i + 1}`}
|
||||
onClick={() => onPortClick(`Rack1-1B-${i + 1}`)}
|
||||
onMouseEnter={() => onPortHover(`Rack1-1B-${i + 1}`)}
|
||||
>
|
||||
<div className={`w-8 h-8 bg-[url('/lc.png')] bg-contain bg-no-repeat bg-center`}></div>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function OfficeTask() {
|
||||
<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">GB/T信道链路</span>标准。我们不能容忍任何故障链路。
|
||||
在这些重要链路正式启用前,集团工程部要求进行 <span className="font-semibold text-red-400">全面的物理层认证测试</span>。你的测试部署对于确保每条连接都符合严格的 <span className="font-medium text-yellow-300">GB/T永久链路</span>标准。我们不能容忍任何故障链路。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ const API_URLS = {
|
||||
// 教学相关
|
||||
TEACHING: {
|
||||
DATA: `${BASE_URL}/dsxapi/api/teaching/data`,
|
||||
CLEAR: `${BASE_URL}/dsxapi/api/teaching/clear`,
|
||||
},
|
||||
// 管理员鉴权相关
|
||||
ADMIN: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { API_URLS } from '@/config/api';
|
||||
import { faultReferenceDict, resolveFaultReference } from '@/store/faultReferenceDict';
|
||||
|
||||
|
||||
export default function Grade({ isComponent = false }) {
|
||||
@@ -34,108 +35,209 @@ export default function Grade({ isComponent = false }) {
|
||||
|
||||
|
||||
const exportToExcel = () => {
|
||||
import('xlsx').then((xlsx) => {
|
||||
// 获取表格元素
|
||||
const table = document.querySelector('table');
|
||||
if (!table) return;
|
||||
|
||||
// 获取表头数据
|
||||
const headers = [];
|
||||
const headerCells = table.querySelectorAll('thead th');
|
||||
headerCells.forEach(cell => {
|
||||
headers.push(cell.textContent.trim());
|
||||
});
|
||||
|
||||
// 获取表格数据
|
||||
const exportData = [];
|
||||
const rows = table.querySelectorAll('tbody tr');
|
||||
rows.forEach(row => {
|
||||
const rowData = {};
|
||||
const cells = row.querySelectorAll('td');
|
||||
cells.forEach((cell, index) => {
|
||||
// 获取单元格中的文本内容
|
||||
let cellText = '';
|
||||
// 查找所有span元素
|
||||
const spans = cell.querySelectorAll('span');
|
||||
if (spans.length > 0) {
|
||||
// 如果有span元素,合并它们的文本内容
|
||||
cellText = Array.from(spans)
|
||||
.map(span => span.textContent.trim())
|
||||
.filter(text => text) // 过滤掉空文本
|
||||
.join(' ');
|
||||
import('xlsx').then(async (xlsx) => {
|
||||
const isValidResult = (result) => {
|
||||
const mt = result?.testconfig?.moduleType;
|
||||
if (mt === '8000') {
|
||||
const lv = result?.testconfig?.params?.limitValue;
|
||||
const ps = (result?.CopperPerformanceStatus || '').toLowerCase();
|
||||
const ct = result?.testconfig?.params?.cableType;
|
||||
const wo = result?.testconfig?.params?.wireOrder;
|
||||
const ref = !!result?.CopperRef;
|
||||
let limitOk = false;
|
||||
if (ps.includes('mptl')) {
|
||||
limitOk = !!(lv?.includes('GBT') && lv?.includes('MPTL') && lv?.includes('PoE'));
|
||||
} else if (ps.includes('workshop')) {
|
||||
limitOk = !!lv?.includes('Profinet');
|
||||
} else {
|
||||
// 如果没有span元素,直接使用单元格的文本内容
|
||||
cellText = cell.textContent.trim();
|
||||
limitOk = !!lv?.includes('GBT');
|
||||
}
|
||||
rowData[headers[index]] = cellText;
|
||||
const cableOk = ct === 'Cat6 U/UTP';
|
||||
let wireOk = false;
|
||||
if (ps.includes('m12')) {
|
||||
wireOk = !!wo?.includes('M12');
|
||||
} else if (ps.includes('2p')) {
|
||||
wireOk = !!wo?.includes('Ethernet');
|
||||
} else {
|
||||
wireOk = wo === 'T568B';
|
||||
}
|
||||
return limitOk && cableOk && wireOk && ref;
|
||||
}
|
||||
if (mt === 'cfp') {
|
||||
const lv = result?.testconfig?.params?.limitValue;
|
||||
const ct = result?.testconfig?.params?.cableType;
|
||||
const connectorOk = Number(result?.testconfig?.params?.connectorCount) === 2;
|
||||
const spliceOk = Number(result?.testconfig?.params?.spliceCount) === 2;
|
||||
const refOk = result?.CFPRef === true;
|
||||
const refConnOk = result?.CFPRefConnect === true;
|
||||
const cleanOk = result?.PortCleanStatus === 2;
|
||||
const limitOk = !!lv?.includes('GBT');
|
||||
const cableOk = !!ct?.includes('OS2');
|
||||
return limitOk && cableOk && connectorOk && spliceOk && refOk && refConnOk && cleanOk;
|
||||
}
|
||||
if (mt === 'ofp') {
|
||||
const lv = result?.testconfig?.params?.limitValue;
|
||||
const ct = result?.testconfig?.params?.cableType;
|
||||
const refStatusOk = result?.ofpRefStatus === 'start';
|
||||
const refConnOk = result?.OFPRefConnect === true;
|
||||
const cleanOk = result?.PortCleanStatus === 2;
|
||||
const limitOk = !!lv?.includes('GBT');
|
||||
const cableOk = !!ct?.includes('OS2');
|
||||
return limitOk && cableOk && refStatusOk && refConnOk && cleanOk;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const summary = dataSource.map((row) => {
|
||||
const all = row.projects?.flatMap(p => p.testResults || []) || [];
|
||||
const counted = all.slice(0, 6);
|
||||
const validCount = counted.reduce((n, r) => n + (isValidResult(r) ? 1 : 0), 0);
|
||||
const pass = row.testResultsCount?.passCount || 0;
|
||||
const fail = row.testResultsCount?.failCount || 0;
|
||||
return {
|
||||
场景: scenario,
|
||||
用户: row.userId,
|
||||
项目: row.project || '',
|
||||
测试总数: pass + fail,
|
||||
计分测试条数: counted.length,
|
||||
有效数据条数: validCount,
|
||||
分数: row.score,
|
||||
报告数: Array.isArray(row.reports) ? row.reports.length : 0,
|
||||
最后更新时间: new Date(row.lastUpdate).toLocaleString('zh-CN')
|
||||
};
|
||||
});
|
||||
exportData.push(rowData);
|
||||
const details = [];
|
||||
dataSource.forEach((row) => {
|
||||
const all = row.projects?.flatMap(p => p.testResults || []) || [];
|
||||
const counted = all.slice(0, 6);
|
||||
counted.forEach((r) => {
|
||||
const mt = r?.testconfig?.moduleType || '';
|
||||
const status =
|
||||
mt === '8000' ? r.CopperResultStatus :
|
||||
mt === 'cfp' ? r.CFPResultStatus :
|
||||
mt === 'ofp' ? r.ofpResultStatus : '';
|
||||
details.push({
|
||||
场景: scenario,
|
||||
用户: row.userId,
|
||||
结果名称: r.name || '',
|
||||
模块: mt,
|
||||
有效性: isValidResult(r) ? '有效' : '无效',
|
||||
检测结果: status || '',
|
||||
极限值: r?.testconfig?.params?.limitValue || '',
|
||||
线缆类型: r?.testconfig?.params?.cableType || '',
|
||||
插座配置: r?.testconfig?.params?.wireOrder || '',
|
||||
参考状态: mt === '8000' ? (r?.CopperRef ? '基准已设置' : '基准未设置')
|
||||
: mt === 'cfp' ? (r?.CFPRef ? '参考已设置' : '参考未设置')
|
||||
: mt === 'ofp' ? (r?.ofpRefStatus || '') : ''
|
||||
});
|
||||
|
||||
// 创建工作簿并导出
|
||||
});
|
||||
});
|
||||
let reference = [];
|
||||
try {
|
||||
const resp = await fetch(API_URLS.CONNECTION.MAP_WITH_SCENE(scenario));
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
reference = Object.entries(data || {})
|
||||
.filter(([id]) => (
|
||||
!id.includes('main-permanent') &&
|
||||
!id.includes('main-channel') &&
|
||||
!id.includes('remote-channel') &&
|
||||
!id.includes('main-cfp-sm-out') &&
|
||||
!id.includes('main-cfp-mm-out') &&
|
||||
!id.includes('main-cfp-in') &&
|
||||
!id.includes('remote-cfp-sm-out') &&
|
||||
!id.includes('remote-cfp-mm-out') &&
|
||||
!id.includes('remote-cfp-in')
|
||||
))
|
||||
.map(([room, conn]) => {
|
||||
const ref = resolveFaultReference(conn);
|
||||
return {
|
||||
场景: scenario,
|
||||
链路名称: `${room}-${conn.connectedTo}`,
|
||||
类型: conn.type === 'fiber' ? '光纤' : '铜缆',
|
||||
故障类型: ref?.type || '',
|
||||
故障位置: ref?.location || '',
|
||||
故障原因: ref?.reason || ''
|
||||
};
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
const wb = xlsx.utils.book_new();
|
||||
const ws = xlsx.utils.json_to_sheet(exportData);
|
||||
xlsx.utils.book_append_sheet(wb, ws, '数据');
|
||||
|
||||
// 生成文件名
|
||||
const ws1 = xlsx.utils.json_to_sheet(summary);
|
||||
const ws2 = xlsx.utils.json_to_sheet(details);
|
||||
const ws3 = xlsx.utils.json_to_sheet(reference);
|
||||
xlsx.utils.book_append_sheet(wb, ws1, '概览');
|
||||
xlsx.utils.book_append_sheet(wb, ws2, '用户明细');
|
||||
xlsx.utils.book_append_sheet(wb, ws3, '参考答案');
|
||||
const fileName = isHistoryMode
|
||||
? `数据_历史_${historyInfo?.uuid || new Date().toISOString().split('T')[0]}.xlsx`
|
||||
: `数据_${new Date().toISOString().split('T')[0]}.xlsx`;
|
||||
|
||||
? `数据_历史_${historyInfo?.uuid || new Date().toISOString().split('T')[0]}_${scenario}.xlsx`
|
||||
: `数据_${new Date().toISOString().split('T')[0]}_${scenario}.xlsx`;
|
||||
xlsx.writeFile(wb, fileName);
|
||||
});
|
||||
};
|
||||
|
||||
// 计算评分的函数
|
||||
const calculateScore = (row) => {
|
||||
let score = 0;
|
||||
|
||||
// 项目名称加2分
|
||||
if (row.project === 'Joja-2025') {
|
||||
score += 2;
|
||||
score += 10;
|
||||
}else{
|
||||
// console.log('项目错误');
|
||||
console.log('项目错误');
|
||||
}
|
||||
|
||||
// 基准设置情况为已设置加2分
|
||||
const allResults = row.projects?.flatMap(project => project.testResults || []) || [];
|
||||
const hasResults = allResults.length > 0;
|
||||
const allSet = hasResults && allResults.every(result => result.CopperRef);
|
||||
|
||||
if (allSet) {
|
||||
score += 2;
|
||||
const countedResults = allResults.slice(0, 6);
|
||||
score += countedResults.length * 7.5;
|
||||
const validCount = countedResults.reduce((acc, result) => {
|
||||
const moduleType = result?.testconfig?.moduleType;
|
||||
if (moduleType === '8000') {
|
||||
const limitValue = result?.testconfig?.params?.limitValue;
|
||||
const performanceStatus = (result?.CopperPerformanceStatus || '').toLowerCase();
|
||||
const cableType = result?.testconfig?.params?.cableType;
|
||||
const wireOrder = result?.testconfig?.params?.wireOrder;
|
||||
const copperRef = !!result?.CopperRef;
|
||||
let limitValid = false;
|
||||
if (performanceStatus.includes('mptl')) {
|
||||
limitValid = !!(limitValue?.includes('GBT') && limitValue?.includes('MPTL') && limitValue?.includes('PoE'));
|
||||
} else if (performanceStatus.includes('workshop')) {
|
||||
limitValid = !!limitValue?.includes('Profinet');
|
||||
} else {
|
||||
// console.log('未设置基准');
|
||||
limitValid = !!limitValue?.includes('GBT');
|
||||
}
|
||||
// 检查测试极限值是否正确
|
||||
const allHaveLimit = hasResults && allResults.every(result =>
|
||||
result.testconfig?.params?.limitValue?.includes('GBT')
|
||||
);
|
||||
if (allHaveLimit) {
|
||||
score += 2;
|
||||
const cableTypeValid = cableType === 'Cat6 U/UTP';
|
||||
let wireOrderValid = false;
|
||||
if (performanceStatus.includes('m12')) {
|
||||
wireOrderValid = !!wireOrder?.includes('M12');
|
||||
} else if (performanceStatus.includes('2p')) {
|
||||
wireOrderValid = !!wireOrder?.includes('Ethernet');
|
||||
} else {
|
||||
// console.log('未设置测试极限值');
|
||||
wireOrderValid = wireOrder === 'T568B';
|
||||
}
|
||||
|
||||
// 检查命名方式是否符合xxx-xxx格式
|
||||
const hasValidNaming = allResults.some(result =>
|
||||
/^[^-]+(?:-[^-]+)+$/.test(result?.name || '')
|
||||
);
|
||||
if (hasValidNaming) {
|
||||
score += 2;
|
||||
} else {
|
||||
// console.log('命名方式不符合格式');
|
||||
return acc + (limitValid && cableTypeValid && wireOrderValid && copperRef ? 1 : 0);
|
||||
}
|
||||
|
||||
// 检查测试结果总数是否大于6
|
||||
const totalResults = row.testResultsCount.passCount + row.testResultsCount.failCount;
|
||||
if (totalResults >= 5) {
|
||||
score += 2;
|
||||
} else {
|
||||
// console.log('测试结果总数小于5');
|
||||
if (moduleType === 'cfp') {
|
||||
const limitValue = result?.testconfig?.params?.limitValue;
|
||||
const cableType = result?.testconfig?.params?.cableType;
|
||||
const connectorValid = Number(result?.testconfig?.params?.connectorCount) === 2;
|
||||
const spliceValid = Number(result?.testconfig?.params?.spliceCount) === 2;
|
||||
const refValid = result?.CFPRef === true;
|
||||
const refConnectValid = result?.CFPRefConnect === true;
|
||||
const cleanValid = result?.PortCleanStatus === 2;
|
||||
const limitValid = !!limitValue?.includes('GBT');
|
||||
const cableValid = !!cableType?.includes('OS2');
|
||||
return acc + (limitValid && cableValid && connectorValid && spliceValid && refValid && refConnectValid && cleanValid ? 1 : 0);
|
||||
}
|
||||
|
||||
return score;
|
||||
if (moduleType === 'ofp') {
|
||||
const limitValue = result?.testconfig?.params?.limitValue;
|
||||
const cableType = result?.testconfig?.params?.cableType;
|
||||
const refStatusValid = result?.ofpRefStatus === 'start';
|
||||
const refConnectValid = result?.OFPRefConnect === true;
|
||||
const cleanValid = result?.PortCleanStatus === 2;
|
||||
const limitValid = !!limitValue?.includes('GBT');
|
||||
const cableValid = !!cableType?.includes('OS2');
|
||||
return acc + (limitValid && cableValid && refStatusValid && refConnectValid && cleanValid ? 1 : 0);
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
score += validCount * 7.5;
|
||||
return Math.min(100, score);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
@@ -220,7 +322,7 @@ export default function Grade({ isComponent = false }) {
|
||||
// },
|
||||
|
||||
{
|
||||
title: '报告提交',
|
||||
title: '课后实验报告提交',
|
||||
dataIndex: 'reports',
|
||||
key: 'reports',
|
||||
},
|
||||
@@ -305,17 +407,87 @@ export default function Grade({ isComponent = false }) {
|
||||
|
||||
const [selectedRow, setSelectedRow] = useState(null);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [showReference, setShowReference] = useState(false);
|
||||
const [referenceConnections, setReferenceConnections] = useState([]);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [alertVisible, setAlertVisible] = useState(false);
|
||||
const [alertMessage, setAlertMessage] = useState('');
|
||||
const [alertType, setAlertType] = useState('info');
|
||||
const handleClearTeachingData = async () => {
|
||||
try {
|
||||
const resp = await fetch(API_URLS.TEACHING.CLEAR, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
scenario,
|
||||
org: orgFromUrl || ''
|
||||
})
|
||||
});
|
||||
if (resp.ok) {
|
||||
setDataSource([]);
|
||||
setOnlineCount(0);
|
||||
setIsHistoryMode(false);
|
||||
setHistoryInfo(null);
|
||||
setAlertMessage('已清除历史数据');
|
||||
setAlertType('success');
|
||||
setAlertVisible(true);
|
||||
} else {
|
||||
setAlertMessage('删除失败');
|
||||
setAlertType('error');
|
||||
setAlertVisible(true);
|
||||
}
|
||||
} catch (e) {
|
||||
setAlertMessage('删除异常');
|
||||
setAlertType('error');
|
||||
setAlertVisible(true);
|
||||
} finally {
|
||||
setShowDeleteConfirm(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRowClick = (record) => {
|
||||
setSelectedRow(record);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isModalVisible || !showReference) return;
|
||||
const fetchReference = async () => {
|
||||
try {
|
||||
const response = await fetch(API_URLS.CONNECTION.MAP_WITH_SCENE(scenario));
|
||||
if (!response.ok) return;
|
||||
const data = await response.json();
|
||||
const formatted = Object.entries(data || {})
|
||||
.filter(([portId]) => (
|
||||
!portId.includes('main-permanent') &&
|
||||
!portId.includes('main-channel') &&
|
||||
!portId.includes('remote-channel') &&
|
||||
!portId.includes('main-cfp-sm-out') &&
|
||||
!portId.includes('main-cfp-mm-out') &&
|
||||
!portId.includes('main-cfp-in') &&
|
||||
!portId.includes('remote-cfp-sm-out') &&
|
||||
!portId.includes('remote-cfp-mm-out') &&
|
||||
!portId.includes('remote-cfp-in')
|
||||
))
|
||||
.map(([source, connection]) => ({
|
||||
room: source,
|
||||
rack: connection.connectedTo,
|
||||
meta: connection
|
||||
}));
|
||||
setReferenceConnections(formatted);
|
||||
} catch (e) {
|
||||
console.error('参考答案数据获取失败:', e);
|
||||
}
|
||||
};
|
||||
fetchReference();
|
||||
}, [isModalVisible, showReference, scenario]);
|
||||
// 处理文件上传
|
||||
const handleFileUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file || !file.name.endsWith('.est')) {
|
||||
alert('请选择.est文件');
|
||||
setAlertMessage('请选择.est文件');
|
||||
setAlertType('warning');
|
||||
setAlertVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,7 +510,9 @@ export default function Grade({ isComponent = false }) {
|
||||
|
||||
const data = jsonData;
|
||||
if (!data.competitionStatus?.statisticsData) {
|
||||
alert('文件格式不正确');
|
||||
setAlertMessage('文件格式不正确');
|
||||
setAlertType('error');
|
||||
setAlertVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,7 +547,9 @@ export default function Grade({ isComponent = false }) {
|
||||
setOnlineCount(sortedData.length);
|
||||
} catch (error) {
|
||||
console.error('读取文件失败:', error);
|
||||
alert('读取文件失败');
|
||||
setAlertMessage('读取文件失败');
|
||||
setAlertType('error');
|
||||
setAlertVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -386,6 +562,16 @@ export default function Grade({ isComponent = false }) {
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h2 className="text-xl font-bold text-[#0ff]">详细信息 - {selectedRow?.userId || ''}</h2>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-2 text-[#0ff]/80">
|
||||
<span>显示参考答案</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showReference}
|
||||
onChange={(e) => setShowReference(e.target.checked)}
|
||||
className="accent-[#0ff]"
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
onClick={() => setIsModalVisible(false)}
|
||||
className="text-[#0ff] hover:text-[#0ff]/70"
|
||||
@@ -393,10 +579,11 @@ export default function Grade({ isComponent = false }) {
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 gap-6 overflow-hidden">
|
||||
{/* 左侧项目列表 */}
|
||||
<div className="w-1/2 overflow-y-auto custom-scrollbar bg-[#0F172A] rounded-lg p-4">
|
||||
<div className={`${showReference ? 'w-1/3' : 'w-1/2'} overflow-y-auto custom-scrollbar bg-[#0F172A] rounded-lg p-4`}>
|
||||
<div className="text-lg font-bold text-[#0ff] mb-4">测试数据</div>
|
||||
{selectedRow?.projects?.map((project) => (
|
||||
<div key={project.id} className="mb-6">
|
||||
@@ -430,28 +617,6 @@ export default function Grade({ isComponent = false }) {
|
||||
<span className="text-[#0ff] text-lg font-semibold">{result.name}</span>
|
||||
{(() => {
|
||||
|
||||
// 命名判断
|
||||
// const validNames = [
|
||||
// 'Room1-TO-1-1F-RackA-1A-1', 'Room1-TO-2-1F-RackA-1A-2',
|
||||
// 'Room3-TO-1-1F-RackA-1A-5', 'Room3-TO-2-1F-RackA-1A-6',
|
||||
// 'Room4-CAM-1F-RackA-1A-12',
|
||||
// '2F-RackA-1A-1-1F-RackA-1B-1', '2F-RackA-1A-2-1F-RackA-1B-2',
|
||||
// '2F-RackA-1A-3-1F-RackA-1B-3', '2F-RackA-1A-4-1F-RackA-1B-4',
|
||||
// '2F-RackA-1A-6-1F-RackA-1B-5', '2F-RackA-1A-5-1F-RackA-1B-6',
|
||||
// 'R-M-A-1-PLC-Rack-1A-1', 'R-M-A-2-PLC-Rack-1A-2',
|
||||
// 'PLC-Rack-1B-1-Data-Rack-1A-1', 'PLC-Rack-1B-2-Data-Rack-1A-2',
|
||||
// '1F-RackA-1A-1-Room1-TO-1', '1F-RackA-1A-2-Room1-TO-2',
|
||||
// '1F-RackA-1A-5-Room3-TO-1', '1F-RackA-1A-6-Room3-TO-2',
|
||||
// '1F-RackA-1A-12-Room4-CAM',
|
||||
// '1F-RackA-1B-1-2F-RackA-1A-1', '1F-RackA-1B-2-2F-RackA-1A-2',
|
||||
// '1F-RackA-1B-3-2F-RackA-1A-3', '1F-RackA-1B-4-2F-RackA-1A-4',
|
||||
// '1F-RackA-1B-5-2F-RackA-1A-6', '1F-RackA-1B-6-2F-RackA-1A-5',
|
||||
// 'PLC-Rack-1A-1-R-M-A-1', 'PLC-Rack-1A-2-R-M-A-2',
|
||||
// 'Data-Rack-1A-1-PLC-Rack-1B-1', 'Data-Rack-1A-2-PLC-Rack-1B-2',
|
||||
// '1F-RackA-1C-1', '1F-RackA-1C-2', '1F-RackA-1C-3', '1F-RackA-1C-4'
|
||||
// ];
|
||||
// const isValidName = validNames.includes(result.name);
|
||||
|
||||
const moduleType = result.testconfig?.moduleType;
|
||||
let isValid = false;
|
||||
|
||||
@@ -766,7 +931,7 @@ export default function Grade({ isComponent = false }) {
|
||||
|
||||
|
||||
{/* 右侧报告列表 */}
|
||||
<div className="w-1/2 overflow-y-auto custom-scrollbar bg-[#0F172A] rounded-lg p-4">
|
||||
<div className={`${showReference ? 'w-1/3' : 'w-1/2'} overflow-y-auto custom-scrollbar bg-[#0F172A] rounded-lg p-4`}>
|
||||
<div className="text-lg font-bold text-[#0ff] mb-4">报告记录</div>
|
||||
{(!selectedRow?.reports || selectedRow.reports.length === 0) ? (
|
||||
<div className="text-[#ff6600] text-center mt-8">暂无提交报告</div>
|
||||
@@ -806,12 +971,97 @@ export default function Grade({ isComponent = false }) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 右侧参考答案列表 */}
|
||||
{showReference && (
|
||||
<div className="w-1/3 overflow-y-auto custom-scrollbar bg-[#0F172A] rounded-lg p-4">
|
||||
<div className="text-lg font-bold text-[#0ff] mb-4">参考答案</div>
|
||||
{referenceConnections.length === 0 ? (
|
||||
<div className="text-[#ff6600] text-center mt-8">暂无参考数据</div>
|
||||
) : (
|
||||
referenceConnections.map((conn, idx) => {
|
||||
const fault = (() => {
|
||||
const m = conn.meta || {};
|
||||
let statusKey = '';
|
||||
if (m.wiremapstatus !== undefined && m.wiremapstatus !== null && String(m.wiremapstatus).length > 0) {
|
||||
if (String(m.wiremapstatus).toLowerCase() === 'pass') {
|
||||
statusKey = m.performancestatus ? `performancestatus:${m.performancestatus}` : '';
|
||||
} else {
|
||||
statusKey = `wiremapstatus:${m.wiremapstatus}`;
|
||||
}
|
||||
} else if (m.fiberstatus) {
|
||||
statusKey = `fiberstatus:${m.fiberstatus}`;
|
||||
}
|
||||
return statusKey ? faultReferenceDict[statusKey] || null : null;
|
||||
})();
|
||||
return (
|
||||
<div key={idx} className="mb-6 p-4 bg-[#1E293B] rounded-lg border border-[#0ff]/20">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-[#0ff] font-semibold">链路名称:{`${conn.room}-${conn.rack}`}</span>
|
||||
<span className="text-[#0ff]/70 text-sm">{conn.meta?.type === 'fiber' ? '光纤' : '铜缆'}</span>
|
||||
</div>
|
||||
<div className="text-[#0ff]/90">
|
||||
{fault ? (
|
||||
<div className="mb-2 p-2 rounded-md bg-[#334155] border border-[#0ff]/10">
|
||||
<div className="text-sm">类型: {fault.type}</div>
|
||||
<div className="text-sm">位置: {fault.location}</div>
|
||||
<div className="text-sm">原因: {fault.reason}</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-sm text-green-400 mt-2">未匹配到故障参考</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="p-4 md:p-6 bg-[#0F172A] ">
|
||||
{alertVisible && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-[#1E293B] rounded-lg p-6 w-full max-w-md">
|
||||
<div className="text-xl font-bold text-[#0ff] mb-4">提示</div>
|
||||
<div className={`${alertType === 'success' ? 'text-green-400' : alertType === 'error' ? 'text-red-400' : 'text-yellow-300'} mb-6`}>
|
||||
{alertMessage}
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={() => setAlertVisible(false)}
|
||||
className="bg-[#0ff]/20 hover:bg-[#0ff]/30 text-[#0ff] px-4 py-2 rounded transition-colors"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showDeleteConfirm && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||
<div className="bg-[#1E293B] rounded-lg p-6 w-full max-w-md">
|
||||
<div className="text-xl font-bold text-[#0ff] mb-4">确认删除</div>
|
||||
<div className="text-[#0ff]/80 mb-6">此操作将删除历史数据,请确认。</div>
|
||||
<div className="flex justify-end gap-3">
|
||||
<button
|
||||
onClick={() => setShowDeleteConfirm(false)}
|
||||
className="bg-[#0ff]/20 hover:bg-[#0ff]/30 text-[#0ff] px-4 py-2 rounded transition-colors"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClearTeachingData}
|
||||
className="bg-red-500/20 hover:bg-red-500/30 text-red-300 px-4 py-2 rounded transition-colors"
|
||||
>
|
||||
确认删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-4 md:mb-6 p-4 md:p-6 bg-[#1E293B] rounded-lg shadow-lg border border-[#0ff]/20">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
@@ -845,6 +1095,7 @@ export default function Grade({ isComponent = false }) {
|
||||
</select>
|
||||
|
||||
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
exportToExcel();
|
||||
@@ -854,6 +1105,13 @@ export default function Grade({ isComponent = false }) {
|
||||
导出至Excel
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setShowDeleteConfirm(true)}
|
||||
className="bg-red-500/20 hover:bg-red-500/30 text-red-300 px-4 py-2 rounded transition-colors"
|
||||
>
|
||||
删除历史数据
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
54
src/store/faultReferenceDict.js
Normal file
54
src/store/faultReferenceDict.js
Normal file
@@ -0,0 +1,54 @@
|
||||
export const faultReferenceDict = {
|
||||
'wiremapstatus:open': { type: '开路', location: '线芯1距离主机端0米,距离远端21.4米开路', reason: '可能是靠近主机端连接器压接不到位' },
|
||||
'wiremapstatus:open2': { type: '开路', location: '线对4,5距离主机端0.2米,距离远端22.4米开路', reason: '可能是靠近主机端连接器压接不到位' },
|
||||
'wiremapstatus:open3': { type: '开路', location: '线芯2、6、4、5、7、8距离主机端11米,远端13米开路', reason: '可能是被老鼠咬了' },
|
||||
'wiremapstatus:short': { type: '短路', location: '线芯1、2、6距离主机端1.1米处短路', reason: '可能是靠近主机端的位置线缆外护套破损' },
|
||||
'wiremapstatus:short2': { type: '短路', location: '线芯1、6、7距离远端1.1米处短路', reason: '可能是靠近远端的位置线缆外护套破损' },
|
||||
'wiremapstatus:short3': { type: '短路', location: '线芯1、2、4、8距离主机端0.1米处短路', reason: '可能是端接时外护套开剥过深' },
|
||||
'wiremapstatus:cross': { type: '跨接', location: '线对1、2与线对3、6跨接', reason: '端接错误' },
|
||||
'wiremapstatus:cross2': { type: '跨接', location: '线对1、2与线对4、5跨接', reason: '端接错误' },
|
||||
'wiremapstatus:cross3': { type: '跨接', location: '线对3、6与线对4、5跨接', reason: '端接错误' },
|
||||
'wiremapstatus:reversed': { type: '交叉', location: '线对4、5交叉', reason: '端接错误' },
|
||||
'wiremapstatus:reversed2': { type: '交叉', location:'线对1、2交叉', reason: '端接错误' },
|
||||
'wiremapstatus:reversed3': { type: '交叉', location: '线对3、6,线对7、8交叉', reason: '端接错误' },
|
||||
'wiremapstatus:miswire': { type: '乱序', location: '无法定位', reason: '端接错误' },
|
||||
'wiremapstatus:sopen': { type: '屏蔽层开路', location: '无法定位', reason: '未正确接地' },
|
||||
'wiremapstatus:ShieldOpen': { type: '屏蔽层开路', location: '无法定位', reason: '未正确接地' },
|
||||
'wiremapstatus:ShieldShort': { type: '屏蔽层短路', location: '线芯4距离主机端0.1米处短路', reason: '未正确可能是端接时外护套开剥过深' },
|
||||
|
||||
'performancestatus:return-loss-fail': { type: '回波损耗不合格', location: '整条链路', reason: '链路阻抗不连续' },
|
||||
'performancestatus:next-fail': { type: '近端串扰不合格', location: '两端连接器', reason: '两端连接器的串扰过高' },
|
||||
'performancestatus:lengthfail': { type: '长度超限', location: '整条链路', reason: '链路长度超过标准,导致插入损耗过高' },
|
||||
'performancestatus:ohmfail': { type: '电阻类故障', location: '线对1、2', reason: '导体电阻偏高或不均衡' },
|
||||
'performancestatus:mptl-nextfail': { type: '电阻类故障', location: '线对4、5', reason: '线对间电阻差过大' },
|
||||
'performancestatus:pass': { type: '通过', location: '无', reason: '链路通过' },
|
||||
'performancestatus:pass-4m': { type: '通过', location: '无', reason: '链路通过' },
|
||||
'performancestatus:pass-30m': { type: '通过', location: '无', reason: '链路通过' },
|
||||
'performancestatus:workshop-m12-pass-30m': { type: '通过', location: '无', reason: '链路通过' },
|
||||
|
||||
'fiberstatus:bend': { type: '光纤弯曲', location: '在48.95米处出现弯曲', reason: '可能是光纤出现扭曲/弯折' },
|
||||
'fiberstatus:connector-fail-start': { type: '连接器故障', location: '距离主机的第一个连接器', reason: '可能是耦合器质量较差或者连接器损坏/污染' },
|
||||
'fiberstatus:connector-fail-end': { type: '连接器故障', location: '最后一个连接器', reason: '可能是耦合器质量较差或者连接器损坏/污染' },
|
||||
'fiberstatus:splice-fail': { type: '熔接点故障', location: '在12.3米处', reason: '熔接点损耗过大' },
|
||||
'fiberstatus:sm-pass': { type: '单模通过', location: '无', reason: '链路通过' },
|
||||
};
|
||||
|
||||
export const resolveFaultReference = (meta) => {
|
||||
const m = meta || {};
|
||||
const wm = m.wiremapstatus;
|
||||
const pm = m.performancestatus;
|
||||
const fs = m.fiberstatus;
|
||||
let key = '';
|
||||
if (wm !== undefined && wm !== null && String(wm).length > 0) {
|
||||
const wmStr = String(wm).toLowerCase();
|
||||
if (wmStr.startsWith('pass')) {
|
||||
key = pm ? `performancestatus:${pm}` : '';
|
||||
} else {
|
||||
key = `wiremapstatus:${wm}`;
|
||||
}
|
||||
} else if (fs) {
|
||||
key = `fiberstatus:${fs}`;
|
||||
}
|
||||
if (!key) return null;
|
||||
return faultReferenceDict[key] || null;
|
||||
};
|
||||
Reference in New Issue
Block a user