// error.js - 示值误差测试相关功能 // 图表相关变量 let errorCtx = null; let errorChartInstance = null; function initializeErrorPage() { // 生成示值误差标签页内容 generateErrorTabContent(); // 加载示值误差数据 loadErrorData(); } // 生成示值误差标签页内容 function generateErrorTabContent() { console.log('generateErrorTabContent'); const errorTab = document.createElement('div'); errorTab.id = 'error'; errorTab.className = 'tab-content'; // 创建内容容器 const contentContainer = document.createElement('div'); contentContainer.className = 'content-container'; // 创建输入部分 const inputSection = document.createElement('div'); inputSection.className = 'input-section'; inputSection.style.width = '100%'; // 创建误差容器 const errorContainer = document.createElement('div'); errorContainer.className = 'error-container'; errorContainer.id = 'error-inputs'; // 添加按钮 const addInputBtn = document.createElement('div'); addInputBtn.className = 'add-input-btn'; addInputBtn.onclick = addErrorPoint; const addInputSpan = document.createElement('span'); addInputSpan.textContent = '+ 添加测试点'; addInputBtn.appendChild(addInputSpan); // 创建不确定度结果部分 const uncertaintyResults = document.createElement('div'); uncertaintyResults.className = 'uncertainty-results'; uncertaintyResults.style.marginTop = '1rem'; uncertaintyResults.style.padding = '1rem'; uncertaintyResults.style.border = '1px solid #eee'; uncertaintyResults.style.borderRadius = '4px'; // 添加标题 const uncertaintyTitle = document.createElement('h3'); uncertaintyTitle.style.marginTop = '0'; uncertaintyTitle.textContent = '示值误差校准结果不确定度'; uncertaintyResults.appendChild(uncertaintyTitle); // 创建结果显示 const results = document.createElement('div'); results.className = 'results'; // 添加结果项 const resultItems = [ { label: 'A类不确定度(uA):', id: 'uncertainty-a' }, { label: 'B类不确定度(uB):', id: 'uncertainty-b' }, { label: 'C类不确定度(uc):', id: 'uncertainty-c' }, { label: '扩展不确定度(U):', id: 'uncertainty-u' } ]; resultItems.forEach(item => { const resultItem = document.createElement('div'); resultItem.className = 'result-item'; resultItem.innerHTML = `${item.label} -`; results.appendChild(resultItem); }); uncertaintyResults.appendChild(results); // 创建图表容器 const chartContainer = document.createElement('div'); chartContainer.className = 'chart-container'; const canvas = document.createElement('canvas'); canvas.id = 'errorChart'; chartContainer.appendChild(canvas); errorCtx = canvas.getContext('2d'); // 组装输入部分 inputSection.appendChild(errorContainer); inputSection.appendChild(addInputBtn); inputSection.appendChild(uncertaintyResults); inputSection.appendChild(chartContainer); // 组装整个内容 contentContainer.appendChild(inputSection); errorTab.appendChild(contentContainer); if (document.getElementById('container')) { console.log('appendChild errorTab'); document.getElementById('container').appendChild(errorTab); } // 添加初始示值误差测试点(因为已移除所有默认测试点) if (document.querySelectorAll('#error-inputs .error-point-container').length === 0) { addErrorPoint(); } } // 示值误差测试点容器创建 function createErrorPointContainer(pointValue = '') { const container = document.createElement('div'); container.className = 'error-point-container'; container.style.marginBottom = '1rem'; container.style.padding = '1rem'; container.style.border = '1px solid #eee'; container.style.borderRadius = '4px'; // 测试点输入 const pointRow = document.createElement('div'); pointRow.style.display = 'flex'; pointRow.style.alignItems = 'center'; pointRow.style.marginBottom = '0.5rem'; const pointLabel = document.createElement('label'); pointLabel.textContent = '测试点:'; pointLabel.style.marginRight = '0.5rem'; pointLabel.style.fontWeight = 'bold'; const pointInput = document.createElement('input'); pointInput.type = 'number'; pointInput.className = 'error-point-input'; pointInput.placeholder = '测试点'; pointInput.value = pointValue; pointInput.setAttribute('data-original', pointValue); pointInput.style.flex = '1'; pointInput.style.marginRight = '0.5rem'; pointInput.oninput = function () { updateErrorPoint(this); // 保存示值误差数据 saveErrorData(); }; const deleteBtn = document.createElement('span'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function () { container.parentNode.removeChild(container); calculateError(); // 保存误差数据 saveErrorData(); }; pointRow.appendChild(pointLabel); pointRow.appendChild(pointInput); pointRow.appendChild(deleteBtn); // 输入值部分 const inputsContainer = document.createElement('div'); inputsContainer.className = 'error-inputs'; inputsContainer.style.display = 'flex'; inputsContainer.style.flexWrap = 'wrap'; inputsContainer.style.gap = '0.5rem'; inputsContainer.style.marginBottom = '0.5rem'; // 添加3个默认输入框 for (let i = 0; i < 3; i++) { const input = document.createElement('input'); input.type = 'number'; input.className = 'error-input'; input.placeholder = `输入值`; input.setAttribute('data-point', pointValue); input.style.flex = '1'; input.style.minWidth = '100px'; input.oninput = function () { calculateError(); // 保存误差数据到localStorage saveErrorData(); }; inputsContainer.appendChild(input); } // 结果显示部分 const resultsRow = document.createElement('div'); resultsRow.style.display = 'flex'; resultsRow.style.flexDirection = 'column'; const avgContainer = document.createElement('div'); avgContainer.style.flex = '1'; const avgLabel = document.createElement('span'); avgLabel.textContent = '平均值:'; avgLabel.style.fontWeight = 'bold'; const avgSpan = document.createElement('span'); avgSpan.className = 'error-avg'; avgSpan.setAttribute('data-point', pointValue); avgSpan.textContent = '-'; avgContainer.appendChild(avgLabel); avgContainer.appendChild(avgSpan); const errorContainer = document.createElement('div'); errorContainer.style.flex = '1'; const errorLabel = document.createElement('span'); errorLabel.textContent = '示值误差:'; errorLabel.style.fontWeight = 'bold'; const errorSpan = document.createElement('span'); errorSpan.className = 'error-value'; errorSpan.setAttribute('data-point', pointValue); errorSpan.textContent = '-'; errorContainer.appendChild(errorLabel); errorContainer.appendChild(errorSpan); resultsRow.appendChild(avgContainer); resultsRow.appendChild(errorContainer); container.appendChild(pointRow); container.appendChild(inputsContainer); container.appendChild(resultsRow); return container; } // 示值误差测试点添加 function addErrorPoint() { const container = document.getElementById('error-inputs'); const pointContainer = createErrorPointContainer(); container.appendChild(pointContainer); } // 示值误差测试点更新 function updateErrorPoint(input) { const pointContainer = input.closest('.error-point-container'); const originalValue = input.getAttribute('data-original'); const newValue = input.value; // 如果测试点值发生变化,更新所有相关元素的data-point属性 if (originalValue !== newValue) { input.setAttribute('data-original', newValue); pointContainer.querySelectorAll('.error-input').forEach(input => { input.setAttribute('data-point', newValue); }); pointContainer.querySelector('.error-avg').setAttribute('data-point', newValue); pointContainer.querySelector('.error-value').setAttribute('data-point', newValue); } calculateError(); saveErrorData(); } // 清除示值误差数据 function clearErrorData() { // 清除示值误差数据 const errorContainer = document.getElementById('error-inputs'); while (errorContainer.firstChild) { errorContainer.removeChild(errorContainer.firstChild); } addErrorPoint(); // 重置图表 if (errorChartInstance) { errorChartInstance.destroy(); errorChartInstance = null; } } // 示值误差数据计算 function calculateError() { const pointContainers = document.querySelectorAll('.error-point-container'); const errorData = []; pointContainers.forEach(container => { const pointInput = container.querySelector('.error-point-input'); const inputs = container.querySelectorAll('.error-input'); const avgSpan = container.querySelector('.error-avg'); const errorSpan = container.querySelector('.error-value'); const pointValue = parseFloat(pointInput.value); const values = Array.from(inputs) .map(input => parseFloat(input.value)) .filter(value => !isNaN(value)); if (!isNaN(pointValue) && values.length > 0) { // 计算平均值 const avg = values.reduce((sum, val) => sum + val, 0) / values.length; // 计算示值误差 const error = avg - pointValue; // 更新显示 avgSpan.textContent = avg.toFixed(6); errorSpan.textContent = error.toFixed(6); // 收集数据用于图表和不确定度计算 errorData.push({ point: pointValue, avg: avg, error: error }); } else { avgSpan.textContent = '-'; errorSpan.textContent = '-'; } }); // 计算不确定度 calculateUncertainty(errorData); // 保存误差数据 saveErrorData(); // 更新图表 updateErrorChart(errorData); return errorData; } // 示值误差不确定度计算 function calculateUncertainty(errorData) { if (errorData.length === 0) { // 清空不确定度显示 document.getElementById('uncertainty-a').textContent = '-'; document.getElementById('uncertainty-b').textContent = '-'; document.getElementById('uncertainty-c').textContent = '-'; document.getElementById('uncertainty-u').textContent = '-'; return; } // 计算A类不确定度(重复测量的标准偏差) let sumSquaredDeviations = 0; let totalMeasurements = 0; document.querySelectorAll('.error-point-container').forEach(container => { const inputs = container.querySelectorAll('.error-input'); const values = Array.from(inputs) .map(input => parseFloat(input.value)) .filter(value => !isNaN(value)); if (values.length > 0) { const avg = values.reduce((sum, val) => sum + val, 0) / values.length; values.forEach(value => { sumSquaredDeviations += Math.pow(value - avg, 2); }); totalMeasurements += values.length; } }); // 计算A类不确定度 const uA = totalMeasurements > 1 ? Math.sqrt(sumSquaredDeviations / (totalMeasurements * (totalMeasurements - 1))) : 0; // 假设B类不确定度(仪器分辨率引起的不确定度) // 这里假设分辨率为0.001,则B类不确定度为0.001/√3 const resolution = 0.001; const uB = resolution / Math.sqrt(3); // 合成不确定度 const uC = Math.sqrt(Math.pow(uA, 2) + Math.pow(uB, 2)); // 扩展不确定度(k=2,约95%置信水平) const U = 2 * uC; // 更新显示 document.getElementById('uncertainty-a').textContent = uA.toExponential(3); document.getElementById('uncertainty-b').textContent = uB.toExponential(3); document.getElementById('uncertainty-c').textContent = uC.toExponential(3); document.getElementById('uncertainty-u').textContent = U.toExponential(3); } // 示值误差图表更新 function updateErrorChart(data) { if (data.length === 0) { if (errorChartInstance) { errorChartInstance.data.labels = []; errorChartInstance.data.datasets[0].data = []; errorChartInstance.update(); } return; } // 按测试点值排序 data.sort((a, b) => a.point - b.point); // 提取标签和误差值 const labels = data.map(item => item.point); const errorValues = data.map(item => item.error); // 使用通用图表创建函数 errorChartInstance = createOrUpdateChart(errorChartInstance, errorCtx, labels, errorValues, '示值误差'); } // 示值误差数据保存 function saveErrorData() { // 收集输入数据 const inputs = Array.from(document.querySelectorAll('.error-point-container')).map(container => { const pointInput = container.querySelector('.error-point-input'); const inputs = container.querySelectorAll('.error-input'); const avgSpan = container.querySelector('.error-avg'); const errorSpan = container.querySelector('.error-value'); return { point: pointInput.value, value: Array.from(inputs).map(input => input.value), avg: avgSpan.innerText, error: errorSpan.innerText }; }); // 收集结果数据 const results = { uncertainty_a: document.getElementById('uncertainty-a').textContent, uncertainty_b: document.getElementById('uncertainty-b').textContent, uncertainty_c: document.getElementById('uncertainty-c').textContent, uncertainty_u: document.getElementById('uncertainty-u').textContent }; const data = { inputs: inputs, results: results }; const dataName = 'errorData'; console.log(dataName, data); setLocalStorage(dataName, JSON.stringify(data)); } // 示值误差数据加载 function loadErrorData() { const savedErrorData = getLocalStorage('errorData'); if (savedErrorData) { try { const data = JSON.parse(savedErrorData); // 清除现有的测试点 const errorContainer = document.getElementById('error-inputs'); while (errorContainer.firstChild) { errorContainer.removeChild(errorContainer.firstChild); } // 添加保存的测试点 if (data.inputs && data.inputs.length > 0) { data.inputs.forEach((item, index) => { const point = typeof item === 'object' ? item.point : ''; const value = typeof item === 'object' ? item.value : ''; const pointContainer = createErrorPointContainer(item.point); errorContainer.appendChild(pointContainer); // 填充测试点 pointContainer.querySelector('.error-point-input').value = point; // 填充输入值 const inputs = pointContainer.querySelectorAll('.error-input'); value.forEach((value, index) => { if (index < inputs.length) { inputs[index].value = value; } }); }); } // 计算结果 calculateError(); } catch (e) { console.error('加载示值误差数据失败:', e); } } }