data.js 24 KB

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