// data.js - 数据管理、导入导出相关功能 // 收集并保存数据到localStorage function collectAndSaveData(selector, timeSelector, resultIds, cookieName) { // 收集输入数据 const inputs = Array.from(document.querySelectorAll(selector)).map((input, index) => { const timeSpan = document.getElementById(`${timeSelector}-${index + 1}`); return { value: input.value, time: timeSpan ? timeSpan.textContent.trim() : '' }; }); // 收集结果数据 const results = {}; if (resultIds) { Object.keys(resultIds).forEach(key => { const element = document.getElementById(resultIds[key]); if (element) { results[key] = element.innerText; } }); } // 保存到localStorage const data = { inputs: inputs, results: results }; console.log(data); setLocalStorage(cookieName, JSON.stringify(data)); } // 添加自定义参数 function addCustomParam() { const container = document.getElementById('custom-params-container'); const paramRow = document.createElement('div'); paramRow.className = 'custom-param-row'; paramRow.style.display = 'flex'; paramRow.style.alignItems = 'center'; paramRow.style.gap = '0.5rem'; const nameInput = document.createElement('input'); nameInput.type = 'text'; nameInput.className = 'param-name'; nameInput.placeholder = '参数名称'; nameInput.style.flex = '1'; nameInput.style.padding = '0.5rem'; nameInput.style.border = '1px solid #ccc'; nameInput.style.borderRadius = '4px'; nameInput.oninput = saveCustomParamsData; const valueInput = document.createElement('input'); valueInput.type = 'text'; valueInput.className = 'param-value'; valueInput.placeholder = '参数值'; valueInput.style.flex = '1'; valueInput.style.padding = '0.5rem'; valueInput.style.border = '1px solid #ccc'; valueInput.style.borderRadius = '4px'; valueInput.oninput = saveCustomParamsData; const deleteBtn = document.createElement('span'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function () { container.removeChild(paramRow); saveCustomParamsData(); }; paramRow.appendChild(nameInput); paramRow.appendChild(valueInput); paramRow.appendChild(deleteBtn); container.appendChild(paramRow); } // 保存自定义参数数据 function saveCustomParamsData() { const customParams = Array.from(document.querySelectorAll('#custom-params-container .custom-param-row')).map(row => ({ name: row.querySelector('.param-name').value, value: row.querySelector('.param-value').value })); setLocalStorage('customParamsData', JSON.stringify(customParams), 365); } // 从localStorage加载自定义参数 function loadCustomParamsFromLocalStorage() { const savedParams = getLocalStorage('customParamsData'); if (savedParams) { try { const params = JSON.parse(savedParams); const container = document.getElementById('custom-params-container'); // 清除现有参数 while (container.firstChild) { container.removeChild(container.firstChild); } // 添加保存的自定义参数 params.forEach(param => { const paramRow = document.createElement('div'); paramRow.className = 'custom-param-row'; paramRow.style.display = 'flex'; paramRow.style.alignItems = 'center'; paramRow.style.gap = '0.5rem'; const nameInput = document.createElement('input'); nameInput.type = 'text'; nameInput.className = 'param-name'; nameInput.placeholder = '参数名称'; nameInput.style.flex = '1'; nameInput.style.padding = '0.5rem'; nameInput.style.border = '1px solid #ccc'; nameInput.style.borderRadius = '4px'; nameInput.value = param.name; nameInput.oninput = saveCustomParamsData; const valueInput = document.createElement('input'); valueInput.type = 'text'; valueInput.className = 'param-value'; valueInput.placeholder = '参数值'; valueInput.style.flex = '1'; valueInput.style.padding = '0.5rem'; valueInput.style.border = '1px solid #ccc'; valueInput.style.borderRadius = '4px'; valueInput.value = param.value; valueInput.oninput = saveCustomParamsData; const deleteBtn = document.createElement('span'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function () { container.removeChild(paramRow); saveCustomParamsData(); }; paramRow.appendChild(nameInput); paramRow.appendChild(valueInput); paramRow.appendChild(deleteBtn); container.appendChild(paramRow); }); } catch (e) { console.error('加载自定义参数失败:', e); } } } // 从localStorage加载数据 function loadDataFromLocalStorage() { // 加载重复性数据 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 value = typeof item === 'object' ? item.value : item; const time = typeof item === 'object' ? item.time : ''; // 如果索引超出现有输入框数量,添加新的输入框 if (index >= 6) { addRepeatabilityInput(); } // 设置值 const input = document.querySelector(`#repeatability-inputs .input-row:nth-child(${index + 1}) input`); if (input) { input.value = value; } // 设置时间 if (time) { const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = `${time}: `; repeatabilityTimes[index] = time; } } }); // 计算结果 calculateRepeatability(); } } catch (e) { console.error('加载重复性数据失败:', e); } } // 加载稳定性数据 const savedStabilityData = getLocalStorage('stabilityData'); if (savedStabilityData) { try { const data = JSON.parse(savedStabilityData); // 清除现有的额外输入框(保留前6个) const stabilityInputs = document.querySelectorAll('#stability-inputs .input-row'); for (let i = 6; i < stabilityInputs.length; i++) { stabilityInputs[i].remove(); } // 清除现有输入值 document.querySelectorAll('#stability-inputs input[type="number"]').forEach(input => { input.value = ''; }); // 填充保存的数据 if (data.inputs && data.inputs.length > 0) { data.inputs.forEach((item, index) => { const value = typeof item === 'object' ? item.value : item; const time = typeof item === 'object' ? item.time : ''; // 如果索引超出现有输入框数量,添加新的输入框 if (index >= 6) { addStabilityInput(); } // 设置值 const input = document.querySelector(`#stability-inputs .input-row:nth-child(${index + 1}) input`); if (input) { input.value = value; } // 设置时间 if (time) { const timeSpan = document.getElementById(`stability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = `${time}: `; stabilityTimes[index] = time; } } }); // 计算结果 calculateStability(); } } catch (e) { console.error('加载稳定性数据失败:', e); } } // 加载示值误差数据 const savedErrorData = getLocalStorage('errorData'); if (savedErrorData) { try { const errorData = JSON.parse(savedErrorData); // 清除现有的测试点 const errorContainer = document.getElementById('error-inputs'); while (errorContainer.firstChild) { errorContainer.removeChild(errorContainer.firstChild); } // 添加保存的测试点 errorData.forEach(item => { const pointContainer = createErrorPointContainer(item.point); errorContainer.appendChild(pointContainer); // 填充输入值 const inputs = pointContainer.querySelectorAll('.error-input'); item.inputs.forEach((value, index) => { if (index < inputs.length) { inputs[index].value = value; } }); }); // 计算结果 calculateError(); updateErrorPoints(); } catch (e) { console.error('加载示值误差数据失败:', e); } } } // 导出数据 function exportData() { const data = { repeatability: { inputs: Array.from(document.querySelectorAll('#repeatability-inputs input[type="number"]')).map((input, index) => { const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`); return { value: input.value, time: timeSpan ? timeSpan.textContent.trim() : '' }; }), results: { range: document.getElementById('range').innerText, mean: document.getElementById('mean').innerText, variance: document.getElementById('variance').innerText, stdDev: document.getElementById('stdDev').innerText, rsd: document.getElementById('rsd').innerText } }, stability: { inputs: Array.from(document.querySelectorAll('#stability-inputs input[type="number"]')).map((input, index) => { const timeSpan = document.getElementById(`stability-time-text-${index + 1}`); return { value: input.value, time: timeSpan ? timeSpan.textContent.trim() : '' }; }), results: { max: document.getElementById('stability-max').innerText, min: document.getElementById('stability-min').innerText, mean: document.getElementById('stability-mean').innerText, range: document.getElementById('stability-range').innerText } }, error: Array.from(document.querySelectorAll('.error-point-container')).map(container => ({ point: container.querySelector('.error-point-input').value, inputs: Array.from(container.querySelectorAll('.error-input')).map(input => input.value), avg: container.querySelector('.error-avg').innerText, error: container.querySelector('.error-value').innerText })), customParams: Array.from(document.querySelectorAll('#custom-params-container .custom-param-row')).map(row => ({ name: row.querySelector('.param-name').value, value: row.querySelector('.param-value').value })), projectTitle: document.getElementById('project-title').value, timestamp: new Date().toISOString() }; const jsonString = JSON.stringify(data, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `lab-statistics-${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); // 保存所有数据到localStorage saveDataToLocalStorage(); } // 导入数据 function importData() { const fileInput = document.getElementById('importFile'); const file = fileInput.files[0]; if (!file) { alert('请选择要导入的文件!'); return; } const reader = new FileReader(); reader.onload = function(e) { try { const data = JSON.parse(e.target.result); // 导入项目标题 if (data.projectTitle) { document.getElementById('project-title').value = data.projectTitle; updateProjectTitle(); } // 清除现有数据 clearAllData(); // 导入重复性数据 if (data.repeatability && data.repeatability.inputs) { const inputsContainer = document.getElementById('repeatability-inputs'); // 清除现有的额外输入框(保留前6个) while (inputsContainer.children.length > 6) { inputsContainer.removeChild(inputsContainer.lastChild); } // 获取现有的输入框 const existingInputs = document.querySelectorAll('#repeatability-inputs input[type="number"]'); // 填充现有输入框的值 data.repeatability.inputs.forEach((item, index) => { const value = typeof item === 'object' ? item.value : item; const time = typeof item === 'object' ? item.time : ''; if (index < existingInputs.length) { // 如果有对应的输入框,直接设置值 existingInputs[index].value = value; // 更新时间显示 if (time) { const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = time + ' '; repeatabilityTimes[index] = time; } } } else { // 如果没有对应的输入框,创建新的 addRepeatabilityInput(); // 获取新创建的输入框并设置值 const newInput = document.querySelector('#repeatability-inputs .input-row:last-child input'); newInput.value = value; // 更新时间显示 if (time) { const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = time + ' '; repeatabilityTimes[index] = time; } } } }); calculateRepeatability(); } // 导入稳定性数据 if (data.stability && data.stability.inputs) { const inputsContainer = document.getElementById('stability-inputs'); // 清除现有的额外输入框(保留前6个) while (inputsContainer.children.length > 6) { inputsContainer.removeChild(inputsContainer.lastChild); } // 获取现有的输入框 const existingInputs = document.querySelectorAll('#stability-inputs input[type="number"]'); // 填充现有输入框的值 data.stability.inputs.forEach((item, index) => { const value = typeof item === 'object' ? item.value : item; const time = typeof item === 'object' ? item.time : ''; if (index < existingInputs.length) { // 如果有对应的输入框,直接设置值 existingInputs[index].value = value; // 更新时间显示 if (time) { const timeSpan = document.getElementById(`stability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = time + ' '; stabilityTimes[index] = time; } } } else { // 如果没有对应的输入框,创建新的 addStabilityInput(); // 获取新创建的输入框并设置值 const newInput = document.querySelector('#stability-inputs .input-row:last-child input'); newInput.value = value; // 更新时间显示 if (time) { const timeSpan = document.getElementById(`stability-time-text-${index + 1}`); if (timeSpan) { timeSpan.textContent = time + ' '; stabilityTimes[index] = time; } } } }); calculateStability(); } // 导入示值误差数据 if (data.error && data.error.length > 0) { const errorContainer = document.getElementById('error-inputs'); while (errorContainer.firstChild) { errorContainer.removeChild(errorContainer.firstChild); } // 处理导入的测试点数据 data.error.forEach(item => { addErrorPoint(); const pointContainers = document.querySelectorAll('.error-point-container'); const pointContainer = pointContainers[pointContainers.length - 1]; // 更新测试点的输入框值 const pointInput = pointContainer.querySelector('.error-point-input'); pointInput.value = item.point; pointInput.setAttribute('data-original', item.point); // 更新输入框的data-point属性 const inputs = pointContainer.querySelectorAll('.error-input'); inputs.forEach(input => { input.setAttribute('data-point', item.point); }); // 更新结果span的data-point属性 const avgSpan = pointContainer.querySelector('.error-avg'); const errorSpan = pointContainer.querySelector('.error-value'); avgSpan.setAttribute('data-point', item.point); errorSpan.setAttribute('data-point', item.point); // 填充输入值 item.inputs.forEach((value, index) => { if (index < inputs.length) { inputs[index].value = value; } }); }); calculateError(); } // 导入自定义参数数据 if (data.customParams && data.customParams.length > 0) { const container = document.getElementById('custom-params-container'); while (container.firstChild) { container.removeChild(container.firstChild); } // 添加保存的自定义参数 data.customParams.forEach(param => { const paramRow = document.createElement('div'); paramRow.className = 'custom-param-row'; paramRow.style.display = 'flex'; paramRow.style.alignItems = 'center'; paramRow.style.gap = '0.5rem'; const nameInput = document.createElement('input'); nameInput.type = 'text'; nameInput.className = 'param-name'; nameInput.placeholder = '参数名称'; nameInput.style.flex = '1'; nameInput.style.padding = '0.5rem'; nameInput.style.border = '1px solid #ccc'; nameInput.style.borderRadius = '4px'; nameInput.value = param.name; nameInput.oninput = saveCustomParamsData; const valueInput = document.createElement('input'); valueInput.type = 'text'; valueInput.className = 'param-value'; valueInput.placeholder = '参数值'; valueInput.style.flex = '1'; valueInput.style.padding = '0.5rem'; valueInput.style.border = '1px solid #ccc'; valueInput.style.borderRadius = '4px'; valueInput.value = param.value; valueInput.oninput = saveCustomParamsData; const deleteBtn = document.createElement('span'); deleteBtn.className = 'delete-btn'; deleteBtn.innerHTML = '×'; deleteBtn.onclick = function () { container.removeChild(paramRow); saveCustomParamsData(); }; paramRow.appendChild(nameInput); paramRow.appendChild(valueInput); paramRow.appendChild(deleteBtn); container.appendChild(paramRow); }); } // 保存所有数据到localStorage saveDataToLocalStorage(); alert('数据导入成功!'); } catch (error) { alert('导入失败!请检查文件格式。'); console.error('导入错误:', error); } }; reader.readAsText(file); } // 保存所有数据到localStorage(兼容性函数) function saveDataToLocalStorage() { saveRepeatabilityData(); saveStabilityData(); saveErrorData(); saveCustomParamsData(); }