{"repo": "bilybaofu/baby-growth-curve", "files": {"baby-tracker.js": {"5b07ca027b872c8c79ccdefa063fb954b92be2ac": {"t": 1786068268.397868, "n": 112807, "h": "d90ab3df4667a9cc", "c": "// 婴幼儿体检报告智能识别系统 - 百度OCR版本\n(function() {\n 'use strict';\n \n class BabyHealthTracker {\n constructor() {\n this.apiKey = localStorage.getItem('deepseek_api_key') || '';\n this.baiduOcrConfig = {\n apiKey: localStorage.getItem('baidu_ocr_api_key') || '',\n secretKey: localStorage.getItem('baidu_ocr_secret_key') || '',\n accessToken: localStorage.getItem('baidu_ocr_access_token') || '',\n tokenExpireTime: parseInt(localStorage.getItem('baidu_ocr_token_expire') || '0')\n };\n this.checkRecords = JSON.parse(localStorage.getItem('baby_check_records') || '[]');\n this.babyInfo = JSON.parse(localStorage.getItem('baby_info') || '{}');\n this.isUploading = false;\n this.lastOCRResult = null;\n this.whoData = this.initCompleteWHOData();\n this.growthChart = null;\n this.tempExtractedData = null;\n \n this.init();\n }\n\n initCompleteWHOData() {\n return {\n maleHeight: {\n 0: [44.2, 46.1, 48.0, 49.9, 51.8, 53.7, 55.6],\n 1: [48.9, 50.8, 52.8, 54.7, 56.7, 58.6, 60.6],\n 2: [52.4, 54.4, 56.4, 58.4, 60.4, 62.4, 64.4],\n 3: [55.3, 57.3, 59.4, 61.4, 63.5, 65.5, 67.6],\n 4: [57.6, 59.7, 61.8, 63.9, 66.0, 68.0, 70.1],\n 5: [59.6, 61.7, 63.8, 65.9, 68.0, 70.1, 72.2],\n 6: [61.2, 63.3, 65.5, 67.6, 69.8, 71.9, 74.0],\n 7: [62.7, 64.8, 67.0, 69.2, 71.3, 73.5, 75.7],\n 8: [64.0, 66.2, 68.4, 70.6, 72.8, 75.0, 77.2],\n 9: [65.2, 67.5, 69.7, 72.0, 74.2, 76.5, 78.7],\n 10: [66.4, 68.7, 71.0, 73.3, 75.6, 77.9, 80.1],\n 11: [67.6, 69.9, 72.2, 74.5, 76.9, 79.2, 81.5],\n 12: [68.6, 71.0, 73.4, 75.7, 78.1, 80.5, 82.9],\n 13: [69.6, 72.1, 74.5, 76.9, 79.3, 81.8, 84.2],\n 14: [70.6, 73.1, 75.6, 78.0, 80.5, 83.0, 85.5],\n 15: [71.6, 74.1, 76.6, 79.1, 81.7, 84.2, 86.7],\n 16: [72.5, 75.0, 77.6, 80.2, 82.8, 85.4, 88.0],\n 17: [73.3, 76.0, 78.6, 81.2, 83.9, 86.5, 89.2],\n 18: [74.2, 76.9, 79.6, 82.3, 85.0, 87.7, 90.4],\n 19: [75.0, 77.7, 80.5, 83.2, 86.0, 88.8, 91.5],\n 20: [75.8, 78.6, 81.4, 84.2, 87.0, 89.8, 92.6],\n 21: [76.5, 79.4, 82.3, 85.1, 88.0, 90.9, 93.8],\n 22: [77.2, 80.2, 83.1, 86.0, 89.0, 91.9, 94.9],\n 23: [78.0, 81.0, 83.9, 86.9, 89.9, 92.9, 95.9],\n 24: [78.7, 81.7, 84.8, 87.8, 90.9, 93.9, 97.0],\n 25: [78.6, 81.7, 84.9, 88.0, 91.1, 94.2, 97.3],\n 26: [79.3, 82.5, 85.6, 88.8, 92.0, 95.2, 98.3],\n 27: [79.9, 83.1, 86.4, 89.6, 92.9, 96.1, 99.3],\n 28: [80.5, 83.8, 87.1, 90.4, 93.7, 97.0, 100.3],\n 29: [81.1, 84.5, 87.8, 91.2, 94.5, 97.9, 101.2],\n 30: [81.7, 85.1, 88.5, 91.9, 95.3, 98.7, 102.1],\n 31: [82.3, 85.7, 89.2, 92.7, 96.1, 99.6, 103.0],\n 32: [82.8, 86.4, 89.9, 93.4, 96.9, 100.4, 103.9],\n 33: [83.4, 86.9, 90.5, 94.1, 97.6, 101.2, 104.8],\n 34: [83.9, 87.5, 91.1, 94.8, 98.4, 102.0, 105.6],\n 35: [84.4, 88.1, 91.8, 95.4, 99.1, 102.7, 106.4],\n 36: [85.0, 88.7, 92.4, 96.1, 99.8, 103.5, 107.2]\n },\n femaleHeight: {\n 0: [43.6, 45.4, 47.3, 49.1, 51.0, 52.9, 54.7],\n 1: [47.8, 49.8, 51.7, 53.7, 55.6, 57.6, 59.5],\n 2: [51.0, 53.0, 55.0, 57.1, 59.1, 61.1, 63.2],\n 3: [53.5, 55.6, 57.7, 59.8, 61.9, 64.0, 66.1],\n 4: [55.6, 57.8, 59.9, 62.1, 64.3, 66.4, 68.6],\n 5: [57.4, 59.6, 61.8, 64.0, 66.2, 68.5, 70.7],\n 6: [58.9, 61.2, 63.5, 65.7, 68.0, 70.3, 72.5],\n 7: [60.3, 62.7, 65.0, 67.3, 69.6, 71.9, 74.2],\n 8: [61.7, 64.0, 66.4, 68.7, 71.1, 73.5, 75.8],\n 9: [62.9, 65.3, 67.7, 70.1, 72.6, 75.0, 77.4],\n 10: [64.1, 66.5, 69.0, 71.5, 73.9, 76.4, 78.9],\n 11: [65.2, 67.7, 70.3, 72.8, 75.3, 77.8, 80.3],\n 12: [66.3, 68.9, 71.4, 74.0, 76.6, 79.2, 81.7],\n 13: [67.3, 70.0, 72.6, 75.2, 77.8, 80.5, 83.1],\n 14: [68.3, 71.0, 73.7, 76.4, 79.1, 81.7, 84.4],\n 15: [69.3, 72.0, 74.8, 77.5, 80.2, 83.0, 85.7],\n 16: [70.2, 73.0, 75.8, 78.6, 81.4, 84.2, 87.0],\n 17: [71.1, 74.0, 76.8, 79.7, 82.5, 85.4, 88.2],\n 18: [72.0, 74.9, 77.8, 80.7, 83.6, 86.5, 89.4],\n 19: [72.8, 75.8, 78.8, 81.7, 84.7, 87.6, 90.6],\n 20: [73.7, 76.7, 79.7, 82.7, 85.7, 88.7, 91.7],\n 21: [74.5, 77.5, 80.6, 83.7, 86.7, 89.8, 92.9],\n 22: [75.2, 78.4, 81.5, 84.6, 87.7, 90.8, 94.0],\n 23: [76.0, 79.2, 82.3, 85.5, 88.7, 91.9, 95.0],\n 24: [76.7, 80.0, 83.2, 86.4, 89.6, 92.9, 96.1],\n 25: [76.8, 80.0, 83.3, 86.6, 89.9, 93.1, 96.4],\n 26: [77.5, 80.8, 84.1, 87.4, 90.8, 94.1, 97.4],\n 27: [78.1, 81.5, 84.9, 88.3, 91.7, 95.0, 98.4],\n 28: [78.8, 82.2, 85.7, 89.1, 92.5, 96.0, 99.4],\n 29: [79.5, 82.9, 86.4, 89.9, 93.4, 96.9, 100.3],\n 30: [80.1, 83.6, 87.1, 90.7, 94.2, 97.7, 101.3],\n 31: [80.7, 84.3, 87.9, 91.4, 95.0, 98.6, 102.2],\n 32: [81.3, 84.9, 88.6, 92.2, 95.8, 99.4, 103.1],\n 33: [81.9, 85.6, 89.3, 92.9, 96.6, 100.3, 103.9],\n 34: [82.5, 86.2, 89.9, 93.6, 97.4, 101.1, 104.8],\n 35: [83.1, 86.8, 90.6, 94.4, 98.1, 101.9, 105.6],\n 36: [83.6, 87.4, 91.2, 95.1, 98.9, 102.7, 106.5]\n },\n maleWeight: {\n 0: [2.1, 2.5, 2.9, 3.3, 3.9, 4.4, 5.0],\n 1: [2.9, 3.4, 3.9, 4.5, 5.1, 5.8, 6.6],\n 2: [3.8, 4.3, 4.9, 5.6, 6.3, 7.1, 8.0],\n 3: [4.4, 5.0, 5.7, 6.4, 7.2, 8.0, 9.0],\n 4: [4.9, 5.6, 6.2, 7.0, 7.8, 8.7, 9.7],\n 5: [5.3, 6.0, 6.7, 7.5, 8.4, 9.3, 10.4],\n 6: [5.7, 6.4, 7.1, 7.9, 8.8, 9.8, 10.9],\n 7: [5.9, 6.7, 7.4, 8.3, 9.2, 10.3, 11.4],\n 8: [6.2, 6.9, 7.7, 8.6, 9.6, 10.7, 11.9],\n 9: [6.4, 7.1, 8.0, 8.9, 9.9, 11.0, 12.3],\n 10: [6.6, 7.4, 8.2, 9.2, 10.2, 11.4, 12.7],\n 11: [6.8, 7.6, 8.4, 9.4, 10.5, 11.7, 13.0],\n 12: [6.9, 7.7, 8.6, 9.6, 10.8, 12.0, 13.3],\n 13: [7.1, 7.9, 8.8, 9.9, 11.0, 12.3, 13.7],\n 14: [7.2, 8.1, 9.0, 10.1, 11.3, 12.6, 14.0],\n 15: [7.4, 8.3, 9.2, 10.3, 11.5, 12.8, 14.3],\n 16: [7.5, 8.4, 9.4, 10.5, 11.7, 13.1, 14.6],\n 17: [7.7, 8.6, 9.6, 10.7, 12.0, 13.4, 14.9],\n 18: [7.8, 8.8, 9.8, 10.9, 12.2, 13.7, 15.3],\n 19: [8.0, 8.9, 10.0, 11.1, 12.5, 13.9, 15.6],\n 20: [8.1, 9.1, 10.1, 11.3, 12.7, 14.2, 15.9],\n 21: [8.2, 9.2, 10.3, 11.5, 12.9, 14.5, 16.2],\n 22: [8.4, 9.4, 10.5, 11.8, 13.2, 14.7, 16.5],\n 23: [8.5, 9.5, 10.7, 12.0, 13.4, 15.0, 16.8],\n 24: [8.6, 9.7, 10.8, 12.2, 13.6, 15.3, 17.1],\n 25: [8.8, 9.8, 11.0, 12.4, 13.9, 15.5, 17.5],\n 26: [8.9, 10.0, 11.2, 12.5, 14.1, 15.8, 17.8],\n 27: [9.0, 10.1, 11.3, 12.7, 14.3, 16.1, 18.1],\n 28: [9.1, 10.2, 11.5, 12.9, 14.5, 16.3, 18.4],\n 29: [9.2, 10.4, 11.7, 13.1, 14.8, 16.6, 18.7],\n 30: [9.4, 10.5, 11.8, 13.3, 15.0, 16.9, 19.0],\n 31: [9.5, 10.7, 12.0, 13.5, 15.2, 17.1, 19.3],\n 32: [9.6, 10.8, 12.1, 13.7, 15.4, 17.4, 19.6],\n 33: [9.7, 10.9, 12.3, 13.8, 15.6, 17.6, 19.9],\n 34: [9.8, 11.0, 12.4, 14.0, 15.8, 17.8, 20.2],\n 35: [9.9, 11.2, 12.6, 14.2, 16.0, 18.1, 20.4],\n 36: [10.0, 11.3, 12.7, 14.3, 16.2, 18.3, 20.7]\n },\n femaleWeight: {\n 0: [2.0, 2.4, 2.8, 3.2, 3.7, 4.2, 4.8],\n 1: [2.7, 3.2, 3.6, 4.2, 4.8, 5.5, 6.2],\n 2: [3.4, 3.9, 4.5, 5.1, 5.8, 6.6, 7.5],\n 3: [4.0, 4.5, 5.2, 5.8, 6.6, 7.5, 8.5],\n 4: [4.4, 5.0, 5.7, 6.4, 7.3, 8.2, 9.3],\n 5: [4.8, 5.4, 6.1, 6.9, 7.8, 8.8, 10.0],\n 6: [5.1, 5.7, 6.5, 7.3, 8.2, 9.3, 10.6],\n 7: [5.3, 6.0, 6.8, 7.6, 8.6, 9.8, 11.1],\n 8: [5.6, 6.3, 7.0, 7.9, 9.0, 10.2, 11.6],\n 9: [5.8, 6.5, 7.3, 8.2, 9.3, 10.5, 12.0],\n 10: [5.9, 6.7, 7.5, 8.5, 9.6, 10.9, 12.4],\n 11: [6.1, 6.9, 7.7, 8.7, 9.9, 11.2, 12.8],\n 12: [6.3, 7.0, 7.9, 8.9, 10.1, 11.5, 13.1],\n 13: [6.4, 7.2, 8.1, 9.2, 10.4, 11.8, 13.5],\n 14: [6.6, 7.4, 8.3, 9.4, 10.6, 12.1, 13.8],\n 15: [6.7, 7.6, 8.5, 9.6, 10.9, 12.4, 14.1],\n 16: [6.9, 7.7, 8.7, 9.8, 11.1, 12.6, 14.5],\n 17: [7.0, 7.9, 8.9, 10.0, 11.4, 12.9, 14.8],\n 18: [7.2, 8.1, 9.1, 10.2, 11.6, 13.2, 15.1],\n 19: [7.3, 8.2, 9.2, 10.4, 11.8, 13.5, 15.4],\n 20: [7.5, 8.4, 9.4, 10.6, 12.1, 13.7, 15.7],\n 21: [7.6, 8.6, 9.6, 10.9, 12.3, 14.0, 16.0],\n 22: [7.8, 8.7, 9.8, 11.1, 12.5, 14.3, 16.4],\n 23: [7.9, 8.9, 10.0, 11.3, 12.8, 14.6, 16.7],\n 24: [8.1, 9.0, 10.2, 11.5, 13.0, 14.8, 17.0],\n 25: [8.2, 9.2, 10.3, 11.7, 13.3, 15.1, 17.3],\n 26: [8.4, 9.4, 10.5, 11.9, 13.5, 15.4, 17.7],\n 27: [8.5, 9.5, 10.7, 12.1, 13.7, 15.7, 18.0],\n 28: [8.6, 9.7, 10.9, 12.3, 14.0, 16.0, 18.3],\n 29: [8.8, 9.8, 11.1, 12.5, 14.2, 16.2, 18.7],\n 30: [8.9, 10.0, 11.2, 12.7, 14.4, 16.5, 19.0],\n 31: [9.0, 10.1, 11.4, 12.9, 14.7, 16.8, 19.3],\n 32: [9.1, 10.3, 11.6, 13.1, 14.9, 17.1, 19.6],\n 33: [9.3, 10.4, 11.7, 13.3, 15.1, 17.3, 20.0],\n 34: [9.4, 10.5, 11.9, 13.5, 15.4, 17.6, 20.3],\n 35: [9.5, 10.7, 12.0, 13.7, 15.6, 17.9, 20.6],\n 36: [9.6, 10.8, 12.2, 13.9, 15.8, 18.1, 20.9]\n },\n maleHeadCircumference: {\n 0: [30.4, 31.7, 33.0, 34.3, 35.6, 36.9, 38.3],\n 1: [33.4, 34.6, 35.8, 37.0, 38.2, 39.4, 40.6],\n 2: [35.7, 36.8, 37.9, 39.1, 40.2, 41.4, 42.6],\n 3: [37.1, 38.2, 39.3, 40.5, 41.6, 42.8, 44.1],\n 4: [38.1, 39.3, 40.4, 41.6, 42.8, 44.0, 45.3],\n 5: [39.0, 40.2, 41.3, 42.5, 43.8, 45.0, 46.3],\n 6: [39.8, 41.0, 42.1, 43.4, 44.6, 45.9, 47.2],\n 7: [40.5, 41.7, 42.8, 44.0, 45.3, 46.6, 47.9],\n 8: [41.1, 42.2, 43.4, 44.6, 45.9, 47.2, 48.5],\n 9: [41.5, 42.7, 43.9, 45.1, 46.4, 47.7, 49.0],\n 10: [41.9, 43.1, 44.3, 45.5, 46.8, 48.1, 49.4],\n 11: [42.3, 43.4, 44.6, 45.8, 47.1, 48.4, 49.8],\n 12: [42.5, 43.7, 44.9, 46.1, 47.4, 48.7, 50.1],\n 13: [42.8, 44.0, 45.1, 46.4, 47.7, 49.0, 50.3],\n 14: [43.0, 44.2, 45.4, 46.6, 47.9, 49.2, 50.6],\n 15: [43.2, 44.4, 45.6, 46.8, 48.1, 49.4, 50.8],\n 16: [43.4, 44.6, 45.8, 47.0, 48.3, 49.6, 51.0],\n 17: [43.6, 44.7, 45.9, 47.2, 48.5, 49.8, 51.2],\n 18: [43.8, 44.9, 46.1, 47.4, 48.7, 50.0, 51.4],\n 19: [43.9, 45.1, 46.3, 47.5, 48.8, 50.2, 51.6],\n 20: [44.1, 45.3, 46.5, 47.7, 49.0, 50.4, 51.7],\n 21: [44.3, 45.4, 46.6, 47.9, 49.2, 50.5, 51.9],\n 22: [44.4, 45.6, 46.8, 48.1, 49.4, 50.7, 52.1],\n 23: [44.6, 45.7, 47.0, 48.2, 49.5, 50.9, 52.3],\n 24: [44.7, 45.9, 47.1, 48.3, 49.6, 51.0, 52.4],\n 25: [44.8, 46.0, 47.2, 48.4, 49.7, 51.1, 52.5],\n 26: [44.9, 46.1, 47.3, 48.5, 49.8, 51.2, 52.6],\n 27: [45.0, 46.2, 47.4, 48.7, 50.0, 51.3, 52.7],\n 28: [45.1, 46.3, 47.5, 48.8, 50.1, 51.4, 52.8],\n 29: [45.2, 46.3, 47.6, 48.8, 50.2, 51.5, 52.9],\n 30: [45.3, 46.4, 47.7, 48.9, 50.3, 51.6, 53.0],\n 31: [45.4, 46.5, 47.8, 49.0, 50.4, 51.7, 53.1],\n 32: [45.4, 46.6, 47.8, 49.1, 50.4, 51.8, 53.2],\n 33: [45.5, 46.7, 47.9, 49.2, 50.5, 51.9, 53.3],\n 34: [45.6, 46.7, 48.0, 49.2, 50.6, 52.0, 53.4],\n 35: [45.6, 46.8, 48.0, 49.3, 50.6, 52.0, 53.4],\n 36: [45.7, 46.8, 48.1, 49.3, 50.7, 52.1, 53.5]\n },\n femaleHeadCircumference: {\n 0: [30.1, 31.4, 32.7, 33.9, 35.2, 36.5, 37.7],\n 1: [32.9, 34.0, 35.2, 36.3, 37.5, 38.6, 39.8],\n 2: [34.9, 36.0, 37.1, 38.2, 39.3, 40.4, 41.6],\n 3: [36.2, 37.3, 38.4, 39.5, 40.7, 41.8, 42.9],\n 4: [37.2, 38.3, 39.4, 40.6, 41.7, 42.9, 44.1],\n 5: [38.0, 39.2, 40.3, 41.5, 42.6, 43.8, 45.0],\n 6: [38.8, 39.9, 41.1, 42.2, 43.4, 44.6, 45.9],\n 7: [39.4, 40.6, 41.7, 42.9, 44.1, 45.3, 46.6],\n 8: [40.0, 41.1, 42.3, 43.5, 44.7, 45.9, 47.2],\n 9: [40.4, 41.6, 42.8, 44.0, 45.2, 46.5, 47.7],\n 10: [40.8, 42.0, 43.2, 44.4, 45.6, 46.9, 48.2],\n 11: [41.2, 42.4, 43.6, 44.8, 46.0, 47.3, 48.6],\n 12: [41.5, 42.7, 43.9, 45.1, 46.4, 47.6, 48.9],\n 13: [41.8, 42.9, 44.2, 45.4, 46.6, 47.9, 49.2],\n 14: [42.0, 43.2, 44.4, 45.6, 46.9, 48.2, 49.5],\n 15: [42.2, 43.4, 44.6, 45.9, 47.1, 48.4, 49.7],\n 16: [42.4, 43.6, 44.8, 46.1, 47.3, 48.6, 50.0],\n 17: [42.6, 43.8, 45.0, 46.2, 47.5, 48.8, 50.1],\n 18: [42.7, 43.9, 45.2, 46.4, 47.7, 49.0, 50.3],\n 19: [42.9, 44.1, 45.3, 46.6, 47.9, 49.2, 50.5],\n 20: [43.1, 44.3, 45.5, 46.7, 48.0, 49.3, 50.7],\n 21: [43.2, 44.4, 45.6, 46.9, 48.2, 49.5, 50.9],\n 22: [43.4, 44.6, 45.8, 47.1, 48.4, 49.7, 51.0],\n 23: [43.5, 44.7, 45.9, 47.2, 48.5, 49.8, 51.2],\n 24: [43.6, 44.8, 46.1, 47.3, 48.6, 50.0, 51.3],\n 25: [43.7, 44.9, 46.2, 47.4, 48.8, 50.1, 51.5],\n 26: [43.8, 45.0, 46.3, 47.5, 48.9, 50.2, 51.6],\n 27: [43.9, 45.1, 46.4, 47.6, 49.0, 50.3, 51.7],\n 28: [44.0, 45.2, 46.5, 47.7, 49.1, 50.4, 51.8],\n 29: [44.1, 45.3, 46.6, 47.8, 49.2, 50.5, 51.9],\n 30: [44.2, 45.4, 46.7, 47.9, 49.3, 50.6, 52.0],\n 31: [44.3, 45.5, 46.8, 48.0, 49.4, 50.7, 52.1],\n 32: [44.3, 45.6, 46.8, 48.1, 49.5, 50.8, 52.2],\n 33: [44.4, 45.7, 46.9, 48.2, 49.6, 50.9, 52.3],\n 34: [44.5, 45.8, 47.0, 48.3, 49.7, 51.0, 52.4],\n 35: [44.6, 45.8, 47.1, 48.4, 49.8, 51.1, 52.5],\n 36: [44.7, 45.9, 47.2, 48.5, 49.9, 51.2, 52.7]\n }\n };\n }\n\n init() {\n this.bindEvents();\n this.loadSavedData();\n this.updateUI();\n this.initializeChart();\n }\n\n validateBasicInfo() {\n const name = this.babyInfo.name ? this.babyInfo.name.trim() : '';\n const gender = this.babyInfo.gender;\n const birthDate = this.babyInfo.birthDate;\n \n console.log('验证基本信息:', { name, gender, birthDate });\n \n if (!name || !gender || !birthDate) {\n this.showMessage('🍼 请先完整填写宝宝的基本信息(姓名、性别、出生日期)', 'error');\n this.switchToBasicInfoTab();\n return false;\n }\n return true;\n }\n\n switchToBasicInfoTab() {\n const basicInfoTab = document.querySelector('[data-tab=\"basic-info\"]');\n const basicInfoContent = document.getElementById('basic-info');\n \n if (basicInfoTab && basicInfoContent) {\n document.querySelectorAll('.tab-button').forEach(tab => tab.classList.remove('active'));\n document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));\n \n basicInfoTab.classList.add('active');\n basicInfoContent.classList.add('active');\n }\n }\n\n bindEvents() {\n const self = this;\n \n // API密钥设置\n const apiKeyInput = document.getElementById('apiKeyInput');\n if (apiKeyInput) {\n apiKeyInput.addEventListener('change', function(e) {\n self.apiKey = e.target.value.trim();\n self.updateApiStatus();\n });\n }\n\n // API密钥保存按钮\n const saveApiKeyBtn = document.getElementById('saveApiKeyBtn');\n if (saveApiKeyBtn) {\n saveApiKeyBtn.addEventListener('click', function() {\n self.saveApiKey();\n });\n }\n\n // 百度OCR配置保存\n const saveBaiduOcrBtn = document.getElementById('saveBaiduOcrBtn');\n if (saveBaiduOcrBtn) {\n saveBaiduOcrBtn.addEventListener('click', function() {\n self.saveBaiduOcrConfig();\n });\n }\n const testBaiduOcrBtn = document.getElementById('testBaiduOcrBtn');\n if (testBaiduOcrBtn) {\n testBaiduOcrBtn.addEventListener('click', function() {\n console.log('🔧 用户点击了测试连接按钮');\n self.testBaiduOcrConnection();\n });\n }\n\n // 图片上传\n const reportUpload = document.getElementById('reportUpload');\n if (reportUpload) {\n reportUpload.addEventListener('change', function(e) {\n self.handleImageUpload(e.target.files[0]);\n });\n }\n\n // 手动添加记录\n const addRecordBtn = document.getElementById('addRecordBtn');\n if (addRecordBtn) {\n addRecordBtn.addEventListener('click', function() {\n self.addManualRecord();\n });\n }\n\n // 一键清除记录\n const clearAllRecordsBtn = document.getElementById('clearAllRecordsBtn');\n if (clearAllRecordsBtn) {\n clearAllRecordsBtn.addEventListener('click', function() {\n self.clearAllRecords();\n });\n }\n\n // 导出数据\n const exportBtn = document.getElementById('exportBtn');\n if (exportBtn) {\n exportBtn.addEventListener('click', function() {\n self.exportData();\n });\n }\n\n // 导入数据\n const importBtn = document.getElementById('importBtn');\n if (importBtn) {\n importBtn.addEventListener('click', function() {\n self.importData();\n });\n }\n\n // 清空表单\n const clearFormBtn = document.getElementById('clearFormBtn');\n if (clearFormBtn) {\n clearFormBtn.addEventListener('click', function() {\n self.clearForm();\n });\n }\n\n // 拖拽上传\n const uploadArea = document.querySelector('.upload-area');\n if (uploadArea) {\n uploadArea.addEventListener('dragover', function(e) {\n e.preventDefault();\n uploadArea.classList.add('dragover');\n });\n\n uploadArea.addEventListener('dragleave', function() {\n uploadArea.classList.remove('dragover');\n });\n\n uploadArea.addEventListener('drop', function(e) {\n e.preventDefault();\n uploadArea.classList.remove('dragover');\n const files = e.dataTransfer.files;\n if (files.length > 0) {\n self.handleImageUpload(files[0]);\n }\n });\n }\n\n // 实时计算百分位\n const inputs = ['ageMonths', 'height', 'weight', 'headCircumference'];\n inputs.forEach(function(id) {\n const input = document.getElementById(id);\n if (input) {\n input.addEventListener('input', function() {\n self.updatePercentiles();\n });\n }\n });\n\n // 保存宝宝基本信息\n const babyInfoInputs = ['babyName', 'babyGender', 'babyBirthDate'];\n babyInfoInputs.forEach(function(id) {\n const input = document.getElementById(id);\n if (input) {\n input.addEventListener('change', function() {\n self.saveBabyInfo();\n self.updateCurrentStatus();\n });\n }\n });\n\n // 体检日期变化时自动计算月龄\n const checkDateInput = document.getElementById('checkDate');\n if (checkDateInput) {\n checkDateInput.addEventListener('change', function() {\n self.autoCalculateAge();\n });\n }\n\n // 生成AI喂养建议按钮\n const generateFeedingBtn = document.getElementById('generateFeedingBtn');\n if (generateFeedingBtn) {\n generateFeedingBtn.addEventListener('click', function() {\n self.generateAIFeedingGuide();\n });\n }\n }\n\n autoCalculateAge() {\n const birthDate = this.babyInfo.birthDate;\n const checkDate = document.getElementById('checkDate') ? document.getElementById('checkDate').value : '';\n \n console.log('计算月龄 - 出生日期:', birthDate, '体检日期:', checkDate);\n \n if (birthDate && checkDate) {\n const birth = new Date(birthDate);\n const check = new Date(checkDate);\n \n if (check >= birth) {\n const years = check.getFullYear() - birth.getFullYear();\n const months = check.getMonth() - birth.getMonth();\n const days = check.getDate() - birth.getDate();\n \n let totalMonths = years * 12 + months;\n if (days < 0) {\n totalMonths -= 1;\n }\n \n const exactMonths = totalMonths + (days >= 0 ? days / 30 : (30 + days) / 30);\n const roundedMonths = Math.round(exactMonths * 10) / 10;\n \n const ageInput = document.getElementById('ageMonths');\n if (ageInput) {\n ageInput.value = roundedMonths;\n this.updatePercentiles();\n console.log('自动计算月龄:', roundedMonths);\n }\n }\n } else if (!birthDate) {\n this.showMessage('🍼 请先在基本信息中填写出生日期', 'warning');\n }\n }\n\n saveApiKey() {\n const apiKeyInput = document.getElementById('apiKeyInput');\n if (apiKeyInput) {\n this.apiKey = apiKeyInput.value.trim();\n if (this.apiKey) {\n localStorage.setItem('deepseek_api_key', this.apiKey);\n this.updateApiStatus();\n this.showMessage('🔑 API密钥保存成功', 'success');\n } else {\n this.showMessage('❌ 请输入有效的API密钥', 'error');\n }\n }\n }\n\n saveBaiduOcrConfig() {\n const apiKeyInput = document.getElementById('baiduOcrApiKey');\n const secretKeyInput = document.getElementById('baiduOcrSecretKey');\n \n if (apiKeyInput && secretKeyInput) {\n const apiKey = apiKeyInput.value.trim();\n const secretKey = secretKeyInput.value.trim();\n \n if (apiKey && secretKey) {\n this.baiduOcrConfig.apiKey = apiKey;\n this.baiduOcrConfig.secretKey = secretKey;\n \n localStorage.setItem('baidu_ocr_api_key', apiKey);\n localStorage.setItem('baidu_ocr_secret_key', secretKey);\n \n this.baiduOcrConfig.accessToken = '';\n this.baiduOcrConfig.tokenExpireTime = 0;\n localStorage.removeItem('baidu_ocr_access_token');\n localStorage.removeItem('baidu_ocr_token_expire');\n \n this.updateBaiduOcrStatus();\n this.showMessage('🔑 百度OCR配置保存成功', 'success');\n } else {\n this.showMessage('❌ 请输入完整的百度OCR API Key和Secret Key', 'error');\n }\n }\n }\n\n updateBaiduOcrStatus() {\n const statusIndicator = document.getElementById('baiduOcrStatus');\n const statusText = document.getElementById('baiduOcrStatusText');\n \n if (this.baiduOcrConfig.apiKey && this.baiduOcrConfig.secretKey) {\n if (statusIndicator) statusIndicator.classList.add('connected');\n if (statusText) statusText.textContent = '🤖 百度OCR已配置';\n } else {\n if (statusIndicator) statusIndicator.classList.remove('connected');\n if (statusText) statusText.textContent = '😴 未配置';\n }\n }\n // 测试百度OCR连接\nasync testBaiduOcrConnection() {\n console.log('🔧 开始测试百度OCR连接...');\n \n try {\n this.showLoadingState('🔧 正在测试连接...');\n \n // 第一步:检查配置\n if (!this.baiduOcrConfig.apiKey || !this.baiduOcrConfig.secretKey) {\n throw new Error('❌ 请先填写百度OCR的API Key和Secret Key');\n }\n console.log('✅ 配置检查通过');\n \n // 第二步:直接测试百度OCR认证(跳过代理服务器健康检查)\n this.showLoadingState('🔑 正在测试百度OCR认证...');\n console.log('🔑 测试百度OCR认证...');\n \n // 清除旧的访问令牌,强制重新获取\n this.baiduOcrConfig.accessToken = '';\n this.baiduOcrConfig.tokenExpireTime = 0;\n \n const accessToken = await this.getBaiduOcrAccessToken();\n if (!accessToken) {\n throw new Error('❌ 获取访问令牌失败');\n }\n console.log('✅ 百度OCR认证成功,令牌长度:', accessToken.length);\n \n // 第三步:测试一个简单的OCR调用\n this.showLoadingState('🧪 正在测试OCR功能...');\n console.log('🧪 测试OCR功能可用性...');\n \n // 创建一个1x1像素的测试图片\n const testImageBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';\n \n const testResponse = await fetch('https://jumpy-foil-whimsey.glitch.me/api/baidu-ocr', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n access_token: accessToken,\n image: testImageBase64,\n language_type: 'CHN_ENG'\n })\n });\n \n if (testResponse.ok) {\n console.log('✅ OCR API调用成功');\n } else {\n console.log('⚠️ OCR API调用失败,但认证成功');\n }\n \n // 测试成功\n this.hideLoadingState();\n this.showMessage('🎉 百度OCR连接测试成功!认证正常,可以进行图片识别', 'success');\n console.log('🎉 连接测试完成,认证正常!');\n \n } catch (error) {\n this.hideLoadingState();\n console.error('❌ 连接测试失败:', error);\n \n // 提供更详细的错误信息\n let errorMessage = error.message;\n if (error.message.includes('CORS')) {\n errorMessage = '网络连接问题,但这不影响图片识别功能的使用';\n }\n \n this.showMessage('❌ 连接测试失败: ' + errorMessage, 'error');\n }\n}\n\n\n\n updateApiStatus() {\n const statusIndicator = document.getElementById('apiStatus');\n const statusText = document.getElementById('apiStatusText');\n \n if (this.apiKey && this.apiKey.trim() !== '') {\n if (statusIndicator) statusIndicator.classList.add('connected');\n if (statusText) statusText.textContent = '🤖 OCR+AI模式';\n } else {\n if (statusIndicator) statusIndicator.classList.remove('connected');\n if (statusText) statusText.textContent = '😴 未连接';\n }\n }\n\n saveBabyInfo() {\n this.babyInfo = {\n name: document.getElementById('babyName') ? document.getElementById('babyName').value : '',\n gender: document.getElementById('babyGender') ? document.getElementById('babyGender').value : '',\n birthDate: document.getElementById('babyBirthDate') ? document.getElementById('babyBirthDate').value : ''\n };\n \n this.saveData();\n console.log('宝宝信息已保存:', this.babyInfo);\n }\n\n clearAllRecords() {\n if (this.checkRecords.length === 0) {\n this.showMessage('🤔 没有记录需要清除', 'info');\n return;\n }\n \n if (confirm('🗑️ 确定要清除所有 ' + this.checkRecords.length + ' 条体检记录吗?此操作不可恢复!')) {\n this.checkRecords = [];\n this.saveData();\n this.updateRecordsTable();\n this.updateStatistics();\n this.updateCurrentStatus();\n this.updateChart();\n this.showMessage('✨ 所有记录已清除', 'success');\n }\n }\n\n async handleImageUpload(file) {\n if (!file) return;\n \n if (!this.validateBasicInfo()) {\n return;\n }\n \n if (this.isUploading) {\n this.showMessage('🤖 正在处理中,请稍候...', 'info');\n return;\n }\n \n if (!file.type.startsWith('image/')) {\n this.showMessage('🖼️ 请上传图片文件 (JPG, PNG, WEBP)', 'error');\n return;\n }\n \n if (file.size > 10 * 1024 * 1024) {\n this.showMessage('📦 图片文件太大,请选择小于10MB的图片', 'error');\n return;\n }\n \n if (!this.baiduOcrConfig.apiKey || !this.baiduOcrConfig.secretKey) {\n this.showMessage('🔧 请先配置百度OCR API密钥', 'error');\n return;\n }\n \n this.isUploading = true;\n \n try {\n this.showImagePreview(file);\n this.showLoadingState('🔍 正在初始化百度OCR识别...');\n const ocrResult = await this.performBaiduOCR(file);\n this.showOCRResult(ocrResult);\n this.hideLoadingState();\n this.showMessage('✨ OCR识别完成,请查看识别结果', 'success');\n } catch (error) {\n this.hideLoadingState();\n console.error('详细错误信息:', error);\n this.showMessage('❌ 识别失败: ' + error.message, 'error');\n } finally {\n this.isUploading = false;\n this.resetUploadInput();\n }\n }\n\n async getBaiduOcrAccessToken() {\n const now = Date.now();\n if (this.baiduOcrConfig.accessToken && now < this.baiduOcrConfig.tokenExpireTime) {\n return this.baiduOcrConfig.accessToken;\n }\n \n console.log('获取新的百度OCR访问令牌...');\n \n try {\n const response = await fetch('https://jumpy-foil-whimsey.glitch.me/api/baidu-token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n client_id: this.baiduOcrConfig.apiKey,\n client_secret: this.baiduOcrConfig.secretKey\n })\n });\n\n \n if (!response.ok) {\n throw new Error('获取访问令牌失败: ' + response.status);\n }\n \n const data = await response.json();\n \n if (data.error) {\n throw new Error('百度API错误: ' + (data.error_description || data.error));\n }\n \n this.baiduOcrConfig.accessToken = data.access_token;\n this.baiduOcrConfig.tokenExpireTime = now + (data.expires_in - 300) * 1000;\n \n localStorage.setItem('baidu_ocr_access_token', data.access_token);\n localStorage.setItem('baidu_ocr_token_expire', this.baiduOcrConfig.tokenExpireTime.toString());\n \n console.log('百度OCR访问令牌获取成功');\n return data.access_token;\n \n } catch (error) {\n console.error('获取百度OCR访问令牌失败:', error);\n throw new Error('获取访问令牌失败: ' + error.message);\n }\n }\n\n async fileToBase64(file) {\n return new Promise(function(resolve, reject) {\n const reader = new FileReader();\n reader.onload = function() {\n const base64 = reader.result.split(',')[1];\n resolve(base64);\n };\n reader.onerror = reject;\n reader.readAsDataURL(file);\n });\n }\n\n async performBaiduOCR(file) {\n try {\n console.log('开始百度OCR识别...文件大小:', file.size, '类型:', file.type);\n \n this.showLoadingState('🔑 正在获取访问令牌...');\n const accessToken = await this.getBaiduOcrAccessToken();\n \n this.showLoadingState('📷 正在处理图片...');\n const base64Image = await this.fileToBase64(file);\n \n this.showLoadingState('🔍 正在识别文字...');\n const response = await fetch('https://jumpy-foil-whimsey.glitch.me/api/baidu-ocr', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n access_token: accessToken,\n image: base64Image,\n language_type: 'CHN_ENG',\n detect_direction: 'true',\n paragraph: 'false',\n probability: 'false'\n })\n});\n\n \n if (!response.ok) {\n throw new Error('百度OCR API调用失败: ' + response.status);\n }\n \n const data = await response.json();\n \n if (data.error_code) {\n throw new Error('百度OCR错误 (' + data.error_code + '): ' + data.error_msg);\n }\n \n if (!data.words_result || data.words_result.length === 0) {\n throw new Error('未识别到任何文字,请确保图片清晰可读');\n }\n \n const recognizedText = data.words_result\n .map(function(item) { return item.words; })\n .join('\\n');\n \n console.log('百度OCR识别完成');\n console.log('识别到文字行数:', data.words_result.length);\n console.log('识别文本长度:', recognizedText.length);\n console.log('识别文本:', recognizedText);\n \n return recognizedText;\n \n } catch (error) {\n console.error('百度OCR识别失败:', error);\n throw new Error('图片文字识别失败: ' + error.message);\n }\n }\n\n showOCRResult(ocrText) {\n const self = this;\n this.lastOCRResult = ocrText;\n \n let resultDiv = document.getElementById('ocrResultDiv');\n \n if (!resultDiv) {\n resultDiv = document.createElement('div');\n resultDiv.id = 'ocrResultDiv';\n resultDiv.className = 'card cute-card';\n resultDiv.style.cssText = 'background: linear-gradient(135deg, #E5F3FF 0%, #F0FFE5 100%); border: 3px solid #4FACFE; margin: 20px 0; padding: 25px; border-radius: 20px; box-shadow: 0 8px 25px rgba(79, 172, 254, 0.3);';\n const uploadedImageDiv = document.getElementById('uploadedImage');\n if (uploadedImageDiv && uploadedImageDiv.parentNode) {\n uploadedImageDiv.parentNode.insertBefore(resultDiv, uploadedImageDiv.nextSibling);\n }\n }\n \n resultDiv.innerHTML = '
✨ 文字识别完成,接下来可以进行智能解析
' + ocrText + '
💡 可以编辑下方数据,确保准确性后再添加到记录中
🤔 未识别到有效记录
请检查图片是否清晰,或尝试手动添加记录
| 📅 体检日期 | 👶 月龄 | 📏 身高(cm) | ⚖️ 体重(kg) | 🧠 头围(cm) | 🛠️ 操作 |
|---|---|---|---|---|---|
请查看上方营养需求部分的完整建议
';\n }\n \n if (tipsContent) {\n tipsContent.innerHTML = sections.tips || '请查看上方营养需求部分的完整建议
';\n }\n }\n\n parseAIGuide(content) {\n const sections = {};\n \n const nutritionMatch = content.match(/(?:每日营养需求|### 1\\. 每日营养需求|1\\. 每日营养需求)[::]?\\s*([\\s\\S]*?)(?=(?:食物推荐|### 2\\. 食物推荐|2\\. 食物推荐|喂养技巧|### 3\\. 喂养技巧|3\\. 喂养技巧)|$)/);\n const foodMatch = content.match(/(?:食物推荐|### 2\\. 食物推荐|2\\. 食物推荐)[::]?\\s*([\\s\\S]*?)(?=(?:喂养技巧|### 3\\. 喂养技巧|3\\. 喂养技巧|每日营养|### 1\\. 每日营养需求|1\\. 每日营养需求)|$)/);\n const tipsMatch = content.match(/(?:喂养技巧|### 3\\. 喂养技巧|3\\. 喂养技巧)[::]?\\s*([\\s\\S]*?)(?=(?:每日营养|### 1\\. 每日营养需求|1\\. 每日营养需求|食物推荐|### 2\\. 食物推荐|2\\. 食物推荐)|$)/);\n \n if (nutritionMatch) {\n sections.nutrition = '📊 总记录数:' + this.checkRecords.length + '
🗓️ 最新记录:' + latestRecord.checkDate + '
👶 当前月龄:' + latestRecord.ageMonths + '个月
🤖 识别模式:百度OCR+AI增强
';\n }\n }\n\n deleteRecord(index) {\n if (confirm('🤔 确定要删除这条记录吗?')) {\n this.checkRecords.splice(index, 1);\n this.saveData();\n this.updateRecordsTable();\n this.updateStatistics();\n this.updateCurrentStatus();\n this.updateChart();\n this.showMessage('✅ 记录已删除', 'success');\n }\n }\n\n clearForm() {\n const inputs = ['checkDate', 'ageMonths', 'height', 'weight', 'headCircumference'];\n for (let i = 0; i < inputs.length; i++) {\n const el = document.getElementById(inputs[i]);\n if (el) el.value = '';\n }\n \n const percentileEls = ['heightPercentile', 'weightPercentile', 'headPercentile'];\n for (let i = 0; i < percentileEls.length; i++) {\n const el = document.getElementById(percentileEls[i]);\n if (el) el.textContent = '--';\n }\n \n const uploadedImageDiv = document.getElementById('uploadedImage');\n if (uploadedImageDiv) uploadedImageDiv.classList.add('hidden');\n \n this.hideOCRResult();\n this.resetUploadInput();\n }\n\n exportData() {\n const data = {\n babyInfo: this.babyInfo,\n checkRecords: this.checkRecords,\n exportDate: new Date().toISOString(),\n version: '10.0-BaiduOCR'\n };\n \n const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'baby_health_data_' + new Date().toISOString().split('T')[0] + '.json';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n \n this.showMessage('💾 数据导出成功', 'success');\n }\n\n importData() {\n const self = this;\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = '.json';\n input.onchange = function(e) {\n const file = e.target.files[0];\n if (file) {\n const reader = new FileReader();\n reader.onload = function(e) {\n try {\n const data = JSON.parse(e.target.result);\n if (data.checkRecords && Array.isArray(data.checkRecords)) {\n self.babyInfo = data.babyInfo || {};\n self.checkRecords = data.checkRecords || [];\n self.saveData();\n self.updateUI();\n self.loadSavedData();\n self.updateChart();\n self.showMessage('📥 数据导入成功', 'success');\n } else {\n self.showMessage('❌ 数据格式不正确', 'error');\n }\n } catch (error) {\n self.showMessage('⚠️ 数据格式错误,请检查文件', 'error');\n console.error('导入错误:', error);\n }\n };\n reader.readAsDataURL(file);\n }\n };\n input.click();\n }\n }\n\n // 页面加载完成后初始化\n document.addEventListener('DOMContentLoaded', function() {\n window.babyTracker = new BabyHealthTracker();\n console.log('🎀 婴幼儿健康追踪系统已初始化 (百度OCR版 v10.0)');\n \n // 绑定图表类型切换事件\n const chartTypeInputs = document.querySelectorAll('input[name=\"chartType\"]');\n for (let i = 0; i < chartTypeInputs.length; i++) {\n const input = chartTypeInputs[i];\n input.addEventListener('change', function() {\n if (window.babyTracker && window.babyTracker.updateChart) {\n window.babyTracker.updateChart();\n }\n });\n }\n \n // 绑定标签页切换功能\n const tabButtons = document.querySelectorAll('.tab-button');\n const tabContents = document.querySelectorAll('.tab-content');\n \n for (let i = 0; i < tabButtons.length; i++) {\n const button = tabButtons[i];\n button.addEventListener('click', function() {\n const targetTab = button.getAttribute('data-tab');\n \n // 移除所有活动状态\n for (let j = 0; j < tabButtons.length; j++) {\n tabButtons[j].classList.remove('active');\n }\n for (let j = 0; j < tabContents.length; j++) {\n tabContents[j].classList.remove('active');\n }\n \n // 激活当前标签页\n button.classList.add('active');\n const targetContent = document.getElementById(targetTab);\n if (targetContent) {\n targetContent.classList.add('active');\n }\n });\n }\n \n // 初始化第一个标签页为活动状态\n if (tabButtons.length > 0) {\n tabButtons[0].classList.add('active');\n const firstTabId = tabButtons[0].getAttribute('data-tab');\n const firstTabContent = document.getElementById(firstTabId);\n if (firstTabContent) {\n firstTabContent.classList.add('active');\n }\n }\n });\n\n})();\n"}}}}