JY 5 месяцев назад
Родитель
Сommit
5058df0271

+ 243 - 222
LabStatistics/common.js

@@ -1,48 +1,20 @@
 // common.js - 通用功能和初始化函数
 
-// 页面加载完成后初始化
-document.addEventListener('DOMContentLoaded', function () {
-    // 添加初始示值误差测试点(因为已移除所有默认测试点)
-    if (document.querySelectorAll('#error-inputs .error-point-container').length === 0) {
-        addErrorPoint();
-    }
-
-
-    // 调用各个模块的占位符更新函数
-    if (typeof updateRepeatabilityInputPlaceholders === 'function') {
-        updateRepeatabilityInputPlaceholders();
-    }
-
-    if (typeof updateStabilityInputPlaceholders === 'function') {
-        updateStabilityInputPlaceholders();
-    }
-    // 调用各个模块的删除按钮更新函数
-    if (typeof updateRepeatabilityDeleteButtons === 'function') {
-        updateRepeatabilityDeleteButtons();
-    }
-
-    if (typeof updateStabilityDeleteButtons === 'function') {
-        updateStabilityDeleteButtons();
-    }
-
-    // 调用updateStabilityInputs以保持向后兼容性
-    if (typeof updateStabilityInputs === 'function') {
-        updateStabilityInputs();
-    }
-    // 初始化示值误差测试点的删除按钮
-    updateErrorPoints();
+// 计时器相关变量
+let timerInterval = null;
+let timerTotalSeconds = 600; // 默认10分钟
+let timerCurrentSeconds = 600;
+let timerPaused = false;
 
-    loadTimerSettings();
-    // 初始化页面数据
+// 在页面加载完成后初始化
+document.addEventListener('DOMContentLoaded', function () {
+    // 初始化页面
     initializePage();
 });
 
 // 标签页切换功能
 document.querySelectorAll('.tab').forEach(tab => {
-    tab.addEventListener('click', function() {
-        // 保存当前自定义参数
-        saveCustomParamsData();
-
+    tab.addEventListener('click', function () {
         // 切换标签页
         const tabId = this.getAttribute('data-tab');
         document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
@@ -52,7 +24,198 @@ document.querySelectorAll('.tab').forEach(tab => {
     });
 });
 
-// 通用函数
+function initializePage() {
+    showToast('加载主架构....', 'success');
+
+    // 初始化时启动时间更新
+    updateCurrentTime();
+    loadTimerSettings();
+
+    showToast('初始化页面....', 'success');
+
+    // 各页面初始化
+    initializeRepeatabilityPage();
+    initializeStabilityPage();
+    initializeErrorPage();
+    initializeDataPage();
+
+    showToast('加载完成', 'success');
+}
+
+function updateProjectTitle() {
+    const titleInput = document.getElementById('project-title');
+    const title = titleInput.value.trim() || '无标题';
+    document.title = `${title} - LabStatistics`;
+    setLocalStorage('projectTitle', title);
+}
+
+
+// 更新当前时间显示
+function updateCurrentTime() {
+    const now = new Date();
+    const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
+    document.getElementById('current-time').textContent = timeString;
+    setTimeout(updateCurrentTime, 1000);
+}
+
+// 记录时间并执行计算
+function recordTimeAndCalculate(type, index, timesArray, calculateFunction) {
+    const now = new Date();
+    const timestamp = now.getTime(); // 获取时间戳
+    const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
+
+    // 更新时间显示
+    document.getElementById(`${type}-time-text-${index}`).textContent = `${timeString}: `;
+
+    // 记录测试时间戳
+    timesArray[index - 1] = timestamp;
+
+    // 执行计算
+    if (typeof calculateFunction === 'function') {
+        calculateFunction();
+    }
+}
+
+// 加载计时器设置
+function loadTimerSettings() {
+    const savedMinutes = getLocalStorage('timerMinutes');
+    const savedSeconds = getLocalStorage('timerSeconds');
+
+    if (savedMinutes !== null && savedSeconds !== null) {
+        const minutes = parseInt(savedMinutes);
+        const seconds = parseInt(savedSeconds);
+        document.getElementById('timer-minutes').value = minutes;
+        document.getElementById('timer-seconds').value = seconds;
+        timerTotalSeconds = minutes * 60 + seconds;
+        timerCurrentSeconds = timerTotalSeconds;
+        updateTimerDisplay();
+    }
+}
+
+// 保存计时器设置
+function saveTimerSettings(minutes, seconds) {
+    setLocalStorage('timerMinutes', minutes, 30);
+    setLocalStorage('timerSeconds', seconds, 30);
+}
+
+// 打开计时器对话框
+function openTimerDialog() {
+    document.getElementById('timer-dialog').style.display = 'flex';
+    loadTimerSettings();
+}
+
+// 关闭计时器对话框
+function closeTimerDialog() {
+    document.getElementById('timer-dialog').style.display = 'none';
+}
+
+// 开始计时器
+function startTimer() {
+    if (timerCurrentSeconds <= 0) {
+        return;
+    }
+
+    // 如果已暂停,则继续计时
+    if (timerPaused) {
+        timerPaused = false;
+        document.querySelector('#timer-dialog button:nth-child(1)').textContent = '暂停';
+    } else {
+        if (document.querySelector('#timer-dialog button:nth-child(1)').textContent == '开始') {
+            // 获取设置的时间
+            const minutes = parseInt(document.getElementById('timer-minutes').value) || 0;
+            const seconds = parseInt(document.getElementById('timer-seconds').value) || 0;
+            timerTotalSeconds = minutes * 60 + seconds;
+            timerCurrentSeconds = timerTotalSeconds;
+
+            // 保存设置到localStorage
+            saveTimerSettings(minutes, seconds);
+
+            // 停止警报铃声
+            document.getElementById('alertSound').pause();
+            document.getElementById('alertSound').currentTime = 0;
+
+            // 更新暂停按钮文本为暂停
+            document.querySelector('#timer-dialog button:nth-child(1)').textContent = '暂停';
+        } else {
+            if (timerInterval) {
+                clearInterval(timerInterval);
+                timerInterval = null;
+                timerPaused = true;
+
+                // 停止警报铃声
+                document.getElementById('alertSound').pause();
+                document.getElementById('alertSound').currentTime = 0;
+
+                // 更新暂停按钮文本为继续
+                document.querySelector('#timer-dialog button:nth-child(1)').textContent = '继续';
+            }
+        }
+    }
+    closeTimerDialog();
+
+    // 更新显示
+    updateTimerDisplay();
+
+    // 开始倒计时
+    if (!timerInterval && !timerPaused) {
+        timerInterval = setInterval(updateTimer, 1000);
+    }
+}
+
+// 重置计时器
+function resetTimer() {
+    if (timerInterval) {
+        clearInterval(timerInterval);
+        timerInterval = null;
+    }
+    timerPaused = false;
+
+    // 获取设置的时间
+    const minutes = parseInt(document.getElementById('timer-minutes').value) || 0;
+    const seconds = parseInt(document.getElementById('timer-seconds').value) || 0;
+    timerTotalSeconds = minutes * 60 + seconds;
+    timerCurrentSeconds = timerTotalSeconds;
+
+    // 更新显示
+    updateTimerDisplay();
+
+    // 停止警报铃声
+    document.getElementById('alertSound').pause();
+    document.getElementById('alertSound').currentTime = 0;
+
+    // 更新暂停按钮文本为开始
+    document.querySelector('#timer-dialog button:nth-child(1)').textContent = '开始';
+}
+
+// 更新计时器
+function updateTimer() {
+    if (timerCurrentSeconds > 0) {
+        timerCurrentSeconds--;
+        updateTimerDisplay();
+
+        // 当倒计时接近结束时播放警报
+        if (timerCurrentSeconds <= 3 && timerCurrentSeconds > 0) {
+            document.getElementById('alertSound').play();
+        }
+    } else {
+        // 倒计时结束
+        clearInterval(timerInterval);
+        timerInterval = null;
+
+        // 播放警报
+        document.getElementById('alertSound').play();
+    }
+}
+
+// 更新计时器显示
+function updateTimerDisplay() {
+    const minutes = Math.floor(timerCurrentSeconds / 60);
+    const seconds = timerCurrentSeconds % 60;
+    const displayText = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+    document.getElementById('countdown').textContent = displayText;
+}
+
+// 自定义参数输入行创建函数
 function createInputRow(index, type, onInputFunction, onDeleteFunction) {
     const row = document.createElement('div');
     row.className = 'input-row';
@@ -65,12 +228,12 @@ function createInputRow(index, type, onInputFunction, onDeleteFunction) {
     input.type = 'number';
     input.placeholder = '输入值';
     input.id = `${type}-value-${index}`;
-    input.oninput = function() { onInputFunction(index); };
+    input.oninput = function () { onInputFunction(index); };
 
-    const deleteBtn = document.createElement('span');
+    const deleteBtn = document.createElement('div');
     deleteBtn.className = 'delete-btn';
     deleteBtn.innerHTML = '×';
-    deleteBtn.onclick = function() { onDeleteFunction(this); };
+    deleteBtn.onclick = function () { onDeleteFunction(this); };
 
     row.appendChild(timeSpan);
     row.appendChild(input);
@@ -79,31 +242,25 @@ function createInputRow(index, type, onInputFunction, onDeleteFunction) {
     return row;
 }
 
-function updateInputRows(selector, minRows) {
-    const rows = document.querySelectorAll(selector);
-    rows.forEach((row, index) => {
-        const deleteBtn = row.querySelector('.delete-btn');
-        if (index < minRows - 1) {
-            deleteBtn.style.visibility = 'hidden';
+function getNumericInputs(selector) {
+    return Array.from(document.querySelectorAll(selector))
+        .map(input => parseFloat(input.value))
+        .filter(value => !isNaN(value));
+}
+
+function updateResultDisplay(id, value, precision = 6) {
+    const element = document.getElementById(id);
+    if (element) {
+        if (value === null || value === undefined || isNaN(value)) {
+            element.innerText = '-';
         } else {
-            deleteBtn.style.visibility = 'visible';
+            // 格式化数字,去除尾随的0
+            const formatted = parseFloat(value.toFixed(precision)).toString();
+            element.innerText = formatted;
         }
-    });
+    }
 }
 
-function recordTimeAndCalculate(type, index, timesArray, calculateFunction) {
-    const now = new Date();
-    const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
-
-    // 更新时间显示
-    document.getElementById(`${type}-time-text-${index}`).textContent = `${timeString}: `;
-
-    // 记录测试时间
-    timesArray[index - 1] = timeString;
-
-    // 执行计算
-    calculateFunction();
-}
 
 function createOrUpdateChart(chartInstance, ctx, labels, data, label) {
     if (chartInstance) {
@@ -140,169 +297,33 @@ function createOrUpdateChart(chartInstance, ctx, labels, data, label) {
     }
 }
 
-function getNumericInputs(selector) {
-    return Array.from(document.querySelectorAll(selector))
-        .map(input => parseFloat(input.value))
-        .filter(value => !isNaN(value));
-}
-
-function updateResultDisplay(id, value, precision = 6) {
-    const element = document.getElementById(id);
-    if (element) {
-        if (value === null || value === undefined || isNaN(value)) {
-            element.innerText = '-';
-        } else {
-            // 格式化数字,去除尾随的0
-            const formatted = parseFloat(value.toFixed(precision)).toString();
-            element.innerText = formatted;
-        }
-    }
-}
-
-function updateInputPlaceholders() {
-    updateInputRows('#repeatability-inputs .input-row', 6);
-}
-
-function updateStabilityInputs() {
-    updateInputRows('#stability-inputs .input-row', 6);
-}
-
-function updateErrorPoints() {
-    const pointContainers = document.querySelectorAll('#error-inputs .error-point-container');
-    pointContainers.forEach((container, index) => {
-        const deleteBtn = container.querySelector('.delete-btn');
-        if (pointContainers.length <= 1) {
-            deleteBtn.style.visibility = 'hidden';
-        } else {
-            deleteBtn.style.visibility = 'visible';
-        }
+// 收集并保存数据到localStorage
+function collectAndSaveData(selector, times, resultIds, dataName) {
+    // 收集输入数据
+    const inputs = Array.from(document.querySelectorAll(selector)).map((input, index) => {
+        return {
+            time: times[index] ? times[index] : null,
+            value: input.value
+        };
     });
-}
 
-function updateProjectTitle() {
-    const titleInput = document.getElementById('project-title');
-    const title = titleInput.value.trim() || '无标题';
-    updateDocumentTitle();
-    setLocalStorage('projectTitle', title, 365);
-}
-
-function updateDocumentTitle() {
-    const titleInput = document.getElementById('project-title');
-    const title = titleInput.value.trim() || '无标题';
-    document.title = `${title} - LabStatistics`;
-}
-
-function newProject() {
-    if (confirm('确定要新建工程吗?这将清除所有当前数据!')) {
-        // 清除所有数据
-        clearAllData();
-        // 重置标题
-        document.getElementById('project-title').value = '';
-        updateProjectTitle();
-        // 重新初始化页面
-        initializePage();
-    }
-}
-
-function clearAllData() {
-    // 清除重复性数据
-    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}: `;
-        }
-    }
-
-    // 清除稳定性数据
-    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 = '';
-    });
-    stabilityTimes = [];
-
-    // 重置稳定性时间标签
-    const stabilityTimes = [0, 10, 20, 30, 40, 50];
-    for (let i = 1; i <= 6; i++) {
-        const timeSpan = document.getElementById(`stability-time-text-${i}`);
-        if (timeSpan) {
-            timeSpan.textContent = `${stabilityTimes[i-1]}: `;
-        }
-    }
-
-    // 清除示值误差数据
-    const errorContainer = document.getElementById('error-inputs');
-    while (errorContainer.firstChild) {
-        errorContainer.removeChild(errorContainer.firstChild);
-    }
-    addErrorPoint();
-
-    // 清除自定义参数
-    const customParamsContainer = document.getElementById('custom-params-container');
-    while (customParamsContainer.firstChild) {
-        customParamsContainer.removeChild(customParamsContainer.firstChild);
-    }
-
-    // 重置图表
-    if (chartInstance) {
-        chartInstance.destroy();
-        chartInstance = null;
-    }
-    if (stabilityChartInstance) {
-        stabilityChartInstance.destroy();
-        stabilityChartInstance = null;
-    }
-    if (errorChartInstance) {
-        errorChartInstance.destroy();
-        errorChartInstance = null;
-    }
-
-    // 清除结果显示
-    document.querySelectorAll('.result-item span').forEach(span => {
-        span.innerText = '-';
-    });
-
-    // 清除localStorage数据
-    removeLocalStorage('repeatabilityData');
-    removeLocalStorage('stabilityData');
-    removeLocalStorage('errorData');
-    removeLocalStorage('customParamsData');
-}
-
-function initializePage() {
-    // 加载项目标题
-    const savedTitle = getLocalStorage('projectTitle');
-    if (savedTitle) {
-        document.getElementById('project-title').value = savedTitle;
-        updateDocumentTitle();
+    // 收集结果数据
+    const results = {};
+    if (resultIds) {
+        Object.keys(resultIds).forEach(key => {
+            const element = document.getElementById(resultIds[key]);
+            if (element) {
+                results[key] = element.innerText;
+            }
+        });
     }
 
-    // 加载自定义参数
-    loadCustomParamsFromLocalStorage();
+    // 保存到localStorage
+    const data = {
+        inputs: inputs,
+        results: results
+    };
+    console.log(dataName, data);
 
-    // 加载重复性、稳定性和示值误差数据
-    loadDataFromLocalStorage();
+    setLocalStorage(dataName, JSON.stringify(data));
 }
-
-// 更新当前时间显示
-function updateCurrentTime() {
-    const now = new Date();
-    const timeString = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
-    document.getElementById('current-time').textContent = timeString;
-    setTimeout(updateCurrentTime, 1000);
-}
-
-// 初始化时启动时间更新
-updateCurrentTime();

+ 17 - 209
LabStatistics/index.html

@@ -133,6 +133,7 @@
             display: flex;
             align-items: center;
             justify-content: center;
+            width: 30px;
         }
 
         .add-input-btn {
@@ -344,7 +345,7 @@
 </head>
 
 <body>
-    <audio id="alertSound" src="alert.mp3" preload="auto" loop></audio>
+    <audio id="alertSound" src="./alert.mp3" preload="auto" loop></audio>
     <!-- 倒计时设置对话框 -->
     <div id="timer-dialog"
         style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000; justify-content: center; align-items: center;">
@@ -358,15 +359,9 @@
                 <input type="number" id="timer-seconds" min="0" max="59" value="0"
                     style="width: 60px;">
             </div>
-            <div style="margin-bottom: 15px;">
-                <input type="checkbox" id="auto-countdown-enabled" checked>
-                <label for="auto-countdown-enabled">稳定性测试自动倒计时</label>
-            </div>
-            <div style="display: flex; justify-content: space-between;">
+            <div style="display: flex; justify-content: space-around;">
                 <button onclick="startTimer()"
                     style="padding: 8px 12px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">开始</button>
-                <button onclick="pauseTimer()"
-                    style="padding: 8px 12px; background-color: #ffc107; color: white; border: none; border-radius: 4px; cursor: pointer;">暂停</button>
                 <button onclick="resetTimer()"
                     style="padding: 8px 12px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer;">复位</button>
                 <button onclick="closeTimerDialog()"
@@ -375,7 +370,7 @@
         </div>
     </div>
 
-    <div class="container">
+    <div id="container" class="container">
         <div class="progress-container" onclick="openTimerDialog()" style="cursor: pointer;">
             <span id="current-time" class="stability-time-display"></span>
             <span class="stability-time-display"> · </span>
@@ -390,209 +385,22 @@
         </div>
 
         <!-- 重复性标签页 -->
-        <div id="repeatability" class="tab-content active">
-            <div class="content-container">
-                <div class="input-section">
-                    <div class="input-group" id="repeatability-inputs">
-                        <div class="input-row">
-                            <span id="repeatability-time-text-1">1: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-1"
-                                oninput="recordTimeAndCalculateRepeatability(1)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="repeatability-time-text-2">2: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-2"
-                                oninput="recordTimeAndCalculateRepeatability(2)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="repeatability-time-text-3">3: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-3"
-                                oninput="recordTimeAndCalculateRepeatability(3)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="repeatability-time-text-4">4: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-4"
-                                oninput="recordTimeAndCalculateRepeatability(4)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="repeatability-time-text-5">5: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-5"
-                                oninput="recordTimeAndCalculateRepeatability(5)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="repeatability-time-text-6">6: </span>
-                            <input type="number" placeholder="输入值" id="repeatability-value-6"
-                                oninput="recordTimeAndCalculateRepeatability(6)">
-                            <span class="delete-btn"
-                                onclick="deleteRepeatabilityInput(this)">×</span>
-                        </div>
-                    </div>
-                    <div class="add-input-btn" onclick="addRepeatabilityInput()">
-                        <span>+ 添加输入</span>
-                    </div>
-                </div>
-                <div class="results-section">
-                    <div class="results">
-                        <div class="result-item">极差R: <span id="range">-</span></div>
-                        <div class="result-item">平均值x̄: <span id="mean">-</span></div>
-                        <div class="result-item">方差s<sup>2</sup>: <span id="variance">-</span></div>
-                        <div class="result-item">标准差s: <span id="stdDev">-</span></div>
-                        <div class="result-item">相对标准偏差RSD: <span id="rsd">-</span></div>
-                    </div>
-                    <div class="chart-container">
-                        <canvas id="lineChart"></canvas>
-                    </div>
-                </div>
-            </div>
-        </div>
-
+        <!-- <div id="repeatability" class="tab-content active"> </div> -->
         <!-- 稳定性标签页 -->
-        <div id="stability" class="tab-content">
-            <div class="content-container">
-                <div class="input-section">
-                    <div class="input-group" id="stability-inputs">
-                        <div class="input-row">
-                            <span id="stability-time-text-1">0: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-1"
-                                oninput="recordTimeAndCalculateStability(1)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="stability-time-text-2">10: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-2"
-                                oninput="recordTimeAndCalculateStability(2)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="stability-time-text-3">20: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-3"
-                                oninput="recordTimeAndCalculateStability(3)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="stability-time-text-4">30: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-4"
-                                oninput="recordTimeAndCalculateStability(4)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="stability-time-text-5">40: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-5"
-                                oninput="recordTimeAndCalculateStability(5)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                        <div class="input-row">
-                            <span id="stability-time-text-6">50: </span>
-                            <input type="number" placeholder="输入值" id="stability-value-6"
-                                oninput="recordTimeAndCalculateStability(6)">
-                            <span class="delete-btn" onclick="deleteStabilityInput(this)">×</span>
-                        </div>
-                    </div>
-                    <div class="add-input-btn" onclick="addStabilityInput()">
-                        <span>+ 添加输入</span>
-                    </div>
-                </div>
-                <div class="results-section">
-                    <div class="results">
-                        <div class="result-item">最大值: <span id="stability-max">-</span></div>
-                        <div class="result-item">最小值: <span id="stability-min">-</span></div>
-                        <div class="result-item">平均值: <span id="stability-mean">-</span></div>
-                        <div class="result-item">极值差: <span id="stability-range">-</span></div>
-                    </div>
-                    <div class="chart-container">
-                        <canvas id="stabilityChart"></canvas>
-                    </div>
-                </div>
-            </div>
-        </div>
-
-
+        <!-- <div id="stability" class="tab-content"> </div> -->
         <!-- 示值误差标签页 -->
-        <div id="error" class="tab-content">
-            <div class="content-container">
-                <div class="input-section" style="width: 100%;">
-                    <div class="error-container" id="error-inputs">
-                    </div>
-                    <div class="add-input-btn" onclick="addErrorPoint()">
-                        <span>+ 添加测试点</span>
-                    </div>
-                    <div class="uncertainty-results"
-                        style="margin-top: 1rem; padding: 1rem; border: 1px solid #eee; border-radius: 4px;">
-                        <h3 style="margin-top: 0;">示值误差校准结果不确定度</h3>
-                        <div class="results">
-                            <div class="result-item">A类不确定度(uA): <span id="uncertainty-a">-</span>
-                            </div>
-                            <div class="result-item">B类不确定度(uB): <span id="uncertainty-b">-</span>
-                            </div>
-                            <div class="result-item">合成不确定度(uc): <span id="uncertainty-c">-</span>
-                            </div>
-                            <div class="result-item">扩展不确定度(U): <span id="uncertainty-u">-</span>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="chart-container">
-                        <canvas id="errorChart"></canvas>
-                    </div>
-                </div>
-            </div>
-        </div>
-
-        <!-- 导出/导入标签页 -->
-        <div id="data" class="tab-content">
-            <div class="content-container" style="flex-direction: column;">
-                <div style="margin-bottom: 1.5rem;">
-                    <div style="display: flex; align-items: center; margin-bottom: 1rem;">
-                        <label for="project-title"
-                            style="margin-right: 1rem; font-weight: bold;">工程标题:</label>
-                        <input type="text" id="project-title" placeholder="请输入工程标题"
-                            style="flex: 1; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px;"
-                            oninput="updateProjectTitle()">
-                    </div>
-                    <div class="button-container">
-                        <button class="export-btn" onclick="exportData()">导出数据</button>
-                        <button class="import-btn"
-                            onclick="document.getElementById('importFile').click()">导入数据</button>
-                        <button class="export-btn" style="background-color: #dc3545;"
-                            onclick="newProject()">新建工程</button>
-                        <input type="file" id="importFile" accept=".json,.txt"
-                            onchange="importData()" style="display:none">
-                    </div>
-                </div>
-
-                <!-- 自定义参数部分 -->
-                <div
-                    style="margin-top: 1.5rem; border: 1px solid #eee; border-radius: 4px; padding: 1rem;">
-                    <h3 style="margin-top: 0; margin-bottom: 1rem;">自定义参数</h3>
-                    <div id="custom-params-container"
-                        style="display: flex; flex-direction: column; gap: 0.5rem; max-height: 300px; overflow-y: auto;">
-                        <!-- 参数行将在这里动态添加 -->
-                    </div>
-                    <div class="add-input-btn" onclick="addCustomParam()" style="margin-top: 1rem;">
-                        <span>+ 添加参数</span>
-                    </div>
-                </div>
-            </div>
-            <div style="text-align: center; margin-top: 1rem; color: #666; font-size: 0.8rem;">Ver
-                1.0.03.250501a2</div>
-        </div>
+        <!-- <div id="error" class="tab-content"> </div> -->
+        <!-- 数据标签页 -->
+        <!-- <div id="data" class="tab-content"> </div> -->
+    </div>
 
-        <script src="./chart.js"></script>
-        <script src="./utils.js"></script>
-        <script src="./pageData.js"></script>
-        <script src="./pageRepeatability.js"></script>
-        <script src="./pageStability.js"></script>
-        <script src="./pageError.js"></script>
-        <script src="./common.js"></script>
+    <script src="./chart.js"></script>
+    <script src="./utils.js"></script>
+    <script src="./pageRepeatability.js"></script>
+    <script src="./pageStability.js"></script>
+    <script src="./pageError.js"></script>
+    <script src="./pageData.js"></script>
+    <script src="./common.js"></script>
 </body>
 
 </html>

+ 245 - 450
LabStatistics/pageData.js

@@ -1,38 +1,155 @@
 // 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;
-            }
-        });
-    }
+function initializeDataPage() {
+    // 生成数据标签页内容
+    generateDataTabContent();
 
-    // 保存到localStorage
-    const data = {
-        inputs: inputs,
-        results: results
+    // 加载项目标题和自定义参数数据
+    loadProjectTitle();
+    loadCustomParamsData();
+}
+
+// 生成数据标签页内容
+function generateDataTabContent() {
+    console.log('generateDataTabContent');
+
+    const dataTab = document.createElement('div');
+    dataTab.id = 'data';
+    dataTab.className = 'tab-content';
+
+    // 创建内容容器
+    const contentContainer = document.createElement('div');
+    contentContainer.className = 'content-container';
+    contentContainer.style.flexDirection = 'column';
+
+    // 创建项目标题和按钮部分
+    const titleSection = document.createElement('div');
+    titleSection.style.marginBottom = '1.5rem';
+
+    // 创建标题输入部分
+    const titleRow = document.createElement('div');
+    titleRow.style.display = 'flex';
+    titleRow.style.alignItems = 'center';
+    titleRow.style.marginBottom = '1rem';
+
+    const titleLabel = document.createElement('label');
+    titleLabel.htmlFor = 'project-title';
+    titleLabel.style.marginRight = '1rem';
+    titleLabel.style.fontWeight = 'bold';
+    titleLabel.textContent = '工程标题:';
+
+    const titleInput = document.createElement('input');
+    titleInput.type = 'text';
+    titleInput.id = 'project-title';
+    titleInput.placeholder = '请输入工程标题';
+    titleInput.style.flex = '1';
+    titleInput.style.padding = '0.5rem';
+    titleInput.style.border = '1px solid #ccc';
+    titleInput.style.borderRadius = '4px';
+    titleInput.oninput = updateProjectTitle;
+
+    titleRow.appendChild(titleLabel);
+    titleRow.appendChild(titleInput);
+
+    // 创建按钮容器
+    const buttonContainer = document.createElement('div');
+    buttonContainer.className = 'button-container';
+
+    // 创建导出按钮
+    const exportBtn = document.createElement('button');
+    exportBtn.className = 'export-btn';
+    exportBtn.textContent = '导出数据';
+    exportBtn.onclick = exportData;
+
+    // 创建导入按钮
+    const importBtn = document.createElement('button');
+    importBtn.className = 'import-btn';
+    importBtn.textContent = '导入数据';
+    importBtn.onclick = function () {
+        document.getElementById('importFile').click();
     };
-    console.log(data);
-    
 
-    setLocalStorage(cookieName, JSON.stringify(data));
+    // 创建新建工程按钮
+    const newProjectBtn = document.createElement('button');
+    newProjectBtn.className = 'export-btn';
+    newProjectBtn.style.backgroundColor = '#dc3545';
+    newProjectBtn.textContent = '新建工程';
+    newProjectBtn.onclick = newProject;
+
+    // 创建文件输入(隐藏)
+    const importFile = document.createElement('input');
+    importFile.type = 'file';
+    importFile.id = 'importFile';
+    importFile.accept = '.json,.txt';
+    importFile.style.display = 'none';
+    importFile.onchange = importData;
+
+    // 添加按钮到容器
+    buttonContainer.appendChild(exportBtn);
+    buttonContainer.appendChild(importBtn);
+    buttonContainer.appendChild(newProjectBtn);
+    buttonContainer.appendChild(importFile);
+
+    // 组装标题部分
+    titleSection.appendChild(titleRow);
+    titleSection.appendChild(buttonContainer);
+
+    // 创建自定义参数部分
+    const customParamsSection = document.createElement('div');
+    customParamsSection.style.marginTop = '1.5rem';
+    customParamsSection.style.border = '1px solid #eee';
+    customParamsSection.style.borderRadius = '4px';
+    customParamsSection.style.padding = '1rem';
+
+    // 添加标题
+    const customParamsTitle = document.createElement('h3');
+    customParamsTitle.style.marginTop = '0';
+    customParamsTitle.style.marginBottom = '1rem';
+    customParamsTitle.textContent = '自定义参数';
+
+    // 创建参数容器
+    const customParamsContainer = document.createElement('div');
+    customParamsContainer.id = 'custom-params-container';
+    customParamsContainer.style.display = 'flex';
+    customParamsContainer.style.flexDirection = 'column';
+    customParamsContainer.style.gap = '0.5rem';
+    customParamsContainer.style.maxHeight = '300px';
+    customParamsContainer.style.overflowY = 'auto';
+
+    // 添加按钮
+    const addParamBtn = document.createElement('div');
+    addParamBtn.className = 'add-input-btn';
+    addParamBtn.style.marginTop = '1rem';
+    addParamBtn.onclick = addCustomParam;
+    const addParamSpan = document.createElement('span');
+    addParamSpan.textContent = '+ 添加参数';
+    addParamBtn.appendChild(addParamSpan);
+
+    // 组装自定义参数部分
+    customParamsSection.appendChild(customParamsTitle);
+    customParamsSection.appendChild(customParamsContainer);
+    customParamsSection.appendChild(addParamBtn);
+
+    // 组装整个内容
+    contentContainer.appendChild(titleSection);
+    contentContainer.appendChild(customParamsSection);
+    dataTab.appendChild(contentContainer);
+    if (document.getElementById('container')) {
+        console.log('appendChild dataTab');
+        document.getElementById('container').appendChild(dataTab);
+    }
+
+    // 添加版本信息
+    const versionInfo = document.createElement('div');
+    versionInfo.style.textAlign = 'center';
+    versionInfo.style.marginTop = '1rem';
+    versionInfo.style.color = '#666';
+    versionInfo.style.fontSize = '0.8rem';
+    versionInfo.textContent = 'Ver 1.1.02.250628';
+    dataTab.appendChild(versionInfo);
 }
 
+
 // 添加自定义参数
 function addCustomParam() {
     const container = document.getElementById('custom-params-container');
@@ -77,6 +194,17 @@ function addCustomParam() {
     container.appendChild(paramRow);
 }
 
+// 清除自定义参数数据
+function clearCustomParamsData() {
+    // 清除自定义参数
+    const customParamsContainer = document.getElementById('custom-params-container');
+    while (customParamsContainer.firstChild) {
+        customParamsContainer.removeChild(customParamsContainer.firstChild);
+    }
+
+    saveCustomParamsData();
+}
+
 // 保存自定义参数数据
 function saveCustomParamsData() {
     const customParams = Array.from(document.querySelectorAll('#custom-params-container .custom-param-row')).map(row => ({
@@ -84,12 +212,14 @@ function saveCustomParamsData() {
         value: row.querySelector('.param-value').value
     }));
 
-    setLocalStorage('customParamsData', JSON.stringify(customParams), 365);
+    setLocalStorage('customParamsData', JSON.stringify(customParams));
 }
 
-// 从localStorage加载自定义参数
-function loadCustomParamsFromLocalStorage() {
+// 从localStorage加载自定义参数数据
+function loadCustomParamsData() {
     const savedParams = getLocalStorage('customParamsData');
+    console.log('customParamsData', savedParams);
+
     if (savedParams) {
         try {
             const params = JSON.parse(savedParams);
@@ -149,442 +279,107 @@ function loadCustomParamsFromLocalStorage() {
     }
 }
 
-// 从localStorage加载数据
-function loadDataFromLocalStorage() {
-    // 加载重复性数据
-    const savedRepeatabilityData = getLocalStorage('repeatabilityData');
-    if (savedRepeatabilityData) {
+// 从localStorage加载项目标题
+function loadProjectTitle() {
+    const savedTitle = getLocalStorage('projectTitle');
+    if (savedTitle) {
         try {
-            const data = JSON.parse(savedRepeatabilityData);
+            const titleInput = document.getElementById('project-title');
+            console.log('projectTitle', savedTitle);
 
-            // 清除现有的额外输入框(保留前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();
+            if (titleInput) {
+                titleInput.value = savedTitle;
             }
         } catch (e) {
-            console.error('加载重复性数据失败:', 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;
-                    }
+function newProject() {
+    if (confirm('确定要新建工程吗?这将清除所有当前数据!')) {
+        // 清除整个localStorage
+        localStorage.clear();
+        // 刷新页面
+        location.reload();
+    }
+}
 
-                    // 设置时间
-                    if (time) {
-                        const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
-                        if (timeSpan) {
-                            timeSpan.textContent = `${time}: `;
-                            stabilityTimes[index] = time;
-                        }
-                    }
-                });
 
-                // 计算结果
-                calculateStability();
-            }
+// 导出数据
+function exportData() {
+    // 获取所有localStorage数据
+    const data = {};
+    for (let i = 0; i < localStorage.length; i++) {
+        const key = localStorage.key(i);
+        try {
+            // 尝试解析JSON数据
+            const value = localStorage.getItem(key);
+            data[key] = JSON.parse(value);
         } catch (e) {
-            console.error('加载稳定性数据失败:', e);
+            // 如果不是JSON格式则直接存储原始值
+            data[key] = localStorage.getItem(key);
         }
     }
 
-    // 加载示值误差数据
-    const savedErrorData = getLocalStorage('errorData');
-    if (savedErrorData) {
-        try {
-            const errorData = JSON.parse(savedErrorData);
+    const projectTitle = document.getElementById('project-title').value;
+    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;
+    const fileName = `_LabStatistics_${new Date().toISOString().split('.')[0]}.json`;
+    a.download = `${projectTitle}${fileName.replace(/-/g, '_')}`;
+    document.body.appendChild(a);
+    a.click();
+    document.body.removeChild(a);
+    URL.revokeObjectURL(url);
+}
 
-            // 清除现有的测试点
-            const errorContainer = document.getElementById('error-inputs');
-            while (errorContainer.firstChild) {
-                errorContainer.removeChild(errorContainer.firstChild);
-            }
+// 导入数据
+function importData() {
+    const fileInput = document.getElementById('importFile');
+    const file = fileInput.files[0];
 
-            // 添加保存的测试点
-            errorData.forEach(item => {
-                const pointContainer = createErrorPointContainer(item.point);
-                errorContainer.appendChild(pointContainer);
+    if (!file) {
+        alert('请选择要导入的文件!');
+        return;
+    }
 
-                // 填充输入值
-                const inputs = pointContainer.querySelectorAll('.error-input');
-                item.inputs.forEach((value, index) => {
-                    if (index < inputs.length) {
-                        inputs[index].value = value;
+    const reader = new FileReader();
+    reader.onload = function (e) {
+        try {
+            const data = JSON.parse(e.target.result);
+
+            // 清除现有数据
+            localStorage.clear();
+
+            // 导入所有数据到localStorage
+            // 遍历数据对象的所有键
+            for (const key in data) {
+                try {
+                    // 检查数据类型并相应处理
+                    if (typeof data[key] === 'object' && data[key] !== null) {
+                        // 对象类型数据转换为JSON字符串存储
+                        localStorage.setItem(key, JSON.stringify(data[key]));
+                    } else {
+                        // 其他类型直接存储
+                        localStorage.setItem(key, data[key]);
                     }
-                });
-            });
+                } catch (error) {
+                    console.error(`导入键 ${key} 时出错:`, error);
+                }
+            }
 
-            // 计算结果
-            calculateError();
-            updateErrorPoints();
-        } catch (e) {
-            console.error('加载示值误差数据失败:', e);
+            // 导入完成后重新加载页面以更新显示
+            location.reload();
+
+            alert('数据导入成功!');
+        } catch (error) {
+            alert('导入失败!请检查文件格式。');
+            console.error('导入错误:', error);
         }
-    }
+    };
+    reader.readAsText(file);
 }
-
-// 导出数据
-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();
-}

+ 209 - 36
LabStatistics/pageError.js

@@ -4,35 +4,109 @@
 let errorCtx = null;
 let errorChartInstance = null;
 
-// 初始化示值误差图表
-function initErrorChart() {
-    errorCtx = document.getElementById('errorChart').getContext('2d');
+
+function initializeErrorPage() {
+    // 生成示值误差标签页内容
+    generateErrorTabContent();
+
+    // 加载示值误差数据
+    loadErrorData();
 }
 
-// 在页面加载完成后初始化图表
-document.addEventListener('DOMContentLoaded', function() {
-    initErrorChart();
-});
+// 生成示值误差标签页内容
+function generateErrorTabContent() {
+    console.log('generateErrorTabContent');
 
-// 示值误差测试函数
-function updateErrorPoint(input) {
-    const pointContainer = input.closest('.error-point-container');
-    const originalValue = input.getAttribute('data-original');
-    const newValue = input.value;
+    const errorTab = document.createElement('div');
+    errorTab.id = 'error';
+    errorTab.className = 'tab-content';
 
-    // 如果测试点值发生变化,更新所有相关元素的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);
+    // 创建内容容器
+    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} <span id="${item.id}">-</span>`;
+        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);
     }
 
-    calculateError();
+    // 添加初始示值误差测试点(因为已移除所有默认测试点)
+    if (document.querySelectorAll('#error-inputs .error-point-container').length === 0) {
+        addErrorPoint();
+    }
 }
 
+// 示值误差测试点容器创建
 function createErrorPointContainer(pointValue = '') {
     const container = document.createElement('div');
     container.className = 'error-point-container';
@@ -55,14 +129,14 @@ function createErrorPointContainer(pointValue = '') {
     const pointInput = document.createElement('input');
     pointInput.type = 'number';
     pointInput.className = 'error-point-input';
-    pointInput.placeholder = '输入测试点';
+    pointInput.placeholder = '测试点';
     pointInput.value = pointValue;
     pointInput.setAttribute('data-original', pointValue);
     pointInput.style.flex = '1';
     pointInput.style.marginRight = '0.5rem';
     pointInput.oninput = function () {
         updateErrorPoint(this);
-        // 保存示值误差数据到localStorage
+        // 保存示值误差数据
         saveErrorData();
     };
 
@@ -71,12 +145,8 @@ function createErrorPointContainer(pointValue = '') {
     deleteBtn.innerHTML = '×';
     deleteBtn.onclick = function () {
         container.parentNode.removeChild(container);
-        // 如果删除后没有测试点,添加一个新的空白测试点
-        if (document.querySelectorAll('.error-point-container').length === 0) {
-            addErrorPoint();
-        }
         calculateError();
-        // 保存误差数据到localStorage
+        // 保存误差数据
         saveErrorData();
     };
 
@@ -97,7 +167,7 @@ function createErrorPointContainer(pointValue = '') {
         const input = document.createElement('input');
         input.type = 'number';
         input.className = 'error-input';
-        input.placeholder = `输入值 ${i + 1}`;
+        input.placeholder = `输入值`;
         input.setAttribute('data-point', pointValue);
         input.style.flex = '1';
         input.style.minWidth = '100px';
@@ -112,7 +182,7 @@ function createErrorPointContainer(pointValue = '') {
     // 结果显示部分
     const resultsRow = document.createElement('div');
     resultsRow.style.display = 'flex';
-    resultsRow.style.gap = '1rem';
+    resultsRow.style.flexDirection = 'column';
 
     const avgContainer = document.createElement('div');
     avgContainer.style.flex = '1';
@@ -148,13 +218,50 @@ function createErrorPointContainer(pointValue = '') {
     return container;
 }
 
+// 示值误差测试点添加
 function addErrorPoint() {
     const container = document.getElementById('error-inputs');
     const pointContainer = createErrorPointContainer();
     container.appendChild(pointContainer);
-    updateErrorPoints();
 }
 
+// 示值误差测试点更新
+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 = [];
@@ -192,15 +299,20 @@ function calculateError() {
         }
     });
 
+    // 计算不确定度
+    calculateUncertainty(errorData);
+
+    // 保存误差数据
+    saveErrorData();
+
     // 更新图表
     updateErrorChart(errorData);
 
-    // 计算不确定度
-    calculateUncertainty(errorData);
 
     return errorData;
 }
 
+// 示值误差不确定度计算
 function calculateUncertainty(errorData) {
     if (errorData.length === 0) {
         // 清空不确定度显示
@@ -249,8 +361,10 @@ function calculateUncertainty(errorData) {
     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) {
@@ -272,8 +386,10 @@ function updateErrorChart(data) {
     errorChartInstance = createOrUpdateChart(errorChartInstance, errorCtx, labels, errorValues, '示值误差');
 }
 
+// 示值误差数据保存
 function saveErrorData() {
-    const errorData = Array.from(document.querySelectorAll('.error-point-container')).map(container => {
+    // 收集输入数据
+    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');
@@ -281,11 +397,68 @@ function saveErrorData() {
 
         return {
             point: pointInput.value,
-            inputs: Array.from(inputs).map(input => input.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
+    };
 
-    setLocalStorage('errorData', JSON.stringify(errorData), 365);
+    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);
+        }
+    }
 }

+ 205 - 38
LabStatistics/pageRepeatability.js

@@ -4,46 +4,118 @@
 let repeatabilityTimes = [];
 
 // 图表相关变量
-let ctx = null;
-let chartInstance = null;
+let repeatabilityCtx = null;
+let repeatabilityChartInstance = null;
 
-// 初始化重复性图表
-function initRepeatabilityChart() {
-    ctx = document.getElementById('lineChart').getContext('2d');
-}
 
-// 在页面加载完成后初始化图表
-document.addEventListener('DOMContentLoaded', function() {
-    initRepeatabilityChart();
-});
+function initializeRepeatabilityPage() {
+    // 生成重复性标签页内容
+    generateRepeatabilityTabContent();
 
-// 更新重复性输入框的占位符
-function updateRepeatabilityInputPlaceholders() {
-    document.querySelectorAll('#repeatability-inputs input[type="number"]').forEach((input, index) => {
-        input.placeholder = `测量值 ${index + 1}`;
-    });
+    // 加载重复性数据
+    loadRepeatabilityData();
 }
 
-// 更新重复性输入框的删除按钮
-function updateRepeatabilityDeleteButtons() {
-    const repeatabilityInputs = document.querySelectorAll('#repeatability-inputs .input-row');
-    repeatabilityInputs.forEach((row, index) => {
-        const deleteBtn = row.querySelector('.delete-btn');
-        if (deleteBtn) {
-            deleteBtn.style.display = index < 6 ? 'none' : 'inline';
-        }
+// 生成重复性标签页内容
+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: '方差s<sup>2</sup>:', 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} <span id="${item.id}">-</span>`;
+        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);
-    updateRepeatabilityInputPlaceholders();
 }
 
+// 删除重复性输入行
 function deleteRepeatabilityInput(btn) {
     const row = btn.parentNode;
     const container = row.parentNode;
@@ -68,24 +140,55 @@ function deleteRepeatabilityInput(btn) {
 
         // 如果有保存的时间,则显示时间,否则显示索引
         if (repeatabilityTimes[i]) {
-            timeSpan.textContent = `${repeatabilityTimes[i]}: `;
+            timeSpan.textContent = `${parseUnixTime(repeatabilityTimes[i])}: `;
         } else {
             timeSpan.textContent = `${newIndex}: `;
         }
 
         // 更新输入事件
-        input.oninput = function() { recordTimeAndCalculateRepeatability(newIndex); };
+        input.oninput = function () { recordTimeAndCalculateRepeatability(newIndex); };
     });
 
-    updateRepeatabilityInputPlaceholders();
+    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"]');
 
@@ -95,7 +198,6 @@ function calculateRepeatability() {
         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);
@@ -108,6 +210,9 @@ function calculateRepeatability() {
         updateResultDisplay('stdDev', stdDev);
         updateResultDisplay('rsd', rsd);
 
+        // 保存数据
+        saveRepeatabilityData();
+
         // 更新图表
         updateRepeatabilityChart(values);
     } else {
@@ -117,28 +222,90 @@ function calculateRepeatability() {
         });
 
         // 清空图表
-        if (chartInstance) {
-            chartInstance.data.labels = [];
-            chartInstance.data.datasets[0].data = [];
-            chartInstance.update();
+        if (repeatabilityChartInstance) {
+            repeatabilityChartInstance.data.labels = [];
+            repeatabilityChartInstance.data.datasets[0].data = [];
+            repeatabilityChartInstance.update();
         }
     }
 }
 
+// 重复性图表更新
 function updateRepeatabilityChart(values) {
-    // 创建标签(1, 2, 3, ...)
-    const labels = Array.from({length: values.length}, (_, i) => i + 1);
-
+    // 获取时间标签
+    const timeLabels = [];
+    repeatabilityTimes.forEach((time) => {
+        // 使用已保存的时间戳生成标签
+        if (time > 9999) {
+            timeLabels.push(parseUnixTime(time));
+        }
+    });
     // 使用通用图表创建函数
-    chartInstance = createOrUpdateChart(chartInstance, ctx, labels, values, '重复性');
+    repeatabilityChartInstance = createOrUpdateChart(repeatabilityChartInstance, repeatabilityCtx, timeLabels, values, '重复性');
 }
 
+// 重复性数据保存
 function saveRepeatabilityData() {
-    collectAndSaveData('#repeatability-inputs input[type="number"]', 'repeatability-time-text', {
+    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);
+        }
+    }
 }

+ 299 - 0
LabStatistics/pageStability.js

@@ -0,0 +1,299 @@
+// stability.js - 稳定性测试相关功能
+
+// 稳定性测试时间数组
+let stabilityTimes = [];
+
+// 图表相关变量
+let stabilityCtx = null;
+let stabilityChartInstance = null;
+
+
+function initializeStabilityPage() {
+    // 生成稳定性标签页内容
+    generateStabilityTabContent();
+
+    // 加载稳定性数据
+    loadStabilityData();
+}
+
+// 生成稳定性标签页内容
+function generateStabilityTabContent() {
+    console.log('generateStabilityTabContent');
+
+    const stabilityTab = document.createElement('div');
+    stabilityTab.id = 'stability';
+    stabilityTab.className = 'tab-content';
+
+    // 创建内容容器
+    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 = 'stability-inputs';
+
+    // 添加默认的6个输入行,设置默认时间间隔为10分钟
+    const defaultTimes = [0, 10, 20, 30, 40, 50];
+    for (let i = 1; i <= 6; i++) {
+        const row = createInputRow(i, 'stability', recordTimeAndCalculateStability, deleteStabilityInput);
+        inputGroup.appendChild(row);
+    }
+
+    // 添加按钮
+    const addInputBtn = document.createElement('div');
+    addInputBtn.className = 'add-input-btn';
+    addInputBtn.onclick = addStabilityInput;
+    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: '最大值:', id: 'stability-max' },
+        { label: '最小值:', id: 'stability-min' },
+        { label: '平均值:', id: 'stability-mean' },
+        { label: '极值差:', id: 'stability-range' }
+    ];
+
+    resultItems.forEach(item => {
+        const resultItem = document.createElement('div');
+        resultItem.className = 'result-item';
+        resultItem.innerHTML = `${item.label} <span id="${item.id}">-</span>`;
+        results.appendChild(resultItem);
+    });
+
+    // 创建图表容器
+    const chartContainer = document.createElement('div');
+    chartContainer.className = 'chart-container';
+    const canvas = document.createElement('canvas');
+    canvas.id = 'stabilityChart';
+    chartContainer.appendChild(canvas);
+    stabilityCtx = canvas.getContext('2d');
+
+    // 组装结果部分
+    resultsSection.appendChild(results);
+    resultsSection.appendChild(chartContainer);
+
+    // 组装整个内容
+    contentContainer.appendChild(inputSection);
+    contentContainer.appendChild(resultsSection);
+    stabilityTab.appendChild(contentContainer);
+    if (document.getElementById('container')) {
+        console.log('appendChild stabilityTab');
+        document.getElementById('container').appendChild(stabilityTab);
+    }
+}
+
+// 添加稳定性输入行
+function addStabilityInput() {
+    const container = document.getElementById('stability-inputs');
+    const newIndex = container.children.length + 1;
+    const row = createInputRow(newIndex, 'stability', recordTimeAndCalculateStability, deleteStabilityInput);
+    container.appendChild(row);
+}
+
+// 删除稳定性输入行
+function deleteStabilityInput(btn) {
+    const row = btn.parentNode;
+    const container = row.parentNode;
+    const index = Array.from(container.children).indexOf(row);
+
+    // 移除对应的时间记录
+    stabilityTimes.splice(index, 1);
+
+    // 移除输入行
+    container.removeChild(row);
+
+    // 更新剩余输入行的索引
+    const rows = container.querySelectorAll('.input-row');
+    rows.forEach((row, i) => {
+        const timeSpan = row.querySelector(`span[id^="stability-time-text-"]`);
+        const input = row.querySelector(`input[id^="stability-value-"]`);
+
+        // 更新ID
+        const newIndex = i + 1;
+        timeSpan.id = `stability-time-text-${newIndex}`;
+        input.id = `stability-value-${newIndex}`;
+
+        // 如果有保存的时间,则显示时间,否则显示索引
+        if (stabilityTimes[i]) {
+            timeSpan.textContent = `${parseUnixTime(stabilityTimes[i])}: `;
+        } else {
+            timeSpan.textContent = `${newIndex}: `;
+        }
+
+        // 更新输入事件
+        input.oninput = function() { recordTimeAndCalculateStability(newIndex); };
+    });
+
+    document.querySelectorAll('#stability-inputs input[type="number"]').forEach((input) => {
+        input.placeholder = `输入值`;
+    });
+    calculateStability();
+    saveStabilityData();
+}
+
+// 清除稳定性数据
+function clearStabilityData() {
+    // 清除稳定性数据
+    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 = '';
+    });
+    stabilityTimes = [];
+
+    // 重置稳定性时间标签
+    for (let i = 1; i <= 6; i++) {
+        const timeSpan = document.getElementById(`stability-time-text-${i}`);
+        if (timeSpan) {
+            timeSpan.textContent = `${i}: `;
+        }
+    }
+
+    // 重置图表
+    if (stabilityChartInstance) {
+        stabilityChartInstance.destroy();
+        stabilityChartInstance = null;
+    }
+}
+
+// 记录稳定性时间并计算
+function recordTimeAndCalculateStability(index) {
+    recordTimeAndCalculate('stability', index, stabilityTimes, calculateStability);
+}
+
+// 稳定性计算
+function calculateStability() {
+    const values = getNumericInputs('#stability-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;
+
+        // 更新结果显示
+        updateResultDisplay('stability-max', max);
+        updateResultDisplay('stability-min', min);
+        updateResultDisplay('stability-mean', mean);
+        updateResultDisplay('stability-range', range);
+
+        // 保存数据
+        saveStabilityData();
+
+        // 更新图表
+        updateStabilityChart(values);
+    } else {
+        // 清空结果显示
+        document.querySelectorAll('#stability .result-item span').forEach(span => {
+            span.innerText = '-';
+        });
+
+        // 清空图表
+        if (stabilityChartInstance) {
+            stabilityChartInstance.data.labels = [];
+            stabilityChartInstance.data.datasets[0].data = [];
+            stabilityChartInstance.update();
+        }
+    }
+}
+
+// 稳定性图表更新
+function updateStabilityChart(values) {
+    // 获取时间标签
+    const timeLabels = [];
+    stabilityTimes.forEach((time) => {
+        // 使用已保存的时间戳生成标签
+        if (time > 9999) {
+            timeLabels.push(parseUnixTime(time));
+        }
+    });
+
+    // 使用通用图表创建函数
+    stabilityChartInstance = createOrUpdateChart(stabilityChartInstance, stabilityCtx, timeLabels, values, '稳定性');
+}
+
+// 稳定性数据保存
+function saveStabilityData() {
+    collectAndSaveData('#stability-inputs input[type="number"]', stabilityTimes, {
+        max: 'stability-max',
+        min: 'stability-min',
+        mean: 'stability-mean',
+        range: 'stability-range'
+    }, 'stabilityData');
+}
+
+// 稳定性数据加载
+function loadStabilityData() {
+    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 time = typeof item === 'object' ? item.time : '';
+                    const value = typeof item === 'object' ? item.value : '';
+
+                    // 如果索引超出现有输入框数量,添加新的输入框
+                    if (index >= 6) {
+                        addStabilityInput();
+                    }
+
+                    // 设置时间
+                    if (time) {
+                        const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
+                        if (timeSpan) {
+                            timeSpan.textContent = parseUnixTime(time) + ': ';
+                            stabilityTimes[index] = time;
+                        }
+                    }
+
+                    // 设置值
+                    const input = document.querySelector(`#stability-inputs .input-row:nth-child(${index + 1}) input`);
+                    if (input) {
+                        input.value = value;
+                    }
+                });
+
+                // 计算结果
+                calculateStability();
+            }
+        } catch (e) {
+            console.error('加载稳定性数据失败:', e);
+        }
+    }
+}

+ 0 - 299
LabStatistics/psgeStability.js

@@ -1,299 +0,0 @@
-// stability.js - 稳定性测试相关功能
-
-// 稳定性测试时间数组
-let stabilityTimes = [];
-
-// 图表相关变量
-let stabilityCtx = null;
-let stabilityChartInstance = null;
-
-// 计时器相关变量
-let timerInterval = null;
-let timerTotalSeconds = 600; // 默认10分钟
-let timerCurrentSeconds = 600;
-let timerPaused = false;
-
-// 初始化稳定性图表
-function initStabilityChart() {
-    stabilityCtx = document.getElementById('stabilityChart').getContext('2d');
-}
-
-// 在页面加载完成后初始化图表
-document.addEventListener('DOMContentLoaded', function() {
-    initStabilityChart();
-});
-
-// 更新稳定性输入框的占位符
-function updateStabilityInputPlaceholders() {
-    document.querySelectorAll('#stability-inputs input[type="number"]').forEach((input, index) => {
-        input.placeholder = `测量值 ${index + 1}`;
-    });
-}
-
-// 更新稳定性输入框的删除按钮
-function updateStabilityDeleteButtons() {
-    const stabilityInputs = document.querySelectorAll('#stability-inputs .input-row');
-    stabilityInputs.forEach((row, index) => {
-        const deleteBtn = row.querySelector('.delete-btn');
-        if (deleteBtn) {
-            deleteBtn.style.display = index < 6 ? 'none' : 'inline';
-        }
-    });
-}
-
-// 稳定性测试函数
-function addStabilityInput() {
-    const container = document.getElementById('stability-inputs');
-    const newIndex = container.children.length + 1;
-    const row = createInputRow(newIndex, 'stability', recordTimeAndCalculateStability, deleteStabilityInput);
-    
-    // 设置默认时间为上一个时间+10分钟
-    const lastTimeSpan = container.querySelector(`.input-row:last-child span[id^="stability-time-text-"]`);
-    let lastTime = 0;
-    if (lastTimeSpan) {
-        const lastTimeText = lastTimeSpan.textContent.trim();
-        lastTime = parseInt(lastTimeText) || 0;
-    }
-    const newTime = lastTime + 10;
-    row.querySelector(`span[id^="stability-time-text-"]`).textContent = `${newTime}: `;
-    
-    container.appendChild(row);
-    updateStabilityInputs();
-}
-
-function deleteStabilityInput(btn) {
-    const row = btn.parentNode;
-    const container = row.parentNode;
-    const index = Array.from(container.children).indexOf(row);
-    
-    // 移除对应的时间记录
-    stabilityTimes.splice(index, 1);
-    
-    // 移除输入行
-    container.removeChild(row);
-    
-    // 更新剩余输入行的索引
-    const rows = container.querySelectorAll('.input-row');
-    rows.forEach((row, i) => {
-        const timeSpan = row.querySelector(`span[id^="stability-time-text-"]`);
-        const input = row.querySelector(`input[id^="stability-value-"]`);
-        
-        // 更新ID
-        const newIndex = i + 1;
-        timeSpan.id = `stability-time-text-${newIndex}`;
-        input.id = `stability-value-${newIndex}`;
-        
-        // 更新输入事件
-        input.oninput = function() { recordTimeAndCalculateStability(newIndex); };
-    });
-    
-    updateStabilityInputs();
-    calculateStability();
-    saveStabilityData();
-}
-
-function recordTimeAndCalculateStability(index) {
-    recordTimeAndCalculate('stability', index, stabilityTimes, calculateStability);
-}
-
-function calculateStability() {
-    const values = getNumericInputs('#stability-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;
-        
-        // 更新结果显示
-        updateResultDisplay('stability-max', max);
-        updateResultDisplay('stability-min', min);
-        updateResultDisplay('stability-mean', mean);
-        updateResultDisplay('stability-range', range);
-        
-        // 更新图表
-        updateStabilityChart(values);
-    } else {
-        // 清空结果显示
-        document.querySelectorAll('#stability .result-item span').forEach(span => {
-            span.innerText = '-';
-        });
-        
-        // 清空图表
-        if (stabilityChartInstance) {
-            stabilityChartInstance.data.labels = [];
-            stabilityChartInstance.data.datasets[0].data = [];
-            stabilityChartInstance.update();
-        }
-    }
-}
-
-function updateStabilityChart(values) {
-    // 获取时间标签
-    const timeLabels = [];
-    document.querySelectorAll('#stability-inputs .input-row').forEach((row, index) => {
-        const timeSpan = row.querySelector(`span[id^="stability-time-text-"]`);
-        const timeText = timeSpan.textContent.trim();
-        const time = timeText.replace(':', '');
-        timeLabels.push(time);
-    });
-    
-    // 使用通用图表创建函数
-    stabilityChartInstance = createOrUpdateChart(stabilityChartInstance, stabilityCtx, timeLabels, values, '稳定性');
-}
-
-function saveStabilityData() {
-    collectAndSaveData('#stability-inputs input[type="number"]', 'stability-time-text', {
-        max: 'stability-max',
-        min: 'stability-min',
-        mean: 'stability-mean',
-        range: 'stability-range'
-    }, 'stabilityData');
-}
-
-// 加载计时器设置
-function loadTimerSettings() {
-    const savedMinutes = getLocalStorage('timerMinutes');
-    const savedSeconds = getLocalStorage('timerSeconds');
-    const savedAutoCountdown = getLocalStorage('autoCountdownEnabled');
-
-    if (savedMinutes !== null && savedSeconds !== null) {
-        const minutes = parseInt(savedMinutes);
-        const seconds = parseInt(savedSeconds);
-        document.getElementById('timer-minutes').value = minutes;
-        document.getElementById('timer-seconds').value = seconds;
-        timerTotalSeconds = minutes * 60 + seconds;
-        timerCurrentSeconds = timerTotalSeconds;
-        updateTimerDisplay();
-    }
-
-    // 加载自动倒计时设置
-    if (savedAutoCountdown !== null) {
-        document.getElementById('auto-countdown-enabled').checked = (savedAutoCountdown === '1');
-    }
-}
-
-// 保存计时器设置
-function saveTimerSettings(minutes, seconds) {
-    setLocalStorage('timerMinutes', minutes, 30);
-    setLocalStorage('timerSeconds', seconds, 30);
-
-    // 保存自动倒计时设置
-    const autoCountdownEnabled = document.getElementById('auto-countdown-enabled').checked;
-    setLocalStorage('autoCountdownEnabled', autoCountdownEnabled ? '1' : '0', 30);
-}
-
-// 打开计时器对话框
-function openTimerDialog() {
-    document.getElementById('timer-dialog').style.display = 'flex';
-}
-
-// 关闭计时器对话框
-function closeTimerDialog() {
-    document.getElementById('timer-dialog').style.display = 'none';
-}
-
-// 开始计时器
-function startTimer() {
-    // 如果已暂停,则继续计时
-    if (timerPaused) {
-        timerPaused = false;
-    } else {
-        // 获取设置的时间
-        const minutes = parseInt(document.getElementById('timer-minutes').value) || 0;
-        const seconds = parseInt(document.getElementById('timer-seconds').value) || 0;
-        timerTotalSeconds = minutes * 60 + seconds;
-        timerCurrentSeconds = timerTotalSeconds;
-
-        // 保存设置到cookie
-        saveTimerSettings(minutes, seconds);
-
-        // 停止警报铃声
-        document.getElementById('alertSound').pause();
-        document.getElementById('alertSound').currentTime = 0;
-
-        // 更新暂停按钮文本为暂停
-        document.querySelector('#timer-dialog button:nth-child(1)').textContent = '暂停';
-    }
-
-    // 更新显示
-    updateTimerDisplay();
-
-    // 开始倒计时
-    if (!timerInterval) {
-        timerInterval = setInterval(updateTimer, 1000);
-    }
-}
-
-// 暂停计时器
-function pauseTimer() {
-    if (timerInterval) {
-        clearInterval(timerInterval);
-        timerInterval = null;
-        timerPaused = true;
-
-        // 停止警报铃声
-        document.getElementById('alertSound').pause();
-        document.getElementById('alertSound').currentTime = 0;
-
-        // 更新暂停按钮文本为继续
-        document.querySelector('#timer-dialog button:nth-child(1)').textContent = '继续';
-    }
-}
-
-// 重置计时器
-function resetTimer() {
-    if (timerInterval) {
-        clearInterval(timerInterval);
-        timerInterval = null;
-    }
-    timerPaused = false;
-
-    // 获取设置的时间
-    const minutes = parseInt(document.getElementById('timer-minutes').value) || 0;
-    const seconds = parseInt(document.getElementById('timer-seconds').value) || 0;
-    timerTotalSeconds = minutes * 60 + seconds;
-    timerCurrentSeconds = timerTotalSeconds;
-
-    // 更新显示
-    updateTimerDisplay();
-
-    // 停止警报铃声
-    document.getElementById('alertSound').pause();
-    document.getElementById('alertSound').currentTime = 0;
-
-    // 更新暂停按钮文本为开始
-    document.querySelector('#timer-dialog button:nth-child(1)').textContent = '开始';
-}
-
-// 更新计时器
-function updateTimer() {
-    if (timerCurrentSeconds > 0) {
-        timerCurrentSeconds--;
-        updateTimerDisplay();
-
-        // 当倒计时接近结束时播放警报
-        if (timerCurrentSeconds <= 3 && timerCurrentSeconds > 0) {
-            document.getElementById('alertSound').play();
-        }
-    } else {
-        // 倒计时结束
-        clearInterval(timerInterval);
-        timerInterval = null;
-
-        // 播放警报
-        document.getElementById('alertSound').play();
-
-        // 更新暂停按钮文本为开始
-        document.querySelector('#timer-dialog button:nth-child(1)').textContent = '开始';
-    }
-}
-
-// 更新计时器显示
-function updateTimerDisplay() {
-    const minutes = Math.floor(timerCurrentSeconds / 60);
-    const seconds = timerCurrentSeconds % 60;
-    const displayText = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
-    document.getElementById('countdown').textContent = displayText;
-}

+ 98 - 0
LabStatistics/utils.js

@@ -39,3 +39,101 @@ function updateCurrentTime() {
     document.getElementById('current-time').textContent = timeString;
 }
 
+function parseUnixTime(unixTimestamp) {
+    // 将Unix时间戳转换为毫秒
+    const timestamp = parseInt(unixTimestamp);
+    const date = new Date(timestamp);
+
+    // 格式化日期和时间
+    const year = date.getFullYear();
+    const month = (date.getMonth() + 1).toString().padStart(2, '0');
+    const day = date.getDate().toString().padStart(2, '0');
+    const hours = date.getHours().toString().padStart(2, '0');
+    const minutes = date.getMinutes().toString().padStart(2, '0');
+    const seconds = date.getSeconds().toString().padStart(2, '0');
+
+    // 返回格式化后的时间字符串
+    return `${hours}:${minutes}:${seconds}`;
+    // return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+}
+
+// 显示提示信息
+// 跟踪当前显示的toast数量
+let activeToasts = 0;
+const MAX_TOASTS = 3;
+
+function showToast(message, type = 'info', duration = 3000) {
+    // 如果已经达到最大数量,移除最早的toast
+    const existingToasts = document.querySelectorAll('.toast');
+    if (existingToasts.length >= MAX_TOASTS) {
+        // 不移除最早的,而是等待它自动消失
+        // 只是不再增加新的位置
+    }
+
+    // 创建toast元素
+    const toast = document.createElement('div');
+    toast.className = `toast toast-${type}`;
+    toast.textContent = message;
+    
+    // 计算当前toast应该的位置
+    const position = Math.min(existingToasts.length, MAX_TOASTS - 1);
+    const topPosition = 20 + position * 60; // 每个toast高度约50px,加上间距10px
+
+    // 设置样式
+    toast.style.cssText = `
+        position: fixed;
+        top: ${topPosition}px;
+        left: 50%;
+        transform: translateX(-50%);
+        padding: 10px 20px;
+        border-radius: 4px;
+        color: #fff;
+        font-size: 14px;
+        z-index: 9999;
+        opacity: 0;
+        transition: opacity 0.3s ease-in-out;
+    `;
+
+    // 根据类型设置背景色
+    const colors = {
+        success: '#67C23A',
+        error: '#F56C6C',
+        warning: '#E6A23C',
+        info: '#909399'
+    };
+    toast.style.backgroundColor = colors[type] || colors.info;
+
+    // 添加到文档
+    document.body.appendChild(toast);
+    activeToasts++;
+
+    // 显示动画
+    setTimeout(() => {
+        toast.style.opacity = '1';
+    }, 10);
+
+    // 自动移除
+    setTimeout(() => {
+        toast.style.opacity = '0';
+        setTimeout(() => {
+            document.body.removeChild(toast);
+            activeToasts--;
+            
+            // 当一个toast被移除时,重新调整其他toast的位置
+            repositionToasts();
+        }, 300);
+    }, duration);
+}
+
+// 重新调整所有toast的位置
+function repositionToasts() {
+    const toasts = document.querySelectorAll('.toast');
+    toasts.forEach((toast, index) => {
+        if (index < MAX_TOASTS) {
+            const topPosition = 20 + index * 60;
+            toast.style.top = `${topPosition}px`;
+        }
+    });
+}
+
+

+ 184 - 0
LabStatistics/千问2_LabStatistics_2025_06_26T07_05_36.json

@@ -0,0 +1,184 @@
+{
+  "stabilityData": {
+    "inputs": [
+      {
+        "time": 1750915418399,
+        "value": "12"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": 1750915419549,
+        "value": "13"
+      },
+      {
+        "time": 1750915420734,
+        "value": "12"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": 1750915422745,
+        "value": "12"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": null,
+        "value": ""
+      }
+    ],
+    "results": {
+      "max": "13",
+      "min": "12",
+      "mean": "12.25",
+      "range": "1"
+    }
+  },
+  "customParamsData": [
+    {
+      "name": "11",
+      "value": "22"
+    },
+    {
+      "name": "22",
+      "value": "33"
+    },
+    {
+      "name": "33",
+      "value": ""
+    },
+    {
+      "name": "",
+      "value": ""
+    },
+    {
+      "name": "",
+      "value": "55"
+    },
+    {
+      "name": "",
+      "value": ""
+    }
+  ],
+  "projectTitle": "千问2",
+  "repeatabilityData": {
+    "inputs": [
+      {
+        "time": 1750907401808,
+        "value": "12"
+      },
+      {
+        "time": 1750907402540,
+        "value": "13"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": 1750907404414,
+        "value": "13"
+      },
+      {
+        "time": 1750907405090,
+        "value": "12"
+      },
+      {
+        "time": 1750907455260,
+        "value": "13"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": 1750916695037,
+        "value": "12"
+      },
+      {
+        "time": null,
+        "value": ""
+      },
+      {
+        "time": null,
+        "value": ""
+      }
+    ],
+    "results": {
+      "range": "1",
+      "mean": "12.5",
+      "variance": "0.25",
+      "stdDev": "0.5",
+      "rsd": "4"
+    }
+  },
+  "timerSeconds": 0,
+  "timerMinutes": 1,
+  "errorData": {
+    "inputs": [
+      {
+        "point": "12",
+        "value": [
+          "13",
+          "12",
+          "14"
+        ],
+        "avg": "13.000000",
+        "error": "1.000000"
+      },
+      {
+        "point": "14",
+        "value": [
+          "12",
+          "11",
+          "12"
+        ],
+        "avg": "11.666667",
+        "error": "-2.333333"
+      },
+      {
+        "point": "",
+        "value": [
+          "",
+          "",
+          ""
+        ],
+        "avg": "-",
+        "error": "-"
+      },
+      {
+        "point": "12",
+        "value": [
+          "11",
+          "11",
+          "11"
+        ],
+        "avg": "11.000000",
+        "error": "-1.000000"
+      },
+      {
+        "point": "",
+        "value": [
+          "",
+          "",
+          ""
+        ],
+        "avg": "-",
+        "error": "-"
+      }
+    ],
+    "results": {
+      "uncertainty_a": "1.925e-1",
+      "uncertainty_b": "5.774e-4",
+      "uncertainty_c": "1.925e-1",
+      "uncertainty_u": "3.849e-1"
+    }
+  }
+}