// repeatability.js - 重复性测试相关功能 // 重复性测试时间数组 let repeatabilityTimes = []; // 图表相关变量 let repeatabilityCtx = null; let repeatabilityChartInstance = null; function initializeRepeatabilityPage() { // 生成重复性标签页内容 generateRepeatabilityTabContent(); // 加载重复性数据 loadRepeatabilityData(); } // 生成重复性标签页内容 function generateRepeatabilityTabContent() { console.log('generateRepeatabilityTabContent'); const repeatabilityTab = document.createElement('div'); repeatabilityTab.id = 'repeatability'; repeatabilityTab.className = 'tab-content active'; // 创建内容容器 const contentContainer = document.createElement('div'); contentContainer.className = 'content-container'; // 创建输入部分 const inputSection = document.createElement('div'); inputSection.className = 'input-section'; // 创建输入组 const inputGroup = document.createElement('div'); inputGroup.className = 'input-group'; inputGroup.id = 'repeatability-inputs'; // 添加默认的6个输入行 for (let i = 1; i <= 6; i++) { const row = createInputRow(i, 'repeatability', recordTimeAndCalculateRepeatability, deleteRepeatabilityInput); inputGroup.appendChild(row); } // 添加按钮 const addInputBtn = document.createElement('div'); addInputBtn.className = 'add-input-btn'; addInputBtn.onclick = addRepeatabilityInput; const addInputSpan = document.createElement('span'); addInputSpan.textContent = '+ 添加输入'; addInputBtn.appendChild(addInputSpan); // 组装输入部分 inputSection.appendChild(inputGroup); inputSection.appendChild(addInputBtn); // 创建结果部分 const resultsSection = document.createElement('div'); resultsSection.className = 'results-section'; // 创建结果显示 const results = document.createElement('div'); results.className = 'results'; // 添加结果项 const resultItems = [ { label: '极差R:', id: 'range' }, { label: '平均值x̄:', id: 'mean' }, { label: '方差s2:', id: 'variance' }, { label: '标准差s:', id: 'stdDev' }, { label: '相对标准偏差RSD(%):', id: 'rsd' } ]; resultItems.forEach(item => { const resultItem = document.createElement('div'); resultItem.className = 'result-item'; resultItem.innerHTML = `${item.label} -`; results.appendChild(resultItem); }); // 创建图表容器 const chartContainer = document.createElement('div'); chartContainer.className = 'chart-container'; const canvas = document.createElement('canvas'); canvas.id = 'lineChart'; chartContainer.appendChild(canvas); repeatabilityCtx = canvas.getContext('2d'); // 组装结果部分 resultsSection.appendChild(results); resultsSection.appendChild(chartContainer); // 组装整个内容 contentContainer.appendChild(inputSection); contentContainer.appendChild(resultsSection); repeatabilityTab.appendChild(contentContainer); if (document.getElementById('container')) { console.log('appendChild repeatabilityTab'); document.getElementById('container').appendChild(repeatabilityTab); } // document.querySelectorAll('#repeatability-inputs .input-row').forEach((row, index) => { // const deleteBtn = row.querySelector('.delete-btn'); // if (deleteBtn) { // deleteBtn.style.display = index < 0 ? 'none' : ''; // } // }); } // 添加重复性输入行 function addRepeatabilityInput() { const container = document.getElementById('repeatability-inputs'); const newIndex = container.children.length + 1; const row = createInputRow(newIndex, 'repeatability', recordTimeAndCalculateRepeatability, deleteRepeatabilityInput); container.appendChild(row); } // 删除重复性输入行 function deleteRepeatabilityInput(btn) { const row = btn.parentNode; const container = row.parentNode; const index = Array.from(container.children).indexOf(row); // 移除对应的时间记录 repeatabilityTimes.splice(index, 1); // 移除输入行 container.removeChild(row); // 更新剩余输入行的索引 const rows = container.querySelectorAll('.input-row'); rows.forEach((row, i) => { const timeSpan = row.querySelector(`span[id^="repeatability-time-text-"]`); const input = row.querySelector(`input[id^="repeatability-value-"]`); // 更新ID和文本 const newIndex = i + 1; timeSpan.id = `repeatability-time-text-${newIndex}`; input.id = `repeatability-value-${newIndex}`; // 如果有保存的时间,则显示时间,否则显示索引 if (repeatabilityTimes[i]) { timeSpan.textContent = `${parseUnixTime(repeatabilityTimes[i])}: `; } else { timeSpan.textContent = `${newIndex}: `; } // 更新输入事件 input.oninput = function () { recordTimeAndCalculateRepeatability(newIndex); }; }); document.querySelectorAll('#repeatability-inputs input[type="number"]').forEach((input) => { input.placeholder = `输入值`; }); calculateRepeatability(); saveRepeatabilityData(); } // 清除重复性数据 function clearRepeatabilityData() { // 清除重复性数据 const repeatabilityInputs = document.querySelectorAll('#repeatability-inputs .input-row'); for (let i = 6; i < repeatabilityInputs.length; i++) { repeatabilityInputs[i].remove(); } document.querySelectorAll('#repeatability-inputs input[type="number"]').forEach(input => { input.value = ''; }); repeatabilityTimes = []; // 重置重复性时间标签 for (let i = 1; i <= 6; i++) { const timeSpan = document.getElementById(`repeatability-time-text-${i}`); if (timeSpan) { timeSpan.textContent = `${i}: `; } } // 重置图表 if (repeatabilityChartInstance) { repeatabilityChartInstance.destroy(); repeatabilityChartInstance = null; } } // 记录重复性时间并计算 function recordTimeAndCalculateRepeatability(index) { recordTimeAndCalculate('repeatability', index, repeatabilityTimes, calculateRepeatability); } // 重复性计算 function calculateRepeatability() { const values = getNumericInputs('#repeatability-inputs input[type="number"]'); if (values.length >= 2) { // 计算统计量 const max = Math.max(...values); const min = Math.min(...values); const range = max - min; const mean = values.reduce((sum, val) => sum + val, 0) / values.length; // 计算方差和标准差 const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length; const stdDev = Math.sqrt(variance); const rsd = (stdDev / mean) * 100; // 相对标准偏差,以百分比表示 // 更新结果显示 updateResultDisplay('range', range); updateResultDisplay('mean', mean); updateResultDisplay('variance', variance); updateResultDisplay('stdDev', stdDev); updateResultDisplay('rsd', rsd); // 保存数据 saveRepeatabilityData(); // 更新图表 updateRepeatabilityChart(values); } else { // 清空结果显示 document.querySelectorAll('#repeatability .result-item span').forEach(span => { span.innerText = '-'; }); // 清空图表 if (repeatabilityChartInstance) { repeatabilityChartInstance.data.labels = []; repeatabilityChartInstance.data.datasets[0].data = []; repeatabilityChartInstance.update(); } } } // 重复性图表更新 function updateRepeatabilityChart(values) { // 获取时间标签 const timeLabels = []; repeatabilityTimes.forEach((time) => { // 使用已保存的时间戳生成标签 if (time > 9999) { timeLabels.push(parseUnixTime(time)); } }); // 使用通用图表创建函数 repeatabilityChartInstance = createOrUpdateChart(repeatabilityChartInstance, repeatabilityCtx, timeLabels, values, '重复性'); } // 重复性数据保存 function saveRepeatabilityData() { collectAndSaveData('#repeatability-inputs input[type="number"]', repeatabilityTimes, { range: 'range', mean: 'mean', variance: 'variance', stdDev: 'stdDev', rsd: 'rsd' }, 'repeatabilityData'); } // 重复性数据加载 function loadRepeatabilityData() { const savedRepeatabilityData = getLocalStorage('repeatabilityData'); if (savedRepeatabilityData) { try { const data = JSON.parse(savedRepeatabilityData); // 清除现有的额外输入框(保留前6个) const repeatabilityInputs = document.querySelectorAll('#repeatability-inputs .input-row'); for (let i = 6; i < repeatabilityInputs.length; i++) { repeatabilityInputs[i].remove(); } // 清除现有输入值 document.querySelectorAll('#repeatability-inputs input[type="number"]').forEach(input => { input.value = ''; }); // 填充保存的数据 if (data.inputs && data.inputs.length > 0) { data.inputs.forEach((item, index) => { const time = typeof item === 'object' ? item.time : ''; const value = typeof item === 'object' ? item.value : ''; // 如果索引超出现有输入框数量,添加新的输入框 if (index >= 6) { addRepeatabilityInput(); } // 设置时间 if (time) { const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = parseUnixTime(time) + ': '; repeatabilityTimes[index] = time; } } // 设置值 const input = document.querySelector(`#repeatability-inputs .input-row:nth-child(${index + 1}) input`); if (input) { input.value = value; } }); // 计算结果 calculateRepeatability(); } } catch (e) { console.error('加载重复性数据失败:', e); } } }