858 lines
38 KiB
HTML
858 lines
38 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>CardioAI - 心血管疾病风险预测</title>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<style>
|
||
:root {
|
||
--primary-color: #e63946;
|
||
--secondary-color: #457b9d;
|
||
--success-color: #2a9d8f;
|
||
--warning-color: #e9c46a;
|
||
--danger-color: #e63946;
|
||
--light-color: #f1faee;
|
||
--dark-color: #1d3557;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif;
|
||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||
min-height: 100vh;
|
||
padding-bottom: 50px;
|
||
}
|
||
|
||
.navbar {
|
||
background: linear-gradient(to right, var(--dark-color), var(--secondary-color));
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.navbar-brand {
|
||
font-weight: bold;
|
||
font-size: 1.5rem;
|
||
color: white !important;
|
||
}
|
||
|
||
.card {
|
||
border: none;
|
||
border-radius: 15px;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||
transition: transform 0.3s ease;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.card:hover {
|
||
transform: translateY(-5px);
|
||
}
|
||
|
||
.card-header {
|
||
background: linear-gradient(to right, var(--secondary-color), var(--dark-color));
|
||
color: white;
|
||
border-radius: 15px 15px 0 0 !important;
|
||
font-weight: bold;
|
||
padding: 15px 20px;
|
||
}
|
||
|
||
.form-control, .form-select {
|
||
border-radius: 8px;
|
||
border: 1px solid #ddd;
|
||
padding: 10px 15px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.form-control:focus, .form-select:focus {
|
||
border-color: var(--secondary-color);
|
||
box-shadow: 0 0 0 0.25rem rgba(69, 123, 157, 0.25);
|
||
}
|
||
|
||
.btn-primary {
|
||
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 12px 30px;
|
||
font-weight: bold;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 5px 15px rgba(230, 57, 70, 0.3);
|
||
}
|
||
|
||
.btn-secondary {
|
||
background: linear-gradient(to right, var(--dark-color), #2c3e50);
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 12px 30px;
|
||
font-weight: bold;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 5px 15px rgba(29, 53, 87, 0.3);
|
||
}
|
||
|
||
.result-card {
|
||
border-left: 5px solid var(--secondary-color);
|
||
}
|
||
|
||
.risk-low {
|
||
color: var(--success-color);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.risk-medium {
|
||
color: var(--warning-color);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.risk-high {
|
||
color: var(--danger-color);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.feature-value {
|
||
background-color: var(--light-color);
|
||
padding: 5px 10px;
|
||
border-radius: 5px;
|
||
font-family: monospace;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.loading {
|
||
display: none;
|
||
text-align: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.spinner {
|
||
width: 3rem;
|
||
height: 3rem;
|
||
border-width: 0.3em;
|
||
}
|
||
|
||
.alert {
|
||
border-radius: 10px;
|
||
border: none;
|
||
}
|
||
|
||
.feature-group {
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.feature-label {
|
||
font-weight: 600;
|
||
margin-bottom: 5px;
|
||
color: var(--dark-color);
|
||
}
|
||
|
||
.help-text {
|
||
font-size: 0.85rem;
|
||
color: #6c757d;
|
||
margin-top: 3px;
|
||
}
|
||
|
||
footer {
|
||
background-color: var(--dark-color);
|
||
color: white;
|
||
padding: 20px 0;
|
||
margin-top: 40px;
|
||
border-radius: 15px 15px 0 0;
|
||
}
|
||
|
||
.heart-icon {
|
||
color: var(--primary-color);
|
||
animation: heartbeat 1.5s infinite;
|
||
}
|
||
|
||
@keyframes heartbeat {
|
||
0% { transform: scale(1); }
|
||
5% { transform: scale(1.1); }
|
||
10% { transform: scale(1); }
|
||
15% { transform: scale(1.1); }
|
||
20% { transform: scale(1); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
|
||
.tooltip-inner {
|
||
max-width: 300px;
|
||
text-align: left;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 导航栏 -->
|
||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||
<div class="container">
|
||
<a class="navbar-brand" href="#">
|
||
<i class="fas fa-heartbeat me-2 heart-icon"></i>
|
||
CardioAI - 心血管疾病风险预测系统
|
||
</a>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="navbarNav">
|
||
<ul class="navbar-nav ms-auto">
|
||
<li class="nav-item">
|
||
<a class="nav-link active" href="#"><i class="fas fa-home me-1"></i> 首页</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/health" target="_blank"><i class="fas fa-heart me-1"></i> 服务状态</a>
|
||
</li>
|
||
<li class="nav-item">
|
||
<a class="nav-link" href="/model_info" target="_blank"><i class="fas fa-info-circle me-1"></i> 模型信息</a>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="container mt-4">
|
||
<div class="row">
|
||
<!-- 左侧:输入表单 -->
|
||
<div class="col-lg-6">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<i class="fas fa-clipboard-list me-2"></i> 患者基本信息输入
|
||
</div>
|
||
<div class="card-body">
|
||
<form id="predictionForm">
|
||
<!-- 年龄 -->
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="age">
|
||
<i class="fas fa-birthday-cake me-1"></i> 年龄(天)
|
||
</label>
|
||
<input type="number" class="form-control" id="age" name="age"
|
||
placeholder="请输入年龄(天数)" min="0" max="36500" required
|
||
data-bs-toggle="tooltip" data-bs-placement="top"
|
||
title="输入年龄,单位为天。例如:55岁 = 55 × 365 = 20075天">
|
||
<div class="help-text">示例:55岁 ≈ 20075天</div>
|
||
</div>
|
||
|
||
<!-- 性别 -->
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="gender">
|
||
<i class="fas fa-venus-mars me-1"></i> 性别
|
||
</label>
|
||
<select class="form-select" id="gender" name="gender" required>
|
||
<option value="">请选择性别</option>
|
||
<option value="1">女性</option>
|
||
<option value="2">男性</option>
|
||
</select>
|
||
<div class="help-text">1=女性,2=男性</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<!-- 身高 -->
|
||
<div class="col-md-6">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="height">
|
||
<i class="fas fa-ruler-vertical me-1"></i> 身高(cm)
|
||
</label>
|
||
<input type="number" class="form-control" id="height" name="height"
|
||
placeholder="身高(厘米)" min="100" max="250" required>
|
||
<div class="help-text">范围:100-250 cm</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 体重 -->
|
||
<div class="col-md-6">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="weight">
|
||
<i class="fas fa-weight me-1"></i> 体重(kg)
|
||
</label>
|
||
<input type="number" class="form-control" id="weight" name="weight"
|
||
placeholder="体重(千克)" min="20" max="300" required>
|
||
<div class="help-text">范围:20-300 kg</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<!-- 收缩压 -->
|
||
<div class="col-md-6">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="ap_hi">
|
||
<i class="fas fa-tachometer-alt me-1"></i> 收缩压(mmHg)
|
||
</label>
|
||
<input type="number" class="form-control" id="ap_hi" name="ap_hi"
|
||
placeholder="收缩压" min="50" max="300" required>
|
||
<div class="help-text">范围:50-300 mmHg</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 舒张压 -->
|
||
<div class="col-md-6">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="ap_lo">
|
||
<i class="fas fa-tachometer-alt me-1"></i> 舒张压(mmHg)
|
||
</label>
|
||
<input type="number" class="form-control" id="ap_lo" name="ap_lo"
|
||
placeholder="舒张压" min="30" max="200" required>
|
||
<div class="help-text">范围:30-200 mmHg</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 胆固醇水平 -->
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="cholesterol">
|
||
<i class="fas fa-vial me-1"></i> 胆固醇水平
|
||
</label>
|
||
<select class="form-select" id="cholesterol" name="cholesterol" required>
|
||
<option value="">请选择胆固醇水平</option>
|
||
<option value="1">正常</option>
|
||
<option value="2">高于正常</option>
|
||
<option value="3">极高</option>
|
||
</select>
|
||
<div class="help-text">1=正常,2=高于正常,3=极高</div>
|
||
</div>
|
||
|
||
<!-- 血糖水平 -->
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="gluc">
|
||
<i class="fas fa-vial me-1"></i> 血糖水平
|
||
</label>
|
||
<select class="form-select" id="gluc" name="gluc" required>
|
||
<option value="">请选择血糖水平</option>
|
||
<option value="1">正常</option>
|
||
<option value="2">高于正常</option>
|
||
<option value="3">极高</option>
|
||
</select>
|
||
<div class="help-text">1=正常,2=高于正常,3=极高</div>
|
||
</div>
|
||
|
||
<!-- 生活方式 -->
|
||
<div class="row">
|
||
<div class="col-md-4">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="smoke">
|
||
<i class="fas fa-smoking me-1"></i> 吸烟
|
||
</label>
|
||
<select class="form-select" id="smoke" name="smoke" required>
|
||
<option value="0">否</option>
|
||
<option value="1">是</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="alco">
|
||
<i class="fas fa-wine-glass-alt me-1"></i> 饮酒
|
||
</label>
|
||
<select class="form-select" id="alco" name="alco" required>
|
||
<option value="0">否</option>
|
||
<option value="1">是</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<div class="feature-group">
|
||
<label class="feature-label" for="active">
|
||
<i class="fas fa-running me-1"></i> 体育活动
|
||
</label>
|
||
<select class="form-select" id="active" name="active" required>
|
||
<option value="0">否</option>
|
||
<option value="1">是</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 按钮组 -->
|
||
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
|
||
<button type="button" class="btn btn-secondary me-md-2" id="btnReset">
|
||
<i class="fas fa-redo me-1"></i> 重置表单
|
||
</button>
|
||
<button type="submit" class="btn btn-primary" id="btnPredict">
|
||
<i class="fas fa-stethoscope me-1"></i> 开始预测
|
||
</button>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- 加载动画 -->
|
||
<div class="loading mt-4" id="loading">
|
||
<div class="spinner-border text-primary spinner" role="status">
|
||
<span class="visually-hidden">加载中...</span>
|
||
</div>
|
||
<p class="mt-3">正在分析数据,请稍候...</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 示例数据卡片 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<i class="fas fa-lightbulb me-2"></i> 示例数据
|
||
</div>
|
||
<div class="card-body">
|
||
<p class="card-text">点击下方按钮填充示例数据:</p>
|
||
<div class="d-grid gap-2">
|
||
<button type="button" class="btn btn-outline-primary" id="btnExampleLow">
|
||
<i class="fas fa-user-check me-1"></i> 低风险示例
|
||
</button>
|
||
<button type="button" class="btn btn-outline-warning" id="btnExampleMedium">
|
||
<i class="fas fa-user me-1"></i> 中风险示例
|
||
</button>
|
||
<button type="button" class="btn btn-outline-danger" id="btnExampleHigh">
|
||
<i class="fas fa-user-injured me-1"></i> 高风险示例
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧:结果显示 -->
|
||
<div class="col-lg-6">
|
||
<div class="card result-card">
|
||
<div class="card-header">
|
||
<i class="fas fa-chart-line me-2"></i> 预测结果分析
|
||
</div>
|
||
<div class="card-body">
|
||
<div id="resultPlaceholder" class="text-center">
|
||
<i class="fas fa-chart-bar fa-4x text-muted mb-3"></i>
|
||
<h5 class="text-muted">等待预测结果</h5>
|
||
<p class="text-muted">填写左侧表单并点击"开始预测"按钮,系统将分析您的心血管疾病风险。</p>
|
||
</div>
|
||
|
||
<div id="resultContent" style="display: none;">
|
||
<!-- 风险等级 -->
|
||
<div class="alert" id="riskAlert">
|
||
<h4 class="alert-heading" id="riskTitle"></h4>
|
||
<p id="riskDescription"></p>
|
||
<hr>
|
||
<p class="mb-0" id="riskRecommendation"></p>
|
||
</div>
|
||
|
||
<!-- 预测结果详情 -->
|
||
<div class="mt-4">
|
||
<h5><i class="fas fa-info-circle me-2"></i> 预测详情</h5>
|
||
<table class="table table-borderless">
|
||
<tr>
|
||
<th width="40%">预测结果:</th>
|
||
<td><span class="badge bg-primary" id="predictionResult"></span></td>
|
||
</tr>
|
||
<tr>
|
||
<th>患病概率:</th>
|
||
<td><span class="feature-value" id="probabilityValue"></span></td>
|
||
</tr>
|
||
<tr>
|
||
<th>风险等级:</th>
|
||
<td><span id="riskLevel"></span></td>
|
||
</tr>
|
||
<tr>
|
||
<th>处理后的年龄:</th>
|
||
<td><span class="feature-value" id="ageYears"></span> 岁</td>
|
||
</tr>
|
||
<tr>
|
||
<th>身体质量指数(BMI):</th>
|
||
<td><span class="feature-value" id="bmiValue"></span></td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 特征总结 -->
|
||
<div class="mt-4">
|
||
<h5><i class="fas fa-list-ul me-2"></i> 输入特征总结</h5>
|
||
<div class="row" id="featureSummary">
|
||
<!-- 特征将通过JavaScript动态填充 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 行动建议 -->
|
||
<div class="alert alert-info mt-4">
|
||
<h5><i class="fas fa-hands-helping me-2"></i> 健康建议</h5>
|
||
<ul id="healthAdvice">
|
||
<!-- 建议将通过JavaScript动态填充 -->
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 系统信息 -->
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<i class="fas fa-cogs me-2"></i> 系统信息
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<p><strong><i class="fas fa-server me-2"></i> 服务状态:</strong>
|
||
<span class="badge bg-success" id="serviceStatus">正常</span>
|
||
</p>
|
||
<p><strong><i class="fas fa-brain me-2"></i> 预测模型:</strong>
|
||
<span id="modelName">CardioAI XGBoost</span>
|
||
</p>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<p><strong><i class="fas fa-history me-2"></i> 响应时间:</strong>
|
||
<span id="responseTime">--</span> ms
|
||
</p>
|
||
<p><strong><i class="fas fa-calendar-alt me-2"></i> 最后更新:</strong>
|
||
<span id="lastUpdate">2024-04-02</span>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-2">
|
||
<button class="btn btn-sm btn-outline-secondary" id="btnRefreshStatus">
|
||
<i class="fas fa-sync-alt me-1"></i> 刷新状态
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 页脚 -->
|
||
<footer>
|
||
<div class="container text-center">
|
||
<p class="mb-2">
|
||
<i class="fas fa-heartbeat me-2 heart-icon"></i>
|
||
CardioAI - 心血管疾病智能辅助系统 v1.0
|
||
</p>
|
||
<p class="small mb-0">
|
||
本系统基于机器学习模型提供风险评估,结果仅供参考,不能替代专业医疗诊断。
|
||
<br>
|
||
如有健康问题,请及时咨询专业医生。
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
|
||
<!-- Bootstrap JavaScript -->
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||
|
||
<!-- 自定义JavaScript -->
|
||
<script>
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 初始化工具提示
|
||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||
});
|
||
|
||
// 检查服务状态
|
||
checkServiceStatus();
|
||
|
||
// 绑定事件
|
||
document.getElementById('predictionForm').addEventListener('submit', handlePrediction);
|
||
document.getElementById('btnReset').addEventListener('click', resetForm);
|
||
document.getElementById('btnRefreshStatus').addEventListener('click', checkServiceStatus);
|
||
|
||
// 示例数据按钮
|
||
document.getElementById('btnExampleLow').addEventListener('click', () => fillExampleData('low'));
|
||
document.getElementById('btnExampleMedium').addEventListener('click', () => fillExampleData('medium'));
|
||
document.getElementById('btnExampleHigh').addEventListener('click', () => fillExampleData('high'));
|
||
|
||
// 初始填充低风险示例
|
||
setTimeout(() => fillExampleData('low'), 500);
|
||
});
|
||
|
||
// 检查服务状态
|
||
async function checkServiceStatus() {
|
||
try {
|
||
const response = await fetch('/health');
|
||
const data = await response.json();
|
||
|
||
if (data.status === 'healthy') {
|
||
document.getElementById('serviceStatus').className = 'badge bg-success';
|
||
document.getElementById('serviceStatus').textContent = '正常';
|
||
document.getElementById('modelName').textContent = data.model_version || 'CardioAI XGBoost';
|
||
} else {
|
||
document.getElementById('serviceStatus').className = 'badge bg-danger';
|
||
document.getElementById('serviceStatus').textContent = '异常';
|
||
}
|
||
} catch (error) {
|
||
console.error('服务状态检查失败:', error);
|
||
document.getElementById('serviceStatus').className = 'badge bg-danger';
|
||
document.getElementById('serviceStatus').textContent = '连接失败';
|
||
}
|
||
}
|
||
|
||
// 处理预测表单提交
|
||
async function handlePrediction(event) {
|
||
event.preventDefault();
|
||
|
||
// 显示加载动画
|
||
document.getElementById('loading').style.display = 'block';
|
||
document.getElementById('btnPredict').disabled = true;
|
||
|
||
// 收集表单数据
|
||
const formData = {
|
||
age: parseInt(document.getElementById('age').value),
|
||
gender: parseInt(document.getElementById('gender').value),
|
||
height: parseInt(document.getElementById('height').value),
|
||
weight: parseInt(document.getElementById('weight').value),
|
||
ap_hi: parseInt(document.getElementById('ap_hi').value),
|
||
ap_lo: parseInt(document.getElementById('ap_lo').value),
|
||
cholesterol: parseInt(document.getElementById('cholesterol').value),
|
||
gluc: parseInt(document.getElementById('gluc').value),
|
||
smoke: parseInt(document.getElementById('smoke').value),
|
||
alco: parseInt(document.getElementById('alco').value),
|
||
active: parseInt(document.getElementById('active').value)
|
||
};
|
||
|
||
// 验证血压
|
||
if (formData.ap_lo >= formData.ap_hi) {
|
||
alert('错误:舒张压不能高于或等于收缩压');
|
||
document.getElementById('loading').style.display = 'none';
|
||
document.getElementById('btnPredict').disabled = false;
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const startTime = Date.now();
|
||
|
||
// 发送预测请求
|
||
const response = await fetch('/predict_cardio', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(formData)
|
||
});
|
||
|
||
const responseTime = Date.now() - startTime;
|
||
document.getElementById('responseTime').textContent = responseTime;
|
||
|
||
const data = await response.json();
|
||
|
||
// 隐藏加载动画
|
||
document.getElementById('loading').style.display = 'none';
|
||
document.getElementById('btnPredict').disabled = false;
|
||
|
||
if (data.success) {
|
||
// 显示结果
|
||
displayPredictionResult(data);
|
||
} else {
|
||
alert('预测失败:' + data.message);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('预测请求失败:', error);
|
||
document.getElementById('loading').style.display = 'none';
|
||
document.getElementById('btnPredict').disabled = false;
|
||
alert('网络请求失败,请检查服务器状态');
|
||
}
|
||
}
|
||
|
||
// 显示预测结果
|
||
function displayPredictionResult(data) {
|
||
// 隐藏占位符,显示结果内容
|
||
document.getElementById('resultPlaceholder').style.display = 'none';
|
||
document.getElementById('resultContent').style.display = 'block';
|
||
|
||
// 更新预测结果
|
||
const predictionText = data.prediction === 1 ? '有心血管疾病风险' : '无心血管疾病风险';
|
||
document.getElementById('predictionResult').textContent = predictionText;
|
||
|
||
// 更新概率
|
||
const probabilityPercent = (data.probability * 100).toFixed(1);
|
||
document.getElementById('probabilityValue').textContent = `${probabilityPercent}%`;
|
||
|
||
// 更新风险等级
|
||
let riskClass = '';
|
||
let riskIcon = '';
|
||
if (data.risk_level === '低危') {
|
||
riskClass = 'risk-low';
|
||
riskIcon = 'fa-smile';
|
||
} else if (data.risk_level === '中危') {
|
||
riskClass = 'risk-medium';
|
||
riskIcon = 'fa-meh';
|
||
} else {
|
||
riskClass = 'risk-high';
|
||
riskIcon = 'fa-frown';
|
||
}
|
||
|
||
document.getElementById('riskLevel').innerHTML =
|
||
`<i class="fas ${riskIcon} me-1"></i><span class="${riskClass}">${data.risk_level}</span>`;
|
||
|
||
// 更新风险警告框
|
||
const alertElement = document.getElementById('riskAlert');
|
||
if (data.risk_level === '低危') {
|
||
alertElement.className = 'alert alert-success';
|
||
alertElement.innerHTML = `
|
||
<h4 class="alert-heading"><i class="fas fa-thumbs-up me-2"></i> 低风险</h4>
|
||
<p>根据模型分析,您当前的心血管疾病风险较低。继续保持健康的生活方式!</p>
|
||
<hr>
|
||
<p class="mb-0">建议定期进行健康检查,维持当前的健康状态。</p>
|
||
`;
|
||
} else if (data.risk_level === '中危') {
|
||
alertElement.className = 'alert alert-warning';
|
||
alertElement.innerHTML = `
|
||
<h4 class="alert-heading"><i class="fas fa-exclamation-triangle me-2"></i> 中风险</h4>
|
||
<p>根据模型分析,您有一定的心血管疾病风险,建议关注相关健康指标。</p>
|
||
<hr>
|
||
<p class="mb-0">建议改善生活方式,并考虑进行更详细的医学检查。</p>
|
||
`;
|
||
} else {
|
||
alertElement.className = 'alert alert-danger';
|
||
alertElement.innerHTML = `
|
||
<h4 class="alert-heading"><i class="fas fa-exclamation-circle me-2"></i> 高风险</h4>
|
||
<p>根据模型分析,您的心血管疾病风险较高,建议尽快咨询专业医生。</p>
|
||
<hr>
|
||
<p class="mb-0">请及时就医,进行全面的心血管健康评估。</p>
|
||
`;
|
||
}
|
||
|
||
// 更新处理后的特征
|
||
document.getElementById('ageYears').textContent = data.features.age_years;
|
||
document.getElementById('bmiValue').textContent = data.features.bmi;
|
||
|
||
// 更新特征总结
|
||
const featureSummary = document.getElementById('featureSummary');
|
||
featureSummary.innerHTML = `
|
||
<div class="col-md-6">
|
||
<p><strong>年龄:</strong> ${data.features.age_years} 岁</p>
|
||
<p><strong>性别:</strong> ${data.features.gender === 1 ? '女性' : '男性'}</p>
|
||
<p><strong>BMI:</strong> ${data.features.bmi}</p>
|
||
<p><strong>血压:</strong> ${data.features.ap_hi}/${data.features.ap_lo} mmHg</p>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<p><strong>胆固醇:</strong> ${getCholesterolText(data.features.cholesterol)}</p>
|
||
<p><strong>血糖:</strong> ${getGlucText(data.features.gluc)}</p>
|
||
<p><strong>吸烟:</strong> ${data.features.smoke === 1 ? '是' : '否'}</p>
|
||
<p><strong>饮酒:</strong> ${data.features.alco === 1 ? '是' : '否'}</p>
|
||
<p><strong>体育活动:</strong> ${data.features.active === 1 ? '是' : '否'}</p>
|
||
</div>
|
||
`;
|
||
|
||
// 更新健康建议
|
||
const healthAdvice = document.getElementById('healthAdvice');
|
||
let adviceItems = [];
|
||
|
||
if (data.features.bmi > 25) {
|
||
adviceItems.push('<li>您的BMI偏高,建议控制体重,保持健康饮食</li>');
|
||
}
|
||
|
||
if (data.features.ap_hi > 140 || data.features.ap_lo > 90) {
|
||
adviceItems.push('<li>您的血压偏高,建议定期监测血压,减少盐分摄入</li>');
|
||
}
|
||
|
||
if (data.features.cholesterol > 1) {
|
||
adviceItems.push('<li>您的胆固醇水平偏高,建议减少高胆固醇食物摄入</li>');
|
||
}
|
||
|
||
if (data.features.gluc > 1) {
|
||
adviceItems.push('<li>您的血糖水平偏高,建议控制糖分摄入,定期监测血糖</li>');
|
||
}
|
||
|
||
if (data.features.smoke === 1) {
|
||
adviceItems.push('<li>吸烟是心血管疾病的重要风险因素,建议戒烟</li>');
|
||
}
|
||
|
||
if (data.features.active === 0) {
|
||
adviceItems.push('<li>缺乏体育活动,建议每周进行至少150分钟的中等强度运动</li>');
|
||
}
|
||
|
||
if (adviceItems.length === 0) {
|
||
adviceItems.push('<li>保持当前健康的生活方式,定期进行体检</li>');
|
||
}
|
||
|
||
healthAdvice.innerHTML = adviceItems.join('');
|
||
}
|
||
|
||
// 重置表单
|
||
function resetForm() {
|
||
document.getElementById('predictionForm').reset();
|
||
document.getElementById('resultPlaceholder').style.display = 'block';
|
||
document.getElementById('resultContent').style.display = 'none';
|
||
}
|
||
|
||
// 填充示例数据
|
||
function fillExampleData(type) {
|
||
let exampleData;
|
||
|
||
switch(type) {
|
||
case 'low':
|
||
exampleData = {
|
||
age: 18000, // 约49岁
|
||
gender: 1, // 女性
|
||
height: 165,
|
||
weight: 60,
|
||
ap_hi: 120,
|
||
ap_lo: 80,
|
||
cholesterol: 1,
|
||
gluc: 1,
|
||
smoke: 0,
|
||
alco: 0,
|
||
active: 1
|
||
};
|
||
break;
|
||
|
||
case 'medium':
|
||
exampleData = {
|
||
age: 25000, // 约68岁
|
||
gender: 2, // 男性
|
||
height: 170,
|
||
weight: 80,
|
||
ap_hi: 140,
|
||
ap_lo: 90,
|
||
cholesterol: 2,
|
||
gluc: 1,
|
||
smoke: 1,
|
||
alco: 1,
|
||
active: 0
|
||
};
|
||
break;
|
||
|
||
case 'high':
|
||
exampleData = {
|
||
age: 30000, // 约82岁
|
||
gender: 2, // 男性
|
||
height: 168,
|
||
weight: 95,
|
||
ap_hi: 160,
|
||
ap_lo: 100,
|
||
cholesterol: 3,
|
||
gluc: 2,
|
||
smoke: 1,
|
||
alco: 1,
|
||
active: 0
|
||
};
|
||
break;
|
||
}
|
||
|
||
// 填充表单
|
||
for (const [key, value] of Object.entries(exampleData)) {
|
||
const element = document.getElementById(key);
|
||
if (element) {
|
||
element.value = value;
|
||
}
|
||
}
|
||
|
||
// 重置结果显示
|
||
document.getElementById('resultPlaceholder').style.display = 'block';
|
||
document.getElementById('resultContent').style.display = 'none';
|
||
}
|
||
|
||
// 辅助函数:获取胆固醇文本描述
|
||
function getCholesterolText(value) {
|
||
switch(value) {
|
||
case 1: return '正常';
|
||
case 2: return '高于正常';
|
||
case 3: return '极高';
|
||
default: return '未知';
|
||
}
|
||
}
|
||
|
||
// 辅助函数:获取血糖文本描述
|
||
function getGlucText(value) {
|
||
switch(value) {
|
||
case 1: return '正常';
|
||
case 2: return '高于正常';
|
||
case 3: return '极高';
|
||
default: return '未知';
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |