pageData.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. // data.js - 数据管理、导入导出相关功能
  2. // 收集并保存数据到localStorage
  3. function collectAndSaveData(selector, timeSelector, resultIds, cookieName) {
  4. // 收集输入数据
  5. const inputs = Array.from(document.querySelectorAll(selector)).map((input, index) => {
  6. const timeSpan = document.getElementById(`${timeSelector}-${index + 1}`);
  7. return {
  8. value: input.value,
  9. time: timeSpan ? timeSpan.textContent.trim() : ''
  10. };
  11. });
  12. // 收集结果数据
  13. const results = {};
  14. if (resultIds) {
  15. Object.keys(resultIds).forEach(key => {
  16. const element = document.getElementById(resultIds[key]);
  17. if (element) {
  18. results[key] = element.innerText;
  19. }
  20. });
  21. }
  22. // 保存到localStorage
  23. const data = {
  24. inputs: inputs,
  25. results: results
  26. };
  27. console.log(data);
  28. setLocalStorage(cookieName, JSON.stringify(data));
  29. }
  30. // 添加自定义参数
  31. function addCustomParam() {
  32. const container = document.getElementById('custom-params-container');
  33. const paramRow = document.createElement('div');
  34. paramRow.className = 'custom-param-row';
  35. paramRow.style.display = 'flex';
  36. paramRow.style.alignItems = 'center';
  37. paramRow.style.gap = '0.5rem';
  38. const nameInput = document.createElement('input');
  39. nameInput.type = 'text';
  40. nameInput.className = 'param-name';
  41. nameInput.placeholder = '参数名称';
  42. nameInput.style.flex = '1';
  43. nameInput.style.padding = '0.5rem';
  44. nameInput.style.border = '1px solid #ccc';
  45. nameInput.style.borderRadius = '4px';
  46. nameInput.oninput = saveCustomParamsData;
  47. const valueInput = document.createElement('input');
  48. valueInput.type = 'text';
  49. valueInput.className = 'param-value';
  50. valueInput.placeholder = '参数值';
  51. valueInput.style.flex = '1';
  52. valueInput.style.padding = '0.5rem';
  53. valueInput.style.border = '1px solid #ccc';
  54. valueInput.style.borderRadius = '4px';
  55. valueInput.oninput = saveCustomParamsData;
  56. const deleteBtn = document.createElement('span');
  57. deleteBtn.className = 'delete-btn';
  58. deleteBtn.innerHTML = '×';
  59. deleteBtn.onclick = function () {
  60. container.removeChild(paramRow);
  61. saveCustomParamsData();
  62. };
  63. paramRow.appendChild(nameInput);
  64. paramRow.appendChild(valueInput);
  65. paramRow.appendChild(deleteBtn);
  66. container.appendChild(paramRow);
  67. }
  68. // 保存自定义参数数据
  69. function saveCustomParamsData() {
  70. const customParams = Array.from(document.querySelectorAll('#custom-params-container .custom-param-row')).map(row => ({
  71. name: row.querySelector('.param-name').value,
  72. value: row.querySelector('.param-value').value
  73. }));
  74. setLocalStorage('customParamsData', JSON.stringify(customParams), 365);
  75. }
  76. // 从localStorage加载自定义参数
  77. function loadCustomParamsFromLocalStorage() {
  78. const savedParams = getLocalStorage('customParamsData');
  79. if (savedParams) {
  80. try {
  81. const params = JSON.parse(savedParams);
  82. const container = document.getElementById('custom-params-container');
  83. // 清除现有参数
  84. while (container.firstChild) {
  85. container.removeChild(container.firstChild);
  86. }
  87. // 添加保存的自定义参数
  88. params.forEach(param => {
  89. const paramRow = document.createElement('div');
  90. paramRow.className = 'custom-param-row';
  91. paramRow.style.display = 'flex';
  92. paramRow.style.alignItems = 'center';
  93. paramRow.style.gap = '0.5rem';
  94. const nameInput = document.createElement('input');
  95. nameInput.type = 'text';
  96. nameInput.className = 'param-name';
  97. nameInput.placeholder = '参数名称';
  98. nameInput.style.flex = '1';
  99. nameInput.style.padding = '0.5rem';
  100. nameInput.style.border = '1px solid #ccc';
  101. nameInput.style.borderRadius = '4px';
  102. nameInput.value = param.name;
  103. nameInput.oninput = saveCustomParamsData;
  104. const valueInput = document.createElement('input');
  105. valueInput.type = 'text';
  106. valueInput.className = 'param-value';
  107. valueInput.placeholder = '参数值';
  108. valueInput.style.flex = '1';
  109. valueInput.style.padding = '0.5rem';
  110. valueInput.style.border = '1px solid #ccc';
  111. valueInput.style.borderRadius = '4px';
  112. valueInput.value = param.value;
  113. valueInput.oninput = saveCustomParamsData;
  114. const deleteBtn = document.createElement('span');
  115. deleteBtn.className = 'delete-btn';
  116. deleteBtn.innerHTML = '×';
  117. deleteBtn.onclick = function () {
  118. container.removeChild(paramRow);
  119. saveCustomParamsData();
  120. };
  121. paramRow.appendChild(nameInput);
  122. paramRow.appendChild(valueInput);
  123. paramRow.appendChild(deleteBtn);
  124. container.appendChild(paramRow);
  125. });
  126. } catch (e) {
  127. console.error('加载自定义参数失败:', e);
  128. }
  129. }
  130. }
  131. // 从localStorage加载数据
  132. function loadDataFromLocalStorage() {
  133. // 加载重复性数据
  134. const savedRepeatabilityData = getLocalStorage('repeatabilityData');
  135. if (savedRepeatabilityData) {
  136. try {
  137. const data = JSON.parse(savedRepeatabilityData);
  138. // 清除现有的额外输入框(保留前6个)
  139. const repeatabilityInputs = document.querySelectorAll('#repeatability-inputs .input-row');
  140. for (let i = 6; i < repeatabilityInputs.length; i++) {
  141. repeatabilityInputs[i].remove();
  142. }
  143. // 清除现有输入值
  144. document.querySelectorAll('#repeatability-inputs input[type="number"]').forEach(input => {
  145. input.value = '';
  146. });
  147. // 填充保存的数据
  148. if (data.inputs && data.inputs.length > 0) {
  149. data.inputs.forEach((item, index) => {
  150. const value = typeof item === 'object' ? item.value : item;
  151. const time = typeof item === 'object' ? item.time : '';
  152. // 如果索引超出现有输入框数量,添加新的输入框
  153. if (index >= 6) {
  154. addRepeatabilityInput();
  155. }
  156. // 设置值
  157. const input = document.querySelector(`#repeatability-inputs .input-row:nth-child(${index + 1}) input`);
  158. if (input) {
  159. input.value = value;
  160. }
  161. // 设置时间
  162. if (time) {
  163. const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`);
  164. if (timeSpan) {
  165. timeSpan.textContent = `${time}: `;
  166. repeatabilityTimes[index] = time;
  167. }
  168. }
  169. });
  170. // 计算结果
  171. calculateRepeatability();
  172. }
  173. } catch (e) {
  174. console.error('加载重复性数据失败:', e);
  175. }
  176. }
  177. // 加载稳定性数据
  178. const savedStabilityData = getLocalStorage('stabilityData');
  179. if (savedStabilityData) {
  180. try {
  181. const data = JSON.parse(savedStabilityData);
  182. // 清除现有的额外输入框(保留前6个)
  183. const stabilityInputs = document.querySelectorAll('#stability-inputs .input-row');
  184. for (let i = 6; i < stabilityInputs.length; i++) {
  185. stabilityInputs[i].remove();
  186. }
  187. // 清除现有输入值
  188. document.querySelectorAll('#stability-inputs input[type="number"]').forEach(input => {
  189. input.value = '';
  190. });
  191. // 填充保存的数据
  192. if (data.inputs && data.inputs.length > 0) {
  193. data.inputs.forEach((item, index) => {
  194. const value = typeof item === 'object' ? item.value : item;
  195. const time = typeof item === 'object' ? item.time : '';
  196. // 如果索引超出现有输入框数量,添加新的输入框
  197. if (index >= 6) {
  198. addStabilityInput();
  199. }
  200. // 设置值
  201. const input = document.querySelector(`#stability-inputs .input-row:nth-child(${index + 1}) input`);
  202. if (input) {
  203. input.value = value;
  204. }
  205. // 设置时间
  206. if (time) {
  207. const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
  208. if (timeSpan) {
  209. timeSpan.textContent = `${time}: `;
  210. stabilityTimes[index] = time;
  211. }
  212. }
  213. });
  214. // 计算结果
  215. calculateStability();
  216. }
  217. } catch (e) {
  218. console.error('加载稳定性数据失败:', e);
  219. }
  220. }
  221. // 加载示值误差数据
  222. const savedErrorData = getLocalStorage('errorData');
  223. if (savedErrorData) {
  224. try {
  225. const errorData = JSON.parse(savedErrorData);
  226. // 清除现有的测试点
  227. const errorContainer = document.getElementById('error-inputs');
  228. while (errorContainer.firstChild) {
  229. errorContainer.removeChild(errorContainer.firstChild);
  230. }
  231. // 添加保存的测试点
  232. errorData.forEach(item => {
  233. const pointContainer = createErrorPointContainer(item.point);
  234. errorContainer.appendChild(pointContainer);
  235. // 填充输入值
  236. const inputs = pointContainer.querySelectorAll('.error-input');
  237. item.inputs.forEach((value, index) => {
  238. if (index < inputs.length) {
  239. inputs[index].value = value;
  240. }
  241. });
  242. });
  243. // 计算结果
  244. calculateError();
  245. updateErrorPoints();
  246. } catch (e) {
  247. console.error('加载示值误差数据失败:', e);
  248. }
  249. }
  250. }
  251. // 导出数据
  252. function exportData() {
  253. const data = {
  254. repeatability: {
  255. inputs: Array.from(document.querySelectorAll('#repeatability-inputs input[type="number"]')).map((input, index) => {
  256. const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`);
  257. return {
  258. value: input.value,
  259. time: timeSpan ? timeSpan.textContent.trim() : ''
  260. };
  261. }),
  262. results: {
  263. range: document.getElementById('range').innerText,
  264. mean: document.getElementById('mean').innerText,
  265. variance: document.getElementById('variance').innerText,
  266. stdDev: document.getElementById('stdDev').innerText,
  267. rsd: document.getElementById('rsd').innerText
  268. }
  269. },
  270. stability: {
  271. inputs: Array.from(document.querySelectorAll('#stability-inputs input[type="number"]')).map((input, index) => {
  272. const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
  273. return {
  274. value: input.value,
  275. time: timeSpan ? timeSpan.textContent.trim() : ''
  276. };
  277. }),
  278. results: {
  279. max: document.getElementById('stability-max').innerText,
  280. min: document.getElementById('stability-min').innerText,
  281. mean: document.getElementById('stability-mean').innerText,
  282. range: document.getElementById('stability-range').innerText
  283. }
  284. },
  285. error: Array.from(document.querySelectorAll('.error-point-container')).map(container => ({
  286. point: container.querySelector('.error-point-input').value,
  287. inputs: Array.from(container.querySelectorAll('.error-input')).map(input => input.value),
  288. avg: container.querySelector('.error-avg').innerText,
  289. error: container.querySelector('.error-value').innerText
  290. })),
  291. customParams: Array.from(document.querySelectorAll('#custom-params-container .custom-param-row')).map(row => ({
  292. name: row.querySelector('.param-name').value,
  293. value: row.querySelector('.param-value').value
  294. })),
  295. projectTitle: document.getElementById('project-title').value,
  296. timestamp: new Date().toISOString()
  297. };
  298. const jsonString = JSON.stringify(data, null, 2);
  299. const blob = new Blob([jsonString], { type: 'application/json' });
  300. const url = URL.createObjectURL(blob);
  301. const a = document.createElement('a');
  302. a.href = url;
  303. a.download = `lab-statistics-${new Date().toISOString().slice(0, 10)}.json`;
  304. document.body.appendChild(a);
  305. a.click();
  306. document.body.removeChild(a);
  307. URL.revokeObjectURL(url);
  308. // 保存所有数据到localStorage
  309. saveDataToLocalStorage();
  310. }
  311. // 导入数据
  312. function importData() {
  313. const fileInput = document.getElementById('importFile');
  314. const file = fileInput.files[0];
  315. if (!file) {
  316. alert('请选择要导入的文件!');
  317. return;
  318. }
  319. const reader = new FileReader();
  320. reader.onload = function(e) {
  321. try {
  322. const data = JSON.parse(e.target.result);
  323. // 导入项目标题
  324. if (data.projectTitle) {
  325. document.getElementById('project-title').value = data.projectTitle;
  326. updateProjectTitle();
  327. }
  328. // 清除现有数据
  329. clearAllData();
  330. // 导入重复性数据
  331. if (data.repeatability && data.repeatability.inputs) {
  332. const inputsContainer = document.getElementById('repeatability-inputs');
  333. // 清除现有的额外输入框(保留前6个)
  334. while (inputsContainer.children.length > 6) {
  335. inputsContainer.removeChild(inputsContainer.lastChild);
  336. }
  337. // 获取现有的输入框
  338. const existingInputs = document.querySelectorAll('#repeatability-inputs input[type="number"]');
  339. // 填充现有输入框的值
  340. data.repeatability.inputs.forEach((item, index) => {
  341. const value = typeof item === 'object' ? item.value : item;
  342. const time = typeof item === 'object' ? item.time : '';
  343. if (index < existingInputs.length) {
  344. // 如果有对应的输入框,直接设置值
  345. existingInputs[index].value = value;
  346. // 更新时间显示
  347. if (time) {
  348. const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`);
  349. if (timeSpan) {
  350. timeSpan.textContent = time + ' ';
  351. repeatabilityTimes[index] = time;
  352. }
  353. }
  354. } else {
  355. // 如果没有对应的输入框,创建新的
  356. addRepeatabilityInput();
  357. // 获取新创建的输入框并设置值
  358. const newInput = document.querySelector('#repeatability-inputs .input-row:last-child input');
  359. newInput.value = value;
  360. // 更新时间显示
  361. if (time) {
  362. const timeSpan = document.getElementById(`repeatability-time-text-${index + 1}`);
  363. if (timeSpan) {
  364. timeSpan.textContent = time + ' ';
  365. repeatabilityTimes[index] = time;
  366. }
  367. }
  368. }
  369. });
  370. calculateRepeatability();
  371. }
  372. // 导入稳定性数据
  373. if (data.stability && data.stability.inputs) {
  374. const inputsContainer = document.getElementById('stability-inputs');
  375. // 清除现有的额外输入框(保留前6个)
  376. while (inputsContainer.children.length > 6) {
  377. inputsContainer.removeChild(inputsContainer.lastChild);
  378. }
  379. // 获取现有的输入框
  380. const existingInputs = document.querySelectorAll('#stability-inputs input[type="number"]');
  381. // 填充现有输入框的值
  382. data.stability.inputs.forEach((item, index) => {
  383. const value = typeof item === 'object' ? item.value : item;
  384. const time = typeof item === 'object' ? item.time : '';
  385. if (index < existingInputs.length) {
  386. // 如果有对应的输入框,直接设置值
  387. existingInputs[index].value = value;
  388. // 更新时间显示
  389. if (time) {
  390. const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
  391. if (timeSpan) {
  392. timeSpan.textContent = time + ' ';
  393. stabilityTimes[index] = time;
  394. }
  395. }
  396. } else {
  397. // 如果没有对应的输入框,创建新的
  398. addStabilityInput();
  399. // 获取新创建的输入框并设置值
  400. const newInput = document.querySelector('#stability-inputs .input-row:last-child input');
  401. newInput.value = value;
  402. // 更新时间显示
  403. if (time) {
  404. const timeSpan = document.getElementById(`stability-time-text-${index + 1}`);
  405. if (timeSpan) {
  406. timeSpan.textContent = time + ' ';
  407. stabilityTimes[index] = time;
  408. }
  409. }
  410. }
  411. });
  412. calculateStability();
  413. }
  414. // 导入示值误差数据
  415. if (data.error && data.error.length > 0) {
  416. const errorContainer = document.getElementById('error-inputs');
  417. while (errorContainer.firstChild) {
  418. errorContainer.removeChild(errorContainer.firstChild);
  419. }
  420. // 处理导入的测试点数据
  421. data.error.forEach(item => {
  422. addErrorPoint();
  423. const pointContainers = document.querySelectorAll('.error-point-container');
  424. const pointContainer = pointContainers[pointContainers.length - 1];
  425. // 更新测试点的输入框值
  426. const pointInput = pointContainer.querySelector('.error-point-input');
  427. pointInput.value = item.point;
  428. pointInput.setAttribute('data-original', item.point);
  429. // 更新输入框的data-point属性
  430. const inputs = pointContainer.querySelectorAll('.error-input');
  431. inputs.forEach(input => {
  432. input.setAttribute('data-point', item.point);
  433. });
  434. // 更新结果span的data-point属性
  435. const avgSpan = pointContainer.querySelector('.error-avg');
  436. const errorSpan = pointContainer.querySelector('.error-value');
  437. avgSpan.setAttribute('data-point', item.point);
  438. errorSpan.setAttribute('data-point', item.point);
  439. // 填充输入值
  440. item.inputs.forEach((value, index) => {
  441. if (index < inputs.length) {
  442. inputs[index].value = value;
  443. }
  444. });
  445. });
  446. calculateError();
  447. }
  448. // 导入自定义参数数据
  449. if (data.customParams && data.customParams.length > 0) {
  450. const container = document.getElementById('custom-params-container');
  451. while (container.firstChild) {
  452. container.removeChild(container.firstChild);
  453. }
  454. // 添加保存的自定义参数
  455. data.customParams.forEach(param => {
  456. const paramRow = document.createElement('div');
  457. paramRow.className = 'custom-param-row';
  458. paramRow.style.display = 'flex';
  459. paramRow.style.alignItems = 'center';
  460. paramRow.style.gap = '0.5rem';
  461. const nameInput = document.createElement('input');
  462. nameInput.type = 'text';
  463. nameInput.className = 'param-name';
  464. nameInput.placeholder = '参数名称';
  465. nameInput.style.flex = '1';
  466. nameInput.style.padding = '0.5rem';
  467. nameInput.style.border = '1px solid #ccc';
  468. nameInput.style.borderRadius = '4px';
  469. nameInput.value = param.name;
  470. nameInput.oninput = saveCustomParamsData;
  471. const valueInput = document.createElement('input');
  472. valueInput.type = 'text';
  473. valueInput.className = 'param-value';
  474. valueInput.placeholder = '参数值';
  475. valueInput.style.flex = '1';
  476. valueInput.style.padding = '0.5rem';
  477. valueInput.style.border = '1px solid #ccc';
  478. valueInput.style.borderRadius = '4px';
  479. valueInput.value = param.value;
  480. valueInput.oninput = saveCustomParamsData;
  481. const deleteBtn = document.createElement('span');
  482. deleteBtn.className = 'delete-btn';
  483. deleteBtn.innerHTML = '×';
  484. deleteBtn.onclick = function () {
  485. container.removeChild(paramRow);
  486. saveCustomParamsData();
  487. };
  488. paramRow.appendChild(nameInput);
  489. paramRow.appendChild(valueInput);
  490. paramRow.appendChild(deleteBtn);
  491. container.appendChild(paramRow);
  492. });
  493. }
  494. // 保存所有数据到localStorage
  495. saveDataToLocalStorage();
  496. alert('数据导入成功!');
  497. } catch (error) {
  498. alert('导入失败!请检查文件格式。');
  499. console.error('导入错误:', error);
  500. }
  501. };
  502. reader.readAsText(file);
  503. }
  504. // 保存所有数据到localStorage(兼容性函数)
  505. function saveDataToLocalStorage() {
  506. saveRepeatabilityData();
  507. saveStabilityData();
  508. saveErrorData();
  509. saveCustomParamsData();
  510. }