據(jù)追蹤與可視化系統(tǒng):從SQLite到交互式大屏)
簡介一份面向慢性病管理場景的Web系統(tǒng)課程報告資源適合醫(yī)療信息化方向的學(xué)生或開發(fā)者借鑒完整設(shè)計與實現(xiàn)流程。壓縮包共包含3個文件分別為PDF報告、Markdown源文件與HTML可視化演示頁整體僅1.28MB便于閱讀、編輯與直接查看圖表效果。系統(tǒng)采用Flask、Pandas與PyEcharts分層架構(gòu)涵蓋血糖血壓數(shù)據(jù)采集、缺失值與異常值清洗、統(tǒng)計分析與基于規(guī)則的預(yù)警引擎例如連續(xù)三天空腹血糖高于7.0 mmol/L即觸發(fā)高血糖提醒同時通過趨勢折線圖和箱線圖展示健康變化。文檔按摘要、緒論、相關(guān)技術(shù)、需求分析、總體設(shè)計、詳細實現(xiàn)、系統(tǒng)測試與總結(jié)展望等章節(jié)組織并附帶實現(xiàn)指南可完整還原開發(fā)脈絡(luò)其中還涉及基于角色的訪問控制保障數(shù)據(jù)安全。資源已有63人學(xué)習(xí)可作為課程報告范例或慢性病管理系統(tǒng)開發(fā)的重要參考。1. 慢性病管理數(shù)據(jù)追蹤與可視化系統(tǒng)從課程報告到能演示的完整項目如果你正在為課程報告或畢設(shè)選題發(fā)愁這套基于 Python 的慢性病數(shù)據(jù)追蹤與可視化系統(tǒng)是我拆過最省心的方向之一。它不依賴復(fù)雜的硬件設(shè)備也不需要真實的醫(yī)院數(shù)據(jù)接口——用公開的模擬數(shù)據(jù)就能跑通全流程。系統(tǒng)核心是圍繞血壓、血糖、心率等關(guān)鍵指標(biāo)完成從數(shù)據(jù)采集、清洗、趨勢追蹤到可視化看板的閉環(huán)最終產(chǎn)出一份能直接在答辯現(xiàn)場演示的交互式大屏。很多學(xué)生卡在「數(shù)據(jù)有了但不知道怎么講故事」這一步而這個項目恰好補上了這個缺口。它會幫你建立一套完整的數(shù)據(jù)處理思維數(shù)據(jù)規(guī)范是根基、趨勢算法是中堅、可視化是門面。不管你未來是做數(shù)據(jù)分析還是后端開發(fā)這套流程都能復(fù)用。下面按我實際動手的順序把每個環(huán)節(jié)的參數(shù)、代碼和坑位都攤開講。2. 數(shù)據(jù)模型設(shè)計先定字段再談分析2.1 為什么選 SQLite 而不是 MySQL課程報告場景下我強烈建議用 SQLite 而不是 MySQL。原因很簡單SQLite 是文件型數(shù)據(jù)庫不需要單獨安裝服務(wù)代碼里連接一下就能用交作業(yè)時把.db文件一起打包就行。MySQL 需要配置賬號密碼、處理端口占用很多同學(xué)在環(huán)境搭建階段就勸退了。另一個理由是數(shù)據(jù)量。慢性病追蹤系統(tǒng)單機演示的數(shù)據(jù)量級基本在幾萬條以內(nèi)SQLite 的讀寫性能完全夠用。你在課程報告里寫「選用輕量級嵌入式數(shù)據(jù)庫降低部署復(fù)雜度」這句話在答辯時是加分項。2.2 三張核心表的結(jié)構(gòu)設(shè)計這套系統(tǒng)的數(shù)據(jù)模型我拆成三張表患者信息表、健康記錄表、用藥記錄表。健康記錄表是核心每行對應(yīng)一次測量關(guān)聯(lián)患者 ID 和記錄時間。-- patients: 患者基礎(chǔ)信息 CREATE TABLE patients ( patient_id TEXT PRIMARY KEY, name TEXT NOT NULL, age INTEGER, gender TEXT CHECK(gender IN (M, F)), diagnosis TEXT, created_at TEXT DEFAULT (datetime(now)) ); -- health_records: 核心健康指標(biāo)記錄 CREATE TABLE health_records ( record_id INTEGER PRIMARY KEY AUTOINCREMENT, patient_id TEXT NOT NULL, record_date TEXT NOT NULL, systolic INTEGER, diastolic INTEGER, heart_rate INTEGER, blood_glucose REAL, spo2 INTEGER, note TEXT, FOREIGN KEY (patient_id) REFERENCES patients(patient_id) ); -- medications: 用藥記錄 CREATE TABLE medications ( record_id INTEGER PRIMARY KEY AUTOINCREMENT, patient_id TEXT NOT NULL, med_name TEXT NOT NULL, dosage REAL, frequency TEXT, start_date TEXT, end_date TEXT, FOREIGN KEY (patient_id) REFERENCES patients(patient_id) );字段設(shè)計的核心理念是「寧寬勿窄」——額外的note字段用來記特殊情況比如「測量前運動了」「空腹血糖」這在你分析異常值時非常有幫助。spo2是血氧飽和度疫情期間很多慢病管理項目都加了這項如果你不需要可以留空但字段先占位。關(guān)鍵參數(shù)說明blood_glucose用REAL而不是INTEGER因為血糖值經(jīng)常出現(xiàn) 5.6、7.8 這樣的帶小數(shù)數(shù)值用整型會直接截斷數(shù)據(jù)就廢了。systolic和diastolic是收縮壓和舒張壓用INTEGER沒問題血壓計讀出來就是整數(shù)。2.3 索引查詢性能的隱形加速器數(shù)據(jù)量小的時候你可能感受不到索引的價值但當(dāng)你演示時篩選某個患者的全部記錄或者按日期范圍聚合數(shù)據(jù)沒有索引的表會全表掃描體感上會卡頓。CREATE INDEX idx_health_patient_date ON health_records(patient_id, record_date); CREATE INDEX idx_health_date ON health_records(record_date); CREATE INDEX idx_med_patient ON medications(patient_id);復(fù)合索引(patient_id, record_date)是最關(guān)鍵的查詢場景永遠是「先定位患者再按時間排序」。我把日期放第二列是因為實際查詢中過濾患者的頻率遠高于過濾日期這個順序能讓索引最大化命中。3. 數(shù)據(jù)清洗與追蹤邏輯臟數(shù)據(jù)是常態(tài)不是異常3.1 模擬數(shù)據(jù)生成的正確姿勢課程報告最怕的是「數(shù)據(jù)不夠分析深度」。我見過太多人只造了 30 條數(shù)據(jù)畫出來的折線圖毫無說服力。我的習(xí)慣是模擬生成至少 3 個患者、每人 60 天、每天 1-3 條記錄總量在 500 條以上這樣趨勢分析才有意義。import sqlite3 import random import pandas as pd from datetime import datetime, timedelta random.seed(42) conn sqlite3.connect(chronic.db) cursor conn.cursor() base_date datetime(2024, 1, 1) patients [ (P001, Zhang, 58, M, 糖尿病), (P002, Li, 62, F, 高血壓), (P003, Wang, 55, M, 冠心病) ] for pid, name, age, gender, diag in patients: cursor.execute( INSERT INTO patients VALUES (?,?,?,?,?, datetime(now)), (pid, name, age, gender, diag) ) # 每個患者回歸一個基準(zhǔn)血壓/血糖加隨機波動 base_sbp {P001: 125, P002: 145, P003: 135}[pid] base_dbp {P001: 78, P002: 92, P003: 85}[pid] base_glu {P001: 7.2, P002: 5.8, P003: 5.5}[pid] for day in range(60): d (base_date timedelta(daysday)).strftime(%Y-%m-%d) for _ in range(random.randint(1, 3)): sbp base_sbp random.randint(-8, 10) dbp base_dbp random.randint(-5, 6) hr random.randint(58, 92) glu round(base_glu random.uniform(-0.8, 1.2), 1) spo2 random.randint(94, 99) cursor.execute( INSERT INTO health_records (patient_id, record_date, systolic, diastolic, heart_rate, blood_glucose, spo2) VALUES (?,?,?,?,?,?,?), (pid, d, sbp, dbp, hr, glu, spo2) ) conn.commit()這段代碼的關(guān)鍵設(shè)計是「基準(zhǔn)值 隨機波動」模式。如果沒有基準(zhǔn)值血壓會在 80-180 之間完全隨機跳動畫出來的圖像波形一樣毫無規(guī)律可循加了基準(zhǔn)值后P002 的血壓穩(wěn)定在 145 附近波動P001 的血糖穩(wěn)定在 7.2 附近這樣才能讓追蹤和趨勢分析有素材。random.seed(42)這行非常關(guān)鍵加了種子后無論你運行多少次生成的數(shù)據(jù)完全一致。這意味著你的課程報告結(jié)果可復(fù)現(xiàn)導(dǎo)師問「你這個數(shù)據(jù)哪來的」時你可以當(dāng)場重新生成一遍給他看。3.2 空值與異常值的清理策略模擬數(shù)據(jù)不會自動產(chǎn)生空值但真實數(shù)據(jù)一定會。我建議在清洗代碼里故意構(gòu)造一些臟數(shù)據(jù)場景來演示你的清洗邏輯——這在答辯時是亮點。def clean_health_records(df): 清洗健康記錄處理空值、邏輯錯誤、異常范圍 df df.copy() # 1. 缺失值處理記錄日期與空腹血糖強相關(guān)日期缺失直接刪除 before len(df) df df.dropna(subset[record_date, patient_id]) print(f刪除日期/患者缺失記錄: {before} - {len(df)}) # 2. 血壓范圍檢查收縮壓 70-200舒張壓 40-120 df df[(df[systolic].between(70, 200)) (df[diastolic].between(40, 120))] # 3. 心率范圍 40-130超出視為測量誤差 df df[df[heart_rate].between(40, 130)] # 4. 血糖范圍 2.0-20.0 mmol/L df df[df[blood_glucose].between(2.0, 20.0)] # 5. 血氧 85-100低于 85 屬于嚴(yán)重異常保留但標(biāo)記 df[spo2_abnormal] df[spo2] 90 return df這里的邊界值不是隨便寫的。收縮壓下限 70 是臨床上嚴(yán)重低血壓的閾值低于這個值人已經(jīng)休克了血糖上限 20 是血糖儀的常見量程上限超過說明設(shè)備可能已經(jīng)測不準(zhǔn)了。這些閾值在課程報告里寫清楚依據(jù)是體現(xiàn)專業(yè)度的方式。我對缺失值的處理原則是「能留不留廢」日期和患者 ID 缺失的記錄直接刪因為這兩項沒法推斷數(shù)值字段缺失則用前向填充慢病數(shù)據(jù)具有連續(xù)性今天的血壓和昨天大概率接近。3.3 趨勢追蹤算法移動平均與變化率判定追蹤的「追」字核心在于判斷指標(biāo)在變好還是變差。單個點的波動毫無意義需要滑動窗口看趨勢。我用的方案是 7 日移動平均加上變化率閾值。def trend_analysis(df, window7, threshold5): 計算移動平均與趨勢方向 df df.sort_values(record_date) df[sbp_ma7] df[systolic].rolling(windowwindow, min_periods1).mean() # 變化率: (今日均值 - 窗口起始均值) / 窗口起始均值 * 100 df[change_pct] df[sbp_ma7].pct_change(periodswindow) * 100 # 趨勢判定: 變化率超過正向閾值 - 惡化; 低于負向閾值 - 好轉(zhuǎn) df[trend] stable df.loc[df[change_pct] threshold, trend] worsening df.loc[df[change_pct] -threshold, trend] improving return dfpct_change(periodswindow)計算的是「第 n 天的 7 日均值 相對 第 n-7 天的 7 日均值」的變化百分比。如果連續(xù)兩周血壓升高超過 5%系統(tǒng)標(biāo)記為「惡化」這個標(biāo)記會同步到可視化看板上變色圖塊。閾值 5% 不是拍腦袋定的。血壓 130 的 5% 就是 6.5mmHg而臨床指南通常認為收縮壓波動超過 10mmHg 才需要調(diào)整用藥我取半量作為預(yù)警線靈敏度更高。如果你處理的指標(biāo)是血糖閾值可以改到 10%因為血糖的日內(nèi)波動比血壓大得多。4. 可視化方案設(shè)計與實現(xiàn)讓數(shù)據(jù)替你把話說清楚4.1 圖表選型邏輯不是所有數(shù)據(jù)都配得上折線圖課程報告和答辯演示里最常見的扣分項是圖表選型錯誤。柱狀圖硬畫成折線圖、多指標(biāo)擠在一張圖里——這些問題一眼就能看出來。我按「時間序列、分布、組成、相關(guān)」四類場景來做選型決策血壓/血糖/心率隨時間變化折線圖加移動平均趨勢線各指標(biāo)分布區(qū)間箱線圖能看出中位數(shù)、四分位距和離群點一日內(nèi)多次測量的波動規(guī)律熱力圖縱軸是時間點橫軸是日期舒張壓與收縮壓相關(guān)性散點圖能直觀看到二者聯(lián)動這套選型邏輯各自獨立又能互相印證。折線圖講趨勢箱線圖講分布特征答辯時導(dǎo)師問「為什么這里用箱線圖」你回答「為了展示數(shù)據(jù)分布形態(tài)并識別離群點」這就是專業(yè)術(shù)語。4.2 pyecharts 交互式看板的代碼實現(xiàn)我在課程設(shè)計里選 pyecharts 而不是 matplotlib核心原因是交互性。matplotlib 輸出的是靜態(tài)圖而 pyecharts 渲染成 HTML鼠標(biāo)懸停能看到具體數(shù)值還能縮放時間范圍——答辯演示的觀感差距是碾壓性的。另一個原因是 pyecharts 圖表種類豐富地圖、儀表盤、熱力圖開箱即用。from pyecharts.charts import Line, Bar, Tab, Grid from pyecharts import options as opts import pandas as pd def build_dashboard(db_pathchronic.db): 構(gòu)建數(shù)據(jù)可視化看板 conn sqlite3.connect(db_path) df pd.read_sql_query( SELECT h.record_date, h.systolic, h.diastolic, h.blood_glucose, h.heart_rate, p.name, p.diagnosis FROM health_records h JOIN patients p ON h.patient_id p.patient_id ORDER BY h.record_date, conn ) # 按患者分組計算日均值 df[record_date] pd.to_datetime(df[record_date]) daily df.groupby([name, record_date])[[systolic, diastolic]].mean().reset_index() p001 daily[daily[name] Zhang] # 圖表1: 血壓趨勢 移動平均 line (Line() .add_xaxis(p001[record_date].dt.strftime(%m-%d).tolist()) .add_yaxis(收縮壓, p001[systolic].round(1).tolist(), is_smoothTrue, symbolcircle, symbol_size6) .add_yaxis(舒張壓, p001[diastolic].round(1).tolist(), is_smoothTrue, symbolcircle, symbol_size6) .set_global_opts( title_optsopts.TitleOpts(titleZhang 血壓趨勢追蹤), tooltip_optsopts.TooltipOpts(triggeraxis), datazoom_opts[opts.DataZoomOpts(range_start20, range_end100)], yaxis_optsopts.AxisOpts( namemmHg, min_70, max_200, splitline_optsopts.SplitLineOpts(is_showTrue) ) )) line.render(dashboard/templates/patient_trend.html) build_dashboard()代碼里的關(guān)鍵參數(shù)有三個is_smoothTrue讓折線平滑化避免鋸齒感觀感更專業(yè)datazoom_opts加了一個拖動條允許查看任意時段細節(jié)splitline_opts加了橫向網(wǎng)格線方便讀取具體數(shù)值。我提到的Grid和Tab是 pyecharts 里的布局容器。單個 HTML 里放多個圖表用Grid做上下分欄多個頁面切換用Tab做標(biāo)簽頁。課程設(shè)計中我做的是一個Tab總頁面每個患者一個標(biāo)簽頁點一下就能切換演示效果非常好。4.3 大屏布局的 CSS 網(wǎng)格方案如果內(nèi)容是「數(shù)據(jù)可視化大屏」就涉及布局問題。pyecharts 本身只負責(zé)生成單個圖表 HTML多個圖表拼成一個大屏需要用 HTML 模板做網(wǎng)格布局。!DOCTYPE html html head meta charsetutf-8 style .grid-container { display: grid; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 300px 300px; gap: 12px; padding: 12px; background: #0f1420; } .grid-item { background: #1a2130; border-radius: 8px; padding: 8px; border: 1px solid #2a3441; } .grid-item iframe { width: 100%; height: 100%; border: none; } .header { color: #e6e9ef; text-align: center; font-size: 22px; padding: 16px; background: #1a2130; font-weight: 600; } /style /head body div classheader慢性病管理數(shù)據(jù)追蹤看板/div div classgrid-container div classgrid-item iframe srcpatient_trend.html/iframe /div !-- 其他圖表 iframe 嵌入 -- /div /body /htmliframe 嵌入方案是我總結(jié)出來的最優(yōu)解。pyecharts 的 render API 本身就是生成完整 HTML你直接在網(wǎng)格的每個單元格里嵌 iframe 指向?qū)?yīng) HTML 文件不用手動改 pyecharts 模板源碼——那個模板改起來又長又容易出錯。顏色配比上深色背景配亮色圖表這是數(shù)據(jù)可視化大屏的通用做法能讓圖表的主體色彩更突出。后臺開著python app.py瀏覽器打開本地服務(wù)地址是一個完整系統(tǒng)該有的樣子。5. 數(shù)據(jù)追蹤與可視化系統(tǒng)的避坑指南五個高頻翻車點5.1 中文亂碼與字體缺失圖表標(biāo)題變方塊現(xiàn)象圖表標(biāo)題、軸標(biāo)簽里的中文全部顯示為方塊或問號導(dǎo)出的 HTML 在答辯電腦上也一樣。原因pyecharts 渲染時默認用的字體不含中文字形或者操作系統(tǒng)沒安裝對應(yīng)的中文字體。在未安裝中文字體的 Linux 服務(wù)器上尤其常見學(xué)校實驗室的電腦有些精簡系統(tǒng)也有這個問題。解決在圖表配置里顯式指定中文字體二選一即可。一是本地安裝字體后配置opts.TitleOpts(title血壓趨勢, title_textstyle_optsopts.TextStyleOpts(font_familyMicrosoft YaHei))二是確保系統(tǒng)有可用中文字體。我在代碼里統(tǒng)一用font_familyMicrosoft YaHeiWindows 和 macOS 都有這個字體是兼容性最好的方案。5.2 SQLite 時間用文本存儲日期排序錯亂現(xiàn)象ORDER BY record_date排序后數(shù)據(jù)亂序比如 2024-02-01 排到了 2024-01-31 前面或者同一天的數(shù)據(jù)順序不對。原因如果你把日期存成了TEXT類型并且格式不統(tǒng)一有的是2024-1-1有的是2024-01-01文本排序按字符逐個比較2024-1-1會排在2024-01-02后面。解決所有日期統(tǒng)一用YYYY-MM-DD格式存儲零填充是必須的。我在建表時用datetime(now)生成的默認值天然就是標(biāo)準(zhǔn)格式但插入模擬數(shù)據(jù)時用strftime(%Y-%m-%d)顯式格式化日期確保格式一致。5.3 pandas 讀入 SQLite 后日期自動多 8 小時現(xiàn)象讀取數(shù)據(jù)后record_date從2024-01-01變成了2024-01-01 08:00:00做時間序列分析時按日聚合會出錯。原因SQLite 的 TEXT 日期被 pandas 識別后自動推斷為 datetime64默認按本地時區(qū)東八區(qū)解析。由于 TEXT 里沒有時區(qū)信息pandas 會補一個默認 UTC 的假定時區(qū)再轉(zhuǎn)換導(dǎo)致偏移。解決讀取 SQL 時不用 pandas 自動推斷而是手動指定解析格式df pd.read_sql_query(sql, conn) df[record_date] pd.to_datetime(df[record_date], format%Y-%m-%d %H:%M:%S)這里顯式告訴 pandas 日期字符串的完整格式它就不會自作主張補時區(qū)。如果你在 pandas 2.0 以上版本遇到報錯可以加errorscoerce把無法解析的日期置為 NaT用完再 dropna。5.4 滾動平均出現(xiàn) NaN圖表前半段空白現(xiàn)象計算 7 日移動平均后前 6 天的數(shù)值是 NaN折線圖上開頭一段是空的看起來像數(shù)據(jù)缺損。原因rolling(window7)默認要求窗口內(nèi)有 7 個完整數(shù)據(jù)點才計算。某天有 1 次測量、有時 3 次如果某天缺失記錄窗口湊不齊 7 個點就會一直 NaN。解決rolling有三個方案。其一用min_periods1我在前面代碼里用的窗口內(nèi)有 1 個數(shù)據(jù)就算均值其二用min_periods3至少 3 個點參與計算平衡穩(wěn)定性和空值其三先用resample(D).mean()把數(shù)據(jù)重采樣成按天的規(guī)整序列再滾動平均。我實際用的是第三種因為重采樣后數(shù)據(jù)結(jié)構(gòu)更規(guī)整后續(xù)其他分析也方便。5.5 pyecharts 圖表在 Jupyter 里不顯示但 HTML 文件正?,F(xiàn)象同樣的代碼render(x.html)能正常打開但寫在 Jupyter Notebook 里運行時圖表區(qū)域空白或者只有一行提示文字。原因pyecharts 在 Jupyter 里走的是內(nèi)聯(lián)渲染inline render需要加載相應(yīng)的 JS 資源如果你的網(wǎng)絡(luò)被限制、無法訪問 CDN 資源圖表就放不出來。解決換render_notebook()無效就先排查網(wǎng)絡(luò)。更穩(wěn)妥的方案是調(diào)整資源加載方式from pyecharts.globals import CurrentConfig CurrentConfig.ONLINE_HOST https://cdn.jsdelivr.net/npm/echarts5/dist/把遠程資源指向 jsdelivr它比默認的 bootcdn 穩(wěn)定性更好。如果對離線環(huán)境有要求就把echarts.min.js下載到本地設(shè)置CurrentConfig.ONLINE_HOST ./static/js/。我最后是用 HTML 展示而不是 Jupyter省掉所有依賴問題。6. 進階把分析流程裝進 Flask讓系統(tǒng)變成可演示的產(chǎn)品前面所有分析都是腳本級別的但課程設(shè)計答辯時腳本和系統(tǒng)的區(qū)別很直觀。我給你一套把追蹤和可視化整合到 Flask 服務(wù)的方案讓整個系統(tǒng)可以交互式使用。6.1 核心服務(wù)模塊與路由設(shè)計from flask import Flask, render_template, jsonify, request import sqlite3 import pandas as pd app Flask(__name__) DB_PATH chronic.db def get_records(patient_idNone, startNone, endNone): 查詢健康記錄支持患者和時間范圍過濾 conn sqlite3.connect(DB_PATH) sql SELECT * FROM health_records WHERE 11 params [] if patient_id: sql AND patient_id ? params.append(patient_id) if start: sql AND record_date ? params.append(start) if end: sql AND record_date ? params.append(end) sql ORDER BY record_date df pd.read_sql_query(sql, conn, paramsparams) conn.close() return df app.route(/) def index(): 看板首頁渲染大屏模板 return render_template(dashboard.html) app.route(/api/trend/patient_id) def api_trend(patient_id): 趨勢數(shù)據(jù)接口返回患者時間序列 df get_records(patient_id) df trend_analysis(df) return jsonify({ dates: df[record_date].tolist(), systolic: df[systolic].tolist(), sbp_ma7: df[sbp_ma7].round(2).tolist(), trend: df[trend].tolist() }) if __name__ __main__: app.run(debugTrue, port5000)這段代碼把前面所有分析邏輯串成 HTTP 接口。/api/trend/patient_id接口接受患者 ID 參數(shù)返回該患者的血壓時間序列和趨勢判定前端可以動態(tài)渲染圖表。WHERE 11這種寫法是動態(tài)構(gòu)建 SQL 的經(jīng)典技巧——先寫一個恒真條件后面拼接的過濾條件都用AND連接省去了判斷「是否第一個條件」的麻煩。雖然有點 hack但在課程設(shè)計場景下完全沒問題也不會造成性能消耗。6.2 動態(tài)傳參的圖表渲染方法接口就緒后前端圖表用 JavaScript 發(fā)請求拿數(shù)據(jù)再喂給 ECharts而不是像之前那樣在 Python 端寫死數(shù)據(jù)。async function loadTrend(patientId) { const res await fetch(/api/trend/${patientId}); const data await res.json(); const chart echarts.init(document.getElementById(trend-chart)); chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.dates }, yAxis: { type: value, name: mmHg, min: 70, max: 200 }, series: [ { name: 收縮壓, type: line, data: data.systolic, smooth: true }, { name: 7日均值, type: line, data: data.sbp_ma7, smooth: true, lineStyle: { type: dashed } } ] }); }前端用fetch請求后端接口拿到 JSON 后setOption動態(tài)渲染。這套前后端分離的寫法比單純在 Python 端生成靜態(tài)圖表高一個層次——它意味著系統(tǒng)真的具有了「追數(shù)據(jù)」的能力新增一條記錄后刷新頁面就能看到最新追蹤結(jié)果。我后來給學(xué)生改課程設(shè)計時每次都會讓頁面篩選器加患者下拉框而不是一個患者一個頁面。一來代碼重復(fù)度低二來答辯時可以直接演示系統(tǒng)響應(yīng)。從那以后我每寫一個可視化項目都強制把數(shù)據(jù)層和展示層拆開——先定義接口再做頁面這套流程希望幫到你。本文還有配套的精品資源點擊獲取