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